Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit 0e1f107

Browse files
author
Janos Bonic
committed
Fixes ContainerSSH/ContainerSSH#331: Add SSH certificate information to webhook.
1 parent c925d96 commit 0e1f107

26 files changed

Lines changed: 89 additions & 83 deletions

auth/protocol.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package auth
22

3+
import "time"
4+
35
// PasswordAuthRequest is an authentication request for password authentication.
46
//
57
// swagger:model PasswordAuthRequest
@@ -58,6 +60,13 @@ type PublicKeyAuthRequest struct {
5860
//
5961
// required: true
6062
PublicKey string `json:"publicKey"`
63+
64+
// CACertificate contains information about the SSH certificate presented by a connecting client. This certificate
65+
// is not an SSL/TLS/x509 certificate and has a much simpler structure. However, this can be used to verify if the
66+
// connecting client belongs to an organization.
67+
//
68+
// required: false
69+
CACertificate CACertificate `json:"caCertificate,omitempty"`
6170
}
6271

6372
// ResponseBody is a response to authentication requests.
@@ -85,3 +94,22 @@ type Response struct {
8594
// in: body
8695
ResponseBody
8796
}
97+
98+
// CACertificate contains information about the SSH certificate presented by a connecting client. This certificate
99+
// is not an SSL/TLS/x509 certificate and has a much simpler structure. However, this can be used to verify if the
100+
// connecting client belongs to an organization.
101+
//
102+
// swagger:model CACertificate
103+
type CACertificate struct {
104+
// PublicKey contains the public key of the CA signing the public key presented in the OpenSSH authorized key
105+
// format.
106+
PublicKey string `json:"key"`
107+
// KeyID contains an identifier for the key.
108+
KeyID string `json:"keyID"`
109+
// ValidPrincipals contains a list of principals for which this CA certificate is valid.
110+
ValidPrincipals []string `json:"validPrincipals"`
111+
// ValidAfter contains the time after which this certificate is valid. This may be empty.
112+
ValidAfter *time.Time `json:"validAfter,omitempty"`
113+
// ValidBefore contains the time when this certificate expires. This may be empty.
114+
ValidBefore *time.Time `json:"validBefore,omitempty"`
115+
}

auth/webhook/client.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,22 @@ type Client interface {
2222

2323
// PubKey authenticates with a public key from the client. It returns a bool if the authentication as successful
2424
// or not. If an error happened while contacting the authentication server it will return an error.
25+
//
26+
// The parameters are as follows:
27+
//
28+
// - username is the username provided by the connecting client.
29+
// - pubKey is the public key offered by the connecting client. The client may offer multiple keys which will be
30+
// presented by calling this function multiple times.
31+
// - connectionID is an opaque random string representing this SSH connection across multiple webhooks and logs.
32+
// - remoteAddr is the IP address of the connecting client.
33+
// - caPubKey is the verified public key of the SSH CA certificate offered by the client. If no CA certificate
34+
// was offered this string is empty.
2535
PubKey(
2636
username string,
2737
pubKey string,
2838
connectionID string,
2939
remoteAddr net.IP,
40+
caPubKey string,
3041
) AuthenticationContext
3142
}
3243

@@ -79,6 +90,7 @@ func (a authClientWrapper) PubKey(
7990
pubKey string,
8091
connectionID string,
8192
remoteAddr net.IP,
93+
caPubKey string,
8294
) AuthenticationContext {
83-
return a.c.PubKey(username, pubKey, connectionID, remoteAddr)
95+
return a.c.PubKey(username, pubKey, connectionID, remoteAddr, caPubKey)
8496
}

internal/auditlog/logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type Connection interface {
3434
OnAuthPasswordBackendError(username string, password []byte, reason string)
3535

3636
// OnAuthPubKey creates an audit log message for an authentication attempt with public key.
37-
OnAuthPubKey(username string, pubKey string)
37+
OnAuthPubKey(username string, pubKey string, caKey string)
3838
// OnAuthPubKeySuccess creates an audit log message for a successful public key authentication.
3939
OnAuthPubKeySuccess(username string, pubKey string)
4040
// OnAuthPubKeyFailed creates an audit log message for a failed public key authentication.

internal/auditlog/logger_empty.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (e *empty) OnAuthPasswordFailed(_ string, _ []byte) {}
7373

7474
func (e *empty) OnAuthPasswordBackendError(_ string, _ []byte, _ string) {}
7575

76-
func (e *empty) OnAuthPubKey(_ string, _ string) {}
76+
func (e *empty) OnAuthPubKey(username string, pubKey string, caKey string) {}
7777

7878
func (e *empty) OnAuthPubKeySuccess(_ string, _ string) {}
7979

internal/auditlog/logger_impl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func (l *loggerConnection) OnAuthPasswordBackendError(username string, password
156156
})
157157
}
158158

159-
func (l *loggerConnection) OnAuthPubKey(username string, pubKey string) {
159+
func (l *loggerConnection) OnAuthPubKey(username string, pubKey string, caKey string) {
160160
l.log(message.Message{
161161
ConnectionID: l.connectionID,
162162
Timestamp: time.Now().UnixNano(),

internal/auditlog/logger_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,11 @@ func TestAuth(t *testing.T) {
254254
connection.OnAuthPasswordFailed("foo", []byte("bar"))
255255
connection.OnAuthPassword("foo", []byte("baz"))
256256
connection.OnAuthPasswordSuccess("foo", []byte("baz"))
257-
connection.OnAuthPubKey("foo", "ssh-rsa ASDF")
257+
connection.OnAuthPubKey("foo", "ssh-rsa ASDF", "")
258258
connection.OnAuthPubKeyBackendError("foo", "ssh-rsa ASDF", "no particular reason")
259-
connection.OnAuthPubKey("foo", "ssh-rsa ASDF")
259+
connection.OnAuthPubKey("foo", "ssh-rsa ASDF", "")
260260
connection.OnAuthPubKeyFailed("foo", "ssh-rsa ASDF")
261-
connection.OnAuthPubKey("foo", "ssh-rsa ABCDEF")
261+
connection.OnAuthPubKey("foo", "ssh-rsa ABCDEF", "")
262262
connection.OnAuthPubKeySuccess("foo", "ssh-rsa ABCDEF")
263263
connection.OnHandshakeSuccessful("foo")
264264
connection.OnDisconnect()

internal/auditlogintegration/handler_networkconnection.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,9 @@ func (n *networkConnectionHandler) OnAuthPassword(
8383
return response, metadata, reason
8484
}
8585

86-
func (n *networkConnectionHandler) OnAuthPubKey(
87-
username string,
88-
pubKey string,
89-
clientVersion string,
90-
) (
91-
response sshserver.AuthResponse,
92-
metadata map[string]string,
93-
reason error,
94-
) {
95-
n.audit.OnAuthPubKey(username, pubKey)
96-
response, metadata, reason = n.backend.OnAuthPubKey(username, pubKey, clientVersion)
86+
func (n *networkConnectionHandler) OnAuthPubKey(username string, pubKey string, clientVersion string, caKey string) (response sshserver.AuthResponse, metadata map[string]string, reason error) {
87+
n.audit.OnAuthPubKey(username, pubKey, caKey)
88+
response, metadata, reason = n.backend.OnAuthPubKey(username, pubKey, clientVersion, caKey)
9789
switch response {
9890
case sshserver.AuthResponseSuccess:
9991
n.audit.OnAuthPubKeySuccess(username, pubKey)

internal/auditlogintegration/integration_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,7 @@ func (b *backendHandler) OnAuthPassword(username string, _ []byte, _ string) (
316316
return sshserver.AuthResponseFailure, nil, nil
317317
}
318318

319-
func (b *backendHandler) OnAuthPubKey(_ string, _ string, _ string) (
320-
response sshserver.AuthResponse,
321-
metadata map[string]string,
322-
reason error,
323-
) {
319+
func (b *backendHandler) OnAuthPubKey(username string, pubKey string, clientVersion string, caKey string) (response sshserver.AuthResponse, metadata map[string]string, reason error) {
324320
return sshserver.AuthResponseFailure, nil, nil
325321
}
326322

internal/auth/client.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package auth
22

33
import (
44
"net"
5+
6+
"github.com/containerssh/libcontainerssh/ssh"
57
)
68

79
// AuthenticationContext holds the results of an authentication.
@@ -33,12 +35,7 @@ type Client interface {
3335

3436
// PubKey authenticates with a public key from the client. It returns a bool if the authentication as successful
3537
// or not. If an error happened while contacting the authentication server it will return an error.
36-
PubKey(
37-
username string,
38-
pubKey string,
39-
connectionID string,
40-
remoteAddr net.IP,
41-
) AuthenticationContext
38+
PubKey(username string, pubKey string, connectionID string, remoteAddr net.IP, caPubKey ssh.CACertificate) AuthenticationContext
4239

4340
// KeyboardInteractive is a method to post a series of questions to the user and receive answers.
4441
KeyboardInteractive(
@@ -69,4 +66,4 @@ type KeyboardInteractiveQuestion struct {
6966
type KeyboardInteractiveAnswers struct {
7067
// KeyboardInteractiveQuestion is the original question that was answered.
7168
Answers map[string]string
72-
}
69+
}

internal/auth/client_http.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,7 @@ func (client *httpAuthClient) Password(
9595
return client.processAuthWithRetry(username, method, authType, connectionID, url, authRequest, remoteAddr)
9696
}
9797

98-
func (client *httpAuthClient) PubKey(
99-
username string,
100-
pubKey string,
101-
connectionID string,
102-
remoteAddr net.IP,
103-
) AuthenticationContext {
98+
func (client *httpAuthClient) PubKey(username string, pubKey string, connectionID string, remoteAddr net.IP, caPubKey interface{}) AuthenticationContext {
10499
if !client.enablePubKey {
105100
err := message.UserMessage(
106101
message.EAuthDisabled,
@@ -117,6 +112,7 @@ func (client *httpAuthClient) PubKey(
117112
ConnectionID: connectionID,
118113
SessionID: connectionID,
119114
PublicKey: pubKey,
115+
CAPublicKey: caPubKey,
120116
}
121117
method := "Public key"
122118
authType := "pubkey"

0 commit comments

Comments
 (0)