From 1028828421b06cfe5da727d194d9458f94aee93d Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 29 Jul 2024 20:33:29 +0100 Subject: [PATCH] [sec_scan][24] extract AuthorizedKey's comment and type (#44643) This PR adds ability to extract the comment and key type from AuthorizedKeys files. Signed-off-by: Tiago Silva --- .../accessgraph/v1/authorized_key.pb.go | 52 ++++++++++++++----- .../access_graph/v1/authorized_key.proto | 6 +++ api/types/accessgraph/authorized_key.go | 4 ++ api/types/accessgraph/authorized_key_test.go | 15 ++++++ .../authorizedkeys/authorized_keys.go | 4 +- .../authorizedkeys/authorized_keys_test.go | 19 +++++-- lib/services/local/access_graph_test.go | 4 ++ 7 files changed, 85 insertions(+), 19 deletions(-) diff --git a/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go b/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go index 6ecdbf0e7b9d8..11a3a967d6dbc 100644 --- a/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go +++ b/api/gen/proto/go/teleport/accessgraph/v1/authorized_key.pb.go @@ -133,6 +133,12 @@ type AuthorizedKeySpec struct { KeyFingerprint string `protobuf:"bytes,2,opt,name=key_fingerprint,json=keyFingerprint,proto3" json:"key_fingerprint,omitempty"` // host_user is the user who can be accessed using the fingerprint above. HostUser string `protobuf:"bytes,3,opt,name=host_user,json=hostUser,proto3" json:"host_user,omitempty"` + // key_comment is the authorized key's comment. + // Authorized keys consist of the following space-separated fields: + // options, keytype, base64-encoded key, comment. The options field is optional. + KeyComment string `protobuf:"bytes,4,opt,name=key_comment,json=keyComment,proto3" json:"key_comment,omitempty"` + // key_type is the ssh's key type. + KeyType string `protobuf:"bytes,5,opt,name=key_type,json=keyType,proto3" json:"key_type,omitempty"` } func (x *AuthorizedKeySpec) Reset() { @@ -188,6 +194,20 @@ func (x *AuthorizedKeySpec) GetHostUser() string { return "" } +func (x *AuthorizedKeySpec) GetKeyComment() string { + if x != nil { + return x.KeyComment + } + return "" +} + +func (x *AuthorizedKeySpec) GetKeyType() string { + if x != nil { + return x.KeyType + } + return "" +} + var File_teleport_access_graph_v1_authorized_key_proto protoreflect.FileDescriptor var file_teleport_access_graph_v1_authorized_key_proto_rawDesc = []byte{ @@ -211,20 +231,24 @@ var file_teleport_access_graph_v1_authorized_key_proto_rawDesc = []byte{ 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x22, 0x72, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, - 0x4b, 0x65, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x64, - 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, - 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x46, 0x69, - 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x73, - 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, - 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x61, 0x70, - 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, - 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x63, 0x22, 0xae, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, + 0x64, 0x4b, 0x65, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, + 0x72, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x46, + 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, + 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, + 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x65, + 0x79, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x42, 0x5a, 0x5a, 0x58, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x76, + 0x31, 0x3b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/proto/teleport/access_graph/v1/authorized_key.proto b/api/proto/teleport/access_graph/v1/authorized_key.proto index 15d95228b4897..1795d7a415a58 100644 --- a/api/proto/teleport/access_graph/v1/authorized_key.proto +++ b/api/proto/teleport/access_graph/v1/authorized_key.proto @@ -43,4 +43,10 @@ message AuthorizedKeySpec { string key_fingerprint = 2; // host_user is the user who can be accessed using the fingerprint above. string host_user = 3; + // key_comment is the authorized key's comment. + // Authorized keys consist of the following space-separated fields: + // options, keytype, base64-encoded key, comment. The options field is optional. + string key_comment = 4; + // key_type is the ssh's key type. + string key_type = 5; } diff --git a/api/types/accessgraph/authorized_key.go b/api/types/accessgraph/authorized_key.go index 33de330f9c688..215583716be49 100644 --- a/api/types/accessgraph/authorized_key.go +++ b/api/types/accessgraph/authorized_key.go @@ -76,6 +76,10 @@ func ValidateAuthorizedKey(k *accessgraphv1pb.AuthorizedKey) error { return trace.BadParameter("KeyFingerprint is unset") } + if k.Spec.KeyType == "" { + return trace.BadParameter("KeyType is unset") + } + if k.Metadata.Name == "" { return trace.BadParameter("Name is unset") } diff --git a/api/types/accessgraph/authorized_key_test.go b/api/types/accessgraph/authorized_key_test.go index d28d72ebd3e7e..46c92cbd15a47 100644 --- a/api/types/accessgraph/authorized_key_test.go +++ b/api/types/accessgraph/authorized_key_test.go @@ -39,6 +39,7 @@ func TestAuthorizedKey(t *testing.T) { HostId: uuid.New().String(), KeyFingerprint: "fingerprint", HostUser: "user", + KeyType: "ssh-rsa", }, errValidation: require.NoError, }, @@ -48,6 +49,7 @@ func TestAuthorizedKey(t *testing.T) { HostId: uuid.New().String(), KeyFingerprint: "", HostUser: "user", + KeyType: "ssh-rsa", }, errValidation: func(t require.TestingT, err error, i ...any) { require.ErrorContains(t, err, "KeyFingerprint is unset") @@ -59,6 +61,7 @@ func TestAuthorizedKey(t *testing.T) { HostId: uuid.New().String(), KeyFingerprint: "fingerprint", HostUser: "", + KeyType: "ssh-rsa", }, errValidation: func(t require.TestingT, err error, i ...any) { require.ErrorContains(t, err, "HostUser is unset") @@ -69,11 +72,23 @@ func TestAuthorizedKey(t *testing.T) { spec: &accessgraphv1pb.AuthorizedKeySpec{ KeyFingerprint: "fingerprint", HostUser: "user", + KeyType: "ssh-rsa", }, errValidation: func(t require.TestingT, err error, i ...any) { require.ErrorContains(t, err, "HostId is unset") }, }, + { + name: "missing HostID", + spec: &accessgraphv1pb.AuthorizedKeySpec{ + KeyFingerprint: "fingerprint", + HostUser: "user", + HostId: uuid.New().String(), + }, + errValidation: func(t require.TestingT, err error, i ...any) { + require.ErrorContains(t, err, "KeyType is unset") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/lib/secretsscanner/authorizedkeys/authorized_keys.go b/lib/secretsscanner/authorizedkeys/authorized_keys.go index 5e26720fae4c7..7919ac6162e6a 100644 --- a/lib/secretsscanner/authorizedkeys/authorized_keys.go +++ b/lib/secretsscanner/authorizedkeys/authorized_keys.go @@ -354,7 +354,7 @@ func (w *Watcher) parseAuthorizedKeysFile(ctx context.Context, u user.User, auth if len(payload) == 0 || payload[0] == '#' { continue } - parsedKey, _, _, _, err := ssh.ParseAuthorizedKey(payload) + parsedKey, comment, _, _, err := ssh.ParseAuthorizedKey(payload) if err != nil { w.logger.WarnContext(ctx, "Failed to parse authorized key", "error", err) continue @@ -367,6 +367,8 @@ func (w *Watcher) parseAuthorizedKeysFile(ctx context.Context, u user.User, auth HostId: w.hostID, HostUser: u.Username, KeyFingerprint: ssh.FingerprintSHA256(parsedKey), + KeyComment: comment, + KeyType: parsedKey.Type(), }, ) if err != nil { diff --git a/lib/secretsscanner/authorizedkeys/authorized_keys_test.go b/lib/secretsscanner/authorizedkeys/authorized_keys_test.go index 8e54603ad0ec4..b6d995dcdcf62 100644 --- a/lib/secretsscanner/authorizedkeys/authorized_keys_test.go +++ b/lib/secretsscanner/authorizedkeys/authorized_keys_test.go @@ -202,15 +202,26 @@ func (f *fakeClient) getReqReceived() []*accessgraphsecretsv1pb.ReportAuthorized func createKeysForUsers(t *testing.T, hostID string) []*accessgraphsecretsv1pb.AuthorizedKey { var keys []*accessgraphsecretsv1pb.AuthorizedKey - for _, fingerprint := range []string{ - "SHA256:GbJlTLeQgZhvGoklWGXHo0AinGgGEcldllgYExoSy+s", /* ssh-rsa */ - "SHA256:ewwMB/nCAYurNrYFXYZuxLZv7T7vgpPd7QuIo0d5n+U", /* ssh-ed25519 */ + for _, k := range []struct { + fingerprint string + keyType string + }{ + { + fingerprint: "SHA256:GbJlTLeQgZhvGoklWGXHo0AinGgGEcldllgYExoSy+s", + keyType: "ssh-ed25519", + }, + { + fingerprint: "SHA256:ewwMB/nCAYurNrYFXYZuxLZv7T7vgpPd7QuIo0d5n+U", + keyType: "ssh-rsa", + }, } { for _, user := range []string{"root", "user"} { at, err := accessgraph.NewAuthorizedKey(&accessgraphsecretsv1pb.AuthorizedKeySpec{ HostId: hostID, HostUser: user, - KeyFingerprint: fingerprint, + KeyFingerprint: k.fingerprint, + KeyComment: "friel@test", + KeyType: k.keyType, }) require.NoError(t, err) keys = append(keys, at) diff --git a/lib/services/local/access_graph_test.go b/lib/services/local/access_graph_test.go index 2eda122f8c045..b2a56819195b1 100644 --- a/lib/services/local/access_graph_test.go +++ b/lib/services/local/access_graph_test.go @@ -55,21 +55,25 @@ func TestAccessGraphAuthorizedKeys(t *testing.T) { HostId: "host1", HostUser: "user1", KeyFingerprint: "AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + KeyType: "ssh-rsa", }, { HostId: "host1", HostUser: "user2", KeyFingerprint: "AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + KeyType: "ssh-rsa", }, { HostId: "host2", HostUser: "user1", KeyFingerprint: "AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + KeyType: "ssh-rsa", }, { HostId: "host2", HostUser: "user2", KeyFingerprint: "AAAAB3NzaC1yc2EAAAADAQABAAABAQC...", + KeyType: "ssh-rsa", }, } var authKeys []*accessgraphsecretspb.AuthorizedKey