From 7fafadd70b1aad84f7dec4952db9c74ddff92daa Mon Sep 17 00:00:00 2001 From: Louis Ruch Date: Mon, 8 Aug 2022 18:54:42 -0700 Subject: [PATCH] db: Add credential_sha256 to session_credentials (#2339) * db: Add credential_sha256 to session_credentials --- .../static/repository_credential_test.go | 11 +++++ internal/credential/static/testing.go | 44 ++++++++++++++++- .../cluster/handlers/worker_service_test.go | 48 +++++++++++++------ .../postgres/23/01_session_credential.up.sql | 2 + .../postgres/43/01_session_credentials.up.sql | 35 ++++++++++++++ 5 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 internal/db/schema/migrations/oss/postgres/43/01_session_credentials.up.sql diff --git a/internal/credential/static/repository_credential_test.go b/internal/credential/static/repository_credential_test.go index dae7cc7e10..87210557b3 100644 --- a/internal/credential/static/repository_credential_test.go +++ b/internal/credential/static/repository_credential_test.go @@ -277,6 +277,17 @@ func TestRepository_CreateSshPrivateKeyCredential(t *testing.T) { }, }, }, + { + name: "valid-large-pk", + scopeId: prj.PublicId, + cred: &SshPrivateKeyCredential{ + SshPrivateKeyCredential: &store.SshPrivateKeyCredential{ + Username: "my-user", + PrivateKey: []byte(TestLargeSshPrivateKeyPem), + StoreId: cs.PublicId, + }, + }, + }, { name: "valid-with-passphrase", scopeId: prj.PublicId, diff --git a/internal/credential/static/testing.go b/internal/credential/static/testing.go index 1ee4fe72d7..1b1463db66 100644 --- a/internal/credential/static/testing.go +++ b/internal/credential/static/testing.go @@ -12,7 +12,8 @@ import ( "github.com/stretchr/testify/require" ) -const TestSshPrivateKeyPem = ` +const ( + TestSshPrivateKeyPem = ` -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACDxfwhEAZKrnsbQxOjVA3PFiB3bW3tSpNKx8TdMiCqlzQAAAJDmpbfr5qW3 @@ -21,6 +22,47 @@ AAAEBvvkQkH06ad2GpX1VVARzu9NkHA6gzamAaQ/hkn5FuZvF/CEQBkquextDE6NUDc8WI Hdtbe1Kk0rHxN0yIKqXNAAAACWplZmZAYXJjaAECAwQ= -----END OPENSSH PRIVATE KEY----- ` + TestLargeSshPrivateKeyPem = ` +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAYEArlz+vsY5wJ3bdEJ+A8UuButCeUnhLL0GJAnKNHiFlxNBYagTyoFX +ATiUz89+V41HWstfo+9nghtqNTRZipu0qkh+O7thAt5lF/9UVWMNpb4CA3C4I8os4k3x6d +047s8QLBHWJITcTkZ3ic4eyExzeOW0X9YcdbMOz8GLBalIyZAsq/7vZBDh+8wj0Jm6ZFxz +gGzgX6th86vipX2IUqpfTlQ3AWFpTS8FLXH8hU0KM11qMDZFPPO/3QPo6HA0cR6ft4gLuV +SsNAgYkFqTZ0aFD1UmFDkgfcLwgtrBarIRY8VU2nwYThATsSsCJVxfEYI3vhm6b215VaGS +hwbha/LbKHb+pGSj7P1vAPTfC9TUAhEOzBpERkaBdGpsLKYM+pozwQW4ETHtyZTFA7N1z2 +4v0A0qMxO6FXDjgpK00rtrC+w3SwuEfJXgjCXydaBSwM1PXJ9YFXoyuHMa2BtwFSNK0dr2 +afNmz4ysjUBIBitMH95yvX9N8pJu04GTOYWUqBLpAAAFmAqP1NYKj9TWAAAAB3NzaC1yc2 +EAAAGBAK5c/r7GOcCd23RCfgPFLgbrQnlJ4Sy9BiQJyjR4hZcTQWGoE8qBVwE4lM/PfleN +R1rLX6PvZ4IbajU0WYqbtKpIfju7YQLeZRf/VFVjDaW+AgNwuCPKLOJN8endOO7PECwR1i +SE3E5Gd4nOHshMc3jltF/WHHWzDs/BiwWpSMmQLKv+72QQ4fvMI9CZumRcc4Bs4F+rYfOr +4qV9iFKqX05UNwFhaU0vBS1x/IVNCjNdajA2RTzzv90D6OhwNHEen7eIC7lUrDQIGJBak2 +dGhQ9VJhQ5IH3C8ILawWqyEWPFVNp8GE4QE7ErAiVcXxGCN74Zum9teVWhkocG4Wvy2yh2 +/qRko+z9bwD03wvU1AIRDswaREZGgXRqbCymDPqaM8EFuBEx7cmUxQOzdc9uL9ANKjMTuh +Vw44KStNK7awvsN0sLhHyV4Iwl8nWgUsDNT1yfWBV6MrhzGtgbcBUjStHa9mnzZs+MrI1A +SAYrTB/ecr1/TfKSbtOBkzmFlKgS6QAAAAMBAAEAAAGBAIzIMztvq6O1EULuiPacV0xo2Z +Q6rZ/Ew1eHvAbfpOVVO74QymIASnKG78hWfWlNfeZ2PLONkiJ/5iItMXrzu0yeGaY65do+ +HJvioYIL5zICl3eVpGfpTpIuYvvzjYtsDl+2yxNTXtmolc3jagFJkRZ1SUz0AKibuYLPf2 +NDyqxMR3Vb8of2BbCbo/NCnDd6WhvATO2R4BWxm98I22/7ddY1su/faflS1Lhbx4sNqAXP +D/T7bK4JFMnr5Tr/lagcE31Viq5kMjOsHk9QMA1e1R9JPyXRF74poEtwz4hmOG0P9lWEZK +gwMkqPAfnTc7fdG4KL6yuj0iHVIcC5rFf3ZJ1UZPgt8DxYFqaSUsSf9OvdU9GP7eobIcxB +pXy86V8l7bld8jRGnhxglU6nOOIWa4NVLMo+WY2wm2zaY0gZf7ksYn5LoWiB3XnmuwAJbq +B6LjgD+48/Cqn5WrDcTSwA0UqVRoJ+wspINyWmS5+j6ZRW3/n8TVIk/B6nc0bQclD58QAA +AMBPefLRxz97radLP8Ec+SV95YDXtbeyS5exnP7rNWCADmPlKYjUqpFTiajErfAcs6qt3i +9ZsZWE1FSucaz0u2tUTyLV6K73cw9YzXuI/ezLIoM63RUsT4lu8Hycow66kt+UrTvJ2UDU +NlwcouVPOiB/V780p7PYHaUD9kfsSD2sAooUfbj91uD3gxHM3NQQFT8OSievQZHicaoBDg +x+rvOjKQHWV1WEl3Kr2X/QZcMh0Et9scZBQdWbQsu8mSLP0aIAAADBAOIIH+iKFJPERKlY +hG+0ntEB/is7ShpxMAuYkdwT9bo3iuula233bfKVEXFEa0RtMqmHb3R7iqik5Jg9nUqsPS +qM9jgRko3RT1ACgSnRrvLQJXAzaGsn9vjNsZAs7VZQvn5ZT52xct/C0fHWybXgodBG22zK +QyekbxtIYGHJdamFSleHHKxr9rk/eqGzNFbvDGgbKc8oU4luR13dQ1pRA3cm/ZFhObwLas +qY/rjwmPJDyL2OqqD9zUxNhpm7AaB7VQAAAMEAxXspGSN91egUu86+1B9yF+tewfP3IAgI +pw1XS4Q3WTkC82C9Y6o7xToM7wg+6KFWfrk/2atwj9FZEi22FbHweu56P2Kerphqm8Qumh +LGBhgYZ1q8Ks4OrE3T/nuyZVCUgxyXqKcFriQDJSc2d+ziL3k1p2adOxHPmzKVpQqONj7e +do/lpv8N1+5Eb3lOB3DrqcEqRwXzSQcO2QcpikNSHyPquGR689I3xUm6kWmpKs49aacTUx +4Zl94GrpFXPYFFAAAAIGxvdWlzcnVjaEBsb3Vpc3J1Y2gtQzAyREYwQlNNTDg1AQI= +-----END OPENSSH PRIVATE KEY----- +` +) // TestCredentialStore creates a static credential store in the provided DB with // the provided scope and any values passed in through the Options vars. diff --git a/internal/daemon/cluster/handlers/worker_service_test.go b/internal/daemon/cluster/handlers/worker_service_test.go index e721ad674c..c69b3394e1 100644 --- a/internal/daemon/cluster/handlers/worker_service_test.go +++ b/internal/daemon/cluster/handlers/worker_service_test.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/boundary/internal/authtoken" + credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/daemon/cluster/handlers" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" @@ -21,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh/testdata" "google.golang.org/protobuf/proto" ) @@ -60,7 +62,7 @@ func TestLookupSession(t *testing.T) { Endpoint: "tcp://127.0.0.1:22", }) - egressSess := session.TestSession(t, conn, wrapper, session.ComposedOf{ + sessWithCreds := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), TargetId: tar.GetPublicId(), @@ -83,10 +85,27 @@ func TestLookupSession(t *testing.T) { }, }, { - Credential: &pbs.Credential_UsernamePassword{ - UsernamePassword: &pbs.UsernamePassword{ - Username: "another-username", - Password: "a different password", + Credential: &pbs.Credential_SshPrivateKey{ + SshPrivateKey: &pbs.SshPrivateKey{ + Username: "another-username", + PrivateKey: credstatic.TestLargeSshPrivateKeyPem, + }, + }, + }, + { + Credential: &pbs.Credential_SshPrivateKey{ + SshPrivateKey: &pbs.SshPrivateKey{ + Username: "another-username", + PrivateKey: string(testdata.PEMBytes["ed25519"]), + }, + }, + }, + { + Credential: &pbs.Credential_SshPrivateKey{ + SshPrivateKey: &pbs.SshPrivateKey{ + Username: "another-username", + PrivateKey: string(testdata.PEMEncryptedKeys[0].PEMBytes), + PrivateKeyPassphrase: testdata.PEMEncryptedKeys[0].EncryptionKey, }, }, }, @@ -98,7 +117,7 @@ func TestLookupSession(t *testing.T) { require.NoError(t, err) workerCreds = append(workerCreds, data) } - err = repo.AddSessionCredentials(ctx, egressSess.ScopeId, egressSess.GetPublicId(), workerCreds) + err = repo.AddSessionCredentials(ctx, sessWithCreds.ScopeId, sessWithCreds.GetPublicId(), workerCreds) require.NoError(t, err) s := handlers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, connectionRepoFn, new(sync.Map), kms) @@ -133,17 +152,17 @@ func TestLookupSession(t *testing.T) { }, }, { - name: "Valid-with-egress-creds", - sessionId: egressSess.PublicId, + name: "Valid-with-worker-creds", + sessionId: sessWithCreds.PublicId, want: &pbs.LookupSessionResponse{ ConnectionLimit: 1, ConnectionsLeft: 1, Version: 1, - Endpoint: egressSess.Endpoint, - HostId: egressSess.HostId, - HostSetId: egressSess.HostSetId, - TargetId: egressSess.TargetId, - UserId: egressSess.UserId, + Endpoint: sessWithCreds.Endpoint, + HostId: sessWithCreds.HostId, + HostSetId: sessWithCreds.HostSetId, + TargetId: sessWithCreds.TargetId, + UserId: sessWithCreds.UserId, Status: pbs.SESSIONSTATUS_SESSIONSTATUS_PENDING, Credentials: creds, }, @@ -168,7 +187,8 @@ func TestLookupSession(t *testing.T) { cmp.Diff( tc.want, got, - cmpopts.IgnoreUnexported(pbs.LookupSessionResponse{}, pbs.Credential{}, pbs.UsernamePassword{}), + cmpopts.IgnoreUnexported(pbs.LookupSessionResponse{}, + pbs.Credential{}, pbs.UsernamePassword{}, pbs.SshPrivateKey{}), cmpopts.IgnoreFields(pbs.LookupSessionResponse{}, "Expiration", "Authorization"), ), ) diff --git a/internal/db/schema/migrations/oss/postgres/23/01_session_credential.up.sql b/internal/db/schema/migrations/oss/postgres/23/01_session_credential.up.sql index 6b4c9db177..19e535290a 100644 --- a/internal/db/schema/migrations/oss/postgres/23/01_session_credential.up.sql +++ b/internal/db/schema/migrations/oss/postgres/23/01_session_credential.up.sql @@ -14,6 +14,7 @@ begin; references kms_database_key_version (private_id) on delete restrict on update cascade, + -- Constraint dropped in 43/01_session_credentials.up.sql constraint session_credential_session_id_credential_uq unique(session_id, credential) ); @@ -21,6 +22,7 @@ begin; 'session_credential is a table where each row contains a credential to be used by ' 'by a worker when a connection is established for the session_id.'; + -- Replaced in 43/01_session_credentials.up.sql create trigger immutable_columns before update on session_credential for each row execute procedure immutable_columns('session_id', 'credential', 'key_id'); diff --git a/internal/db/schema/migrations/oss/postgres/43/01_session_credentials.up.sql b/internal/db/schema/migrations/oss/postgres/43/01_session_credentials.up.sql new file mode 100644 index 0000000000..96d314e313 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/43/01_session_credentials.up.sql @@ -0,0 +1,35 @@ +begin; + + -- Update table from 23/01_session_credential.up.sql + alter table session_credential + drop constraint session_credential_session_id_credential_uq, + add column credential_sha256 bytea; -- digest(credential, 'sha256') + + -- Migrate existing session_credentials to set an sha256 if there are any + update session_credential + set credential_sha256 = digest(credential, 'sha256'); + + alter table session_credential + add constraint session_credential_session_id_credential_sha256_uq + unique(session_id, credential_sha256); + + -- Replace the immutable columns trigger from 23/01_session_credential.up.sql + drop trigger immutable_columns on session_credential; + create trigger immutable_columns before update on session_credential + for each row execute procedure immutable_columns('session_id', 'credential', 'key_id', 'credential_sha256'); + + -- session_credentials_sha256_credential sets the credential_sha256 + -- to digest(credential, 'sha256') + create function session_credentials_sha256_credential() + returns trigger + as $$ + begin + new.credential_sha256 = digest(new.credential, 'sha256'); + return new; + end; + $$ language plpgsql; + + create trigger session_credentials_sha256_credential before insert on session_credential + for each row execute procedure session_credentials_sha256_credential(); + +commit;