From c5cc0d5669414f485a58d4ea69595adaf0f4f700 Mon Sep 17 00:00:00 2001 From: Zain Asgar Date: Tue, 24 Aug 2021 15:42:01 -0700 Subject: [PATCH] PC-1140 - Encrypt deployment keys at rest Summary: This encrypts deployment keys are rest. Does not drop exsiting keys, which will be done next. We use a secondary hash to speed up searches. Test Plan: N/A Reviewers: michelle, vihang Reviewed By: michelle JIRA Issues: PC-1140 Differential Revision: https://phab.corp.pixielabs.ai/D9558 GitOrigin-RevId: 17c4f2cd8d3cb9a0e0b89840dafb69b841e5ffdb --- .../vzmgr/deploymentkey/deployment_keys.go | 24 +++++++--- .../deploymentkey/deployment_keys_test.go | 5 +- ...0024_add_encrypted_deployment_key.down.sql | 7 +++ ...000024_add_encrypted_deployment_key.up.sql | 9 ++++ src/cloud/vzmgr/schema/bindata.gen.go | 46 +++++++++++++++++++ 5 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.down.sql create mode 100644 src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.up.sql diff --git a/src/cloud/vzmgr/deploymentkey/deployment_keys.go b/src/cloud/vzmgr/deploymentkey/deployment_keys.go index 187c7c5a2d1..6577394e6b3 100644 --- a/src/cloud/vzmgr/deploymentkey/deployment_keys.go +++ b/src/cloud/vzmgr/deploymentkey/deployment_keys.go @@ -60,7 +60,9 @@ func (s *Service) Create(ctx context.Context, req *vzmgrpb.CreateDeploymentKeyRe var id uuid.UUID var ts time.Time - query := `INSERT INTO vizier_deployment_keys(org_id, user_id, key, description) VALUES($1, $2, PGP_SYM_ENCRYPT($3, $4), $5) RETURNING id, created_at` + query := `INSERT INTO vizier_deployment_keys(org_id, user_id, hashed_key, encrypted_key, description) + VALUES($1, $2, sha256($3), PGP_SYM_ENCRYPT($3::text, $4::text), $5) + RETURNING id, created_at` keyID, err := uuid.NewV4() if err != nil { return nil, err @@ -90,8 +92,11 @@ func (s *Service) List(ctx context.Context, req *vzmgrpb.ListDeploymentKeyReques } // Return all clusters when the OrgID matches. - query := `SELECT id, org_id, PGP_SYM_DECRYPT(key::bytea, $1), created_at, description from vizier_deployment_keys WHERE org_id=$2 ORDER BY created_at` - rows, err := s.db.QueryxContext(ctx, query, s.dbKey, sCtx.Claims.GetUserClaims().OrgID) + query := `SELECT id, org_id, CONVERT_FROM(PGP_SYM_DECRYPT(encrypted_key, $2::text)::bytea, 'UTF8'), created_at, description + FROM vizier_deployment_keys + WHERE org_id=$1 + ORDER BY created_at` + rows, err := s.db.QueryxContext(ctx, query, sCtx.Claims.GetUserClaims().OrgID, s.dbKey) if err != nil { if err == sql.ErrNoRows { return &vzmgrpb.ListDeploymentKeyResponse{}, nil @@ -140,8 +145,10 @@ func (s *Service) Get(ctx context.Context, req *vzmgrpb.GetDeploymentKeyRequest) var key string var createdAt time.Time var desc string - query := `SELECT PGP_SYM_DECRYPT(key::bytea, $1), created_at, description from vizier_deployment_keys WHERE org_id=$2 and id=$3` - err = s.db.QueryRowxContext(ctx, query, s.dbKey, sCtx.Claims.GetUserClaims().OrgID, tokenID).Scan(&key, &createdAt, &desc) + query := `SELECT CONVERT_FROM(PGP_SYM_DECRYPT(encrypted_key, $3::text)::bytea, 'UTF8'), created_at, description + FROM vizier_deployment_keys + WHERE org_id=$1 AND id=$2` + err = s.db.QueryRowxContext(ctx, query, sCtx.Claims.GetUserClaims().OrgID, tokenID, s.dbKey).Scan(&key, &createdAt, &desc) if err != nil { return nil, status.Error(codes.NotFound, "No such deployment key") } @@ -167,7 +174,8 @@ func (s *Service) Delete(ctx context.Context, req *uuidpb.UUID) (*types.Empty, e return nil, status.Error(codes.InvalidArgument, "invalid id format") } - query := `DELETE from vizier_deployment_keys WHERE org_id=$1 and id=$2` + query := `DELETE FROM vizier_deployment_keys + WHERE org_id=$1 AND id=$2` res, err := s.db.ExecContext(ctx, query, sCtx.Claims.GetUserClaims().OrgID, tokenID) if err != nil { log.WithError(err).Error("Failed to delete deployment token") @@ -189,7 +197,9 @@ func (s *Service) Delete(ctx context.Context, req *uuidpb.UUID) (*types.Empty, e // FetchOrgUserIDUsingDeploymentKey gets the org and user ID based on the deployment key. func (s *Service) FetchOrgUserIDUsingDeploymentKey(ctx context.Context, key string) (uuid.UUID, uuid.UUID, error) { - query := `SELECT org_id, user_id from vizier_deployment_keys WHERE PGP_SYM_DECRYPT(key::bytea, $2)=$1` + query := `SELECT org_id, user_id + FROM vizier_deployment_keys + WHERE hashed_key=sha256($1) AND PGP_SYM_DECRYPT(encrypted_key::bytea, $2::text)::bytea=$1` var orgID uuid.UUID var userID uuid.UUID err := s.db.QueryRowxContext(ctx, query, key, s.dbKey).Scan(&orgID, &userID) diff --git a/src/cloud/vzmgr/deploymentkey/deployment_keys_test.go b/src/cloud/vzmgr/deploymentkey/deployment_keys_test.go index 6f0dde212f2..bf31976128e 100644 --- a/src/cloud/vzmgr/deploymentkey/deployment_keys_test.go +++ b/src/cloud/vzmgr/deploymentkey/deployment_keys_test.go @@ -96,7 +96,8 @@ func createTestAPIUserContext() context.Context { func mustLoadTestData(db *sqlx.DB) { db.MustExec(`DELETE FROM vizier_deployment_keys`) - insertVizierDeploymentKeys := `INSERT INTO vizier_deployment_keys(id, org_id, user_id, key, description) VALUES ($1, $2, $3, PGP_SYM_ENCRYPT($4, $5), $6)` + insertVizierDeploymentKeys := `INSERT INTO vizier_deployment_keys(id, org_id, user_id, hashed_key, encrypted_key, description) + VALUES ($1, $2, $3, sha256($4), PGP_SYM_ENCRYPT($4::text, $5::text), $6)` db.MustExec(insertVizierDeploymentKeys, testKey1ID, testAuthOrgID, testAuthUserID, "key1", testDBKey, "here is a desc") db.MustExec(insertVizierDeploymentKeys, testKey2ID, testAuthOrgID, testAuthUserID, "key2", testDBKey, "here is another one") db.MustExec(insertVizierDeploymentKeys, testNonAuthUserKeyID.String(), testNonAuthOrgID, "123e4567-e89b-12d3-a456-426655440001", "key2", testDBKey, "some other desc") @@ -373,7 +374,7 @@ func TestDeploymentKeyService_Delete_UnownedKey(t *testing.T) { // Make DB query to make sure the Key still exists. var key string - err = db.QueryRow(`SELECT PGP_SYM_DECRYPT(key::bytea, $1) from vizier_deployment_keys where id=$2`, + err = db.QueryRow(`SELECT CONVERT_FROM(PGP_SYM_DECRYPT(encrypted_key, $1::text)::bytea, 'UTF8') from vizier_deployment_keys where id=$2`, testDBKey, testNonAuthUserKeyID). Scan(&key) require.NoError(t, err) diff --git a/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.down.sql b/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.down.sql new file mode 100644 index 00000000000..ba1f4ee9456 --- /dev/null +++ b/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.down.sql @@ -0,0 +1,7 @@ +DROP INDEX idx_vizier_deployment_keys_hashed_key; + +ALTER TABLE vizier_deployment_keys + DROP COLUMN hashed_key; + +ALTER TABLE vizier_deployment_keys + DROP COLUMN encrypted_key; diff --git a/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.up.sql b/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.up.sql new file mode 100644 index 00000000000..803997b45be --- /dev/null +++ b/src/cloud/vzmgr/schema/000024_add_encrypted_deployment_key.up.sql @@ -0,0 +1,9 @@ +ALTER TABLE vizier_deployment_keys + ADD COLUMN encrypted_key bytea; + +-- Hashed key stores a salted and hashed key that we can use for associative lookup. +ALTER TABLE vizier_deployment_keys + ADD COLUMN hashed_key bytea; + +CREATE INDEX idx_vizier_deployment_keys_hashed_key + ON vizier_deployment_keys(hashed_key); diff --git a/src/cloud/vzmgr/schema/bindata.gen.go b/src/cloud/vzmgr/schema/bindata.gen.go index 166b3f2d6dc..20b05b6ae4a 100644 --- a/src/cloud/vzmgr/schema/bindata.gen.go +++ b/src/cloud/vzmgr/schema/bindata.gen.go @@ -46,6 +46,8 @@ // 000022_add_data_plane_and_past_status_columns.up.sql // 000023_add_triggers_for_prev.down.sql // 000023_add_triggers_for_prev.up.sql +// 000024_add_encrypted_deployment_key.down.sql +// 000024_add_encrypted_deployment_key.up.sql package schema import ( @@ -1042,6 +1044,46 @@ func _000023_add_triggers_for_prevUpSql() (*asset, error) { return a, nil } +var __000024_add_encrypted_deployment_keyDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\xf0\xf4\x73\x71\x8d\x50\xc8\x4c\xa9\x88\x2f\xcb\xac\xca\x4c\x2d\x8a\x4f\x49\x2d\xc8\xc9\xaf\xcc\x4d\xcd\x2b\x89\xcf\x4e\xad\x2c\x8e\xcf\x48\x2c\xce\x48\x4d\x01\xb1\xad\xb9\xb8\x1c\x7d\x42\x5c\x83\x14\x42\x1c\x9d\x7c\x5c\x15\xb0\xab\xe7\x52\x50\x00\x9b\xeb\xec\xef\x13\xea\xeb\xa7\x40\x99\xee\xd4\xbc\xe4\xa2\xca\x82\x12\x98\x01\x80\x00\x00\x00\xff\xff\xa6\xe5\x49\x3f\xb1\x00\x00\x00") + +func _000024_add_encrypted_deployment_keyDownSqlBytes() ([]byte, error) { + return bindataRead( + __000024_add_encrypted_deployment_keyDownSql, + "000024_add_encrypted_deployment_key.down.sql", + ) +} + +func _000024_add_encrypted_deployment_keyDownSql() (*asset, error) { + bytes, err := _000024_add_encrypted_deployment_keyDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000024_add_encrypted_deployment_key.down.sql", size: 177, mode: os.FileMode(436), modTime: time.Unix(1, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var __000024_add_encrypted_deployment_keyUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x8e\xc1\x4a\xc4\x30\x10\x86\xef\x79\x8a\xff\xa8\x87\xf5\x05\xf6\x14\xb7\x01\x85\xda\x85\xa5\x82\xb7\x30\x36\x23\x0d\x5b\x93\x92\x99\xad\xc6\xa7\x97\xe2\xa1\x08\x7a\xd8\xeb\x7c\x33\xf3\x7d\xb6\xed\xdd\x09\xbd\xbd\x6f\x1d\x96\xf8\x15\xb9\xf8\xc0\xf3\x94\xeb\x3b\x27\xf5\x67\xae\x62\x00\xdb\x34\x38\x1c\xdb\xe7\xa7\x0e\x9c\x86\x52\x67\xe5\xb0\x32\xbc\x56\x65\xda\x1b\xb3\xdb\xe1\x81\x64\xe4\x80\x75\x2a\x9a\x0b\x0b\x08\x42\x93\x72\x00\xa5\x80\x71\xc3\x3a\x92\xe2\x83\x31\x50\xc2\x45\x18\x6f\xb9\x80\x44\xf2\x10\x49\xe3\xc2\x98\x72\x3e\x5f\xe6\x3b\x73\x6d\xda\x8f\xe2\x57\xd7\xe1\xe4\x6c\xef\xf0\xd8\x35\xee\x05\x31\x7c\xfa\xbf\xff\xf8\xed\xd4\x00\xc7\xee\x1f\xdd\xcd\xb6\x76\xbb\x37\xdf\x01\x00\x00\xff\xff\xfa\x0f\x00\x58\x3a\x01\x00\x00") + +func _000024_add_encrypted_deployment_keyUpSqlBytes() ([]byte, error) { + return bindataRead( + __000024_add_encrypted_deployment_keyUpSql, + "000024_add_encrypted_deployment_key.up.sql", + ) +} + +func _000024_add_encrypted_deployment_keyUpSql() (*asset, error) { + bytes, err := _000024_add_encrypted_deployment_keyUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "000024_add_encrypted_deployment_key.up.sql", size: 314, mode: os.FileMode(436), modTime: time.Unix(1, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -1140,6 +1182,8 @@ var _bindata = map[string]func() (*asset, error){ "000022_add_data_plane_and_past_status_columns.up.sql": _000022_add_data_plane_and_past_status_columnsUpSql, "000023_add_triggers_for_prev.down.sql": _000023_add_triggers_for_prevDownSql, "000023_add_triggers_for_prev.up.sql": _000023_add_triggers_for_prevUpSql, + "000024_add_encrypted_deployment_key.down.sql": _000024_add_encrypted_deployment_keyDownSql, + "000024_add_encrypted_deployment_key.up.sql": _000024_add_encrypted_deployment_keyUpSql, } // AssetDir returns the file names below a certain @@ -1229,6 +1273,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "000022_add_data_plane_and_past_status_columns.up.sql": &bintree{_000022_add_data_plane_and_past_status_columnsUpSql, map[string]*bintree{}}, "000023_add_triggers_for_prev.down.sql": &bintree{_000023_add_triggers_for_prevDownSql, map[string]*bintree{}}, "000023_add_triggers_for_prev.up.sql": &bintree{_000023_add_triggers_for_prevUpSql, map[string]*bintree{}}, + "000024_add_encrypted_deployment_key.down.sql": &bintree{_000024_add_encrypted_deployment_keyDownSql, map[string]*bintree{}}, + "000024_add_encrypted_deployment_key.up.sql": &bintree{_000024_add_encrypted_deployment_keyUpSql, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory