feat: encrypt gitsshkeys.private_key at rest via dbcrypt (#25872)

Adds an optional dbcrypt wrapper around gitsshkeys.private_key. The
column is encrypted on insert and update through enterprise/dbcrypt when
external token encryption is configured, and decrypted on read.

A new private_key_key_id column references
dbcrypt_keys(active_key_digest) so revocation safety is enforced by the
existing foreign key. Rows with a NULL key_id stay plaintext and remain
readable. Existing plaintext rows can be backfilled by running `coder
server dbcrypt rotate`.

Generated with assistance from Coder Agents.
This commit is contained in:
Zach
2026-06-02 08:36:01 -06:00
committed by GitHub
parent 5088b5fa5f
commit 170c33a475
18 changed files with 362 additions and 42 deletions
+22 -13
View File
@@ -12572,7 +12572,7 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File
const getGitSSHKey = `-- name: GetGitSSHKey :one
SELECT
user_id, created_at, updated_at, private_key, public_key
user_id, created_at, updated_at, private_key, public_key, private_key_key_id
FROM
gitsshkeys
WHERE
@@ -12588,6 +12588,7 @@ func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSH
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
&i.PrivateKeyKeyID,
)
return i, err
}
@@ -12599,18 +12600,20 @@ INSERT INTO
created_at,
updated_at,
private_key,
private_key_key_id,
public_key
)
VALUES
($1, $2, $3, $4, $5) RETURNING user_id, created_at, updated_at, private_key, public_key
($1, $2, $3, $4, $5, $6) RETURNING user_id, created_at, updated_at, private_key, public_key, private_key_key_id
`
type InsertGitSSHKeyParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PublicKey string `db:"public_key" json:"public_key"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PrivateKeyKeyID sql.NullString `db:"private_key_key_id" json:"private_key_key_id"`
PublicKey string `db:"public_key" json:"public_key"`
}
func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSSHKey, error) {
@@ -12619,6 +12622,7 @@ func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyPar
arg.CreatedAt,
arg.UpdatedAt,
arg.PrivateKey,
arg.PrivateKeyKeyID,
arg.PublicKey,
)
var i GitSSHKey
@@ -12628,6 +12632,7 @@ func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyPar
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
&i.PrivateKeyKeyID,
)
return i, err
}
@@ -12638,18 +12643,20 @@ UPDATE
SET
updated_at = $2,
private_key = $3,
public_key = $4
private_key_key_id = $4,
public_key = $5
WHERE
user_id = $1
RETURNING
user_id, created_at, updated_at, private_key, public_key
user_id, created_at, updated_at, private_key, public_key, private_key_key_id
`
type UpdateGitSSHKeyParams struct {
UserID uuid.UUID `db:"user_id" json:"user_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PublicKey string `db:"public_key" json:"public_key"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
PrivateKey string `db:"private_key" json:"private_key"`
PrivateKeyKeyID sql.NullString `db:"private_key_key_id" json:"private_key_key_id"`
PublicKey string `db:"public_key" json:"public_key"`
}
func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error) {
@@ -12657,6 +12664,7 @@ func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyPar
arg.UserID,
arg.UpdatedAt,
arg.PrivateKey,
arg.PrivateKeyKeyID,
arg.PublicKey,
)
var i GitSSHKey
@@ -12666,6 +12674,7 @@ func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyPar
&i.UpdatedAt,
&i.PrivateKey,
&i.PublicKey,
&i.PrivateKeyKeyID,
)
return i, err
}