Skip to content

feat: encrypt oidc and git auth tokens in the database #7959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed

Conversation

kylecarbs
Copy link
Member

No description provided.

@kylecarbs kylecarbs self-assigned this Jun 12, 2023
@matifali
Copy link
Member

does this resolve #7640?

@kylecarbs
Copy link
Member Author

Yup!

@kylecarbs kylecarbs marked this pull request as ready for review June 18, 2023 19:01
@kylecarbs kylecarbs requested a review from deansheather June 18, 2023 19:01
Copy link
Member

@deansheather deansheather left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to support upgrading non-encrypted secrets to encrypted on startup, and it doesn't support rotating the encryption key.

@@ -0,0 +1,48 @@
package cryptorand
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this doesn't belong in cryptorand package because it's not random, it should go in the dbcrypt package or it's own package.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Will move


// CipherAES256 returns a new AES-256 cipher.
func CipherAES256(key []byte) (Cipher, error) {
block, err := aes.NewCipher(key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the key should be verified to ensure it's 32 bytes for AES-256

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aes.NewCipher throws an error if it's not

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aes.NewCipher accepts smaller key sizes for less secure AES variants, we should just enforce 32 bytes for AES-256 which is the most secure algo supported

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, good catch

if err != nil {
return nil, err
}
return a.aead.Seal(nonce, nonce, plaintext, nil), nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should write to a new buffer instead of reusing the nonce buffer, and the nonce should be copied

"github.com/coder/coder/cryptorand"
)

func TestCipherAES256(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add a test to ensure a unique nonce is being generated each time we encrypt something new, and another to ensure that changing the nonce causes decryption to fail

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add another test to ensure that the format doesn't change in a backwards incompatible way. Hardcode a key, encrypt something once and then hardcode the encrypted result to ensure it gets decrypted successfully and matches the same input string.


{
Name: "External Token Encryption Key",
Description: "Encrypt OIDC and Git authentication tokens with AES-256-GCM in the database. The value must be a base64-encoded key.",
Copy link
Member

@deansheather deansheather Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should mention that it needs to be 32 bytes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And should provide a bash one liner to generate a secure random key with e.g. openssl or just plain /dev/random

@@ -229,6 +229,12 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
UserID: key.UserID,
LoginType: key.LoginType,
})
if errors.Is(err, sql.ErrNoRows) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if errors.Is(err, sql.ErrNoRows) {
if xerrors.Is(err, sql.ErrNoRows) {

@@ -27,3 +27,6 @@ UPDATE git_auth_links SET
oauth_refresh_token = $5,
oauth_expiry = $6
WHERE provider_id = $1 AND user_id = $2 RETURNING *;

-- name: DeleteGitAuthLink :exec
DELETE FROM git_auth_links WHERE provider_id = $1 AND user_id = $2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DELETE FROM git_auth_links WHERE provider_id = $1 AND user_id = $2;
DELETE FROM
git_auth_links
WHERE
provider_id = $1 AND
user_id = $2;

Comment on lines +165 to +167
// If we have a magic prefix but encryption is disabled,
// we should delete the row.
return delete("encryption disabled")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when the key is accidentally not specified during coder startup? All rows will start being deleted when they're queried? This seems like a really bad idea

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All data encrypted at rest is intentionally temporary. We will not do this for any data that must persist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be written in a very prominent comment at the top of the file or something then, because I can definitely see this being used for e.g. gitauth by another engineer who doesn't know about this limitation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed will add

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All data encrypted at rest is intentionally temporary. We will not do this for any data that must persist.

What about ssh keys that should use encryption at rest ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Adphi that isn't in this PR yet, but I'm not sure what we should do in that case. Maybe stop the server from starting with a warning that is dismissable?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I may, perhaps when the encryption key is wrong, the program should crash loudly to preserve the integrity of the secrets.
And maybe add a command line flag or environment variable to allow all encrypted data to be erased, recovery style.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

@github-actions github-actions bot added stale This issue is like stale bread. and removed stale This issue is like stale bread. labels Jun 30, 2023
@github-actions github-actions bot added the stale This issue is like stale bread. label Jul 14, 2023
@github-actions github-actions bot closed this Jul 17, 2023
johnstcn added a commit that referenced this pull request Aug 30, 2023
This commit builds upon the previous work in #7959:
- Moved dbcrypt package to enterprise/dbcrypt
- Modified original dbcrypt behaviour to not delete
  un-decryptable rows.
- Added a table dbcrypt_sentinel used to determine database
  encryption status

NOTE: This is part 1 of a 2-part PR. This PR focuses
mainly on the dbcrypt and database packages. A separate
PR will add the required plumbing to integrate this into
enterprise/coderd properly.

Co-authored-by: Kyle Carberry <kyle@coder.com>
johnstcn added a commit that referenced this pull request Aug 30, 2023
This commit builds upon the previous work in #7959:
- Moved dbcrypt package to enterprise/dbcrypt
- Modified original dbcrypt behaviour to not delete
  un-decryptable rows.
- Added a table dbcrypt_sentinel used to determine database
  encryption status.
- Added support for multiple encryption keys in
  dbcrypt.

NOTE: This is part 1 of a 2-part PR. This PR focuses
mainly on the dbcrypt and database packages. A separate
PR will add the required plumbing to integrate this into
enterprise/coderd properly.

Co-authored-by: Kyle Carberry <kyle@coder.com>
@github-actions github-actions bot deleted the dbcrypt branch December 19, 2023 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale This issue is like stale bread.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants