Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(coderd): connect dbcrypt package implementation#9523

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
johnstcn merged 38 commits intomainfromcj/dbcrypt_redux_2
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from1 commit
Commits
Show all changes
38 commits
Select commitHold shift + click to select a range
fb953e4
feat(coderd): add dbcrypt package
johnstcnSep 4, 2023
3b8140b
feat(coderd): plumb through dbcrypt package
johnstcnSep 4, 2023
55b93e7
fix indentation
johnstcnSep 5, 2023
f340cba
fixup! fix indentation
johnstcnSep 5, 2023
feae634
check for primary key revocation on startup
johnstcnSep 5, 2023
381f078
retry insert active key on tx serialization failure
johnstcnSep 5, 2023
c42e6a6
fixup! retry insert active key on tx serialization failure
johnstcnSep 5, 2023
6a50a43
use database.IsSerializedError
johnstcnSep 5, 2023
46b1ff4
encryptFields: check for nil field or digest
johnstcnSep 5, 2023
9c18168
rm insertDBCryptKeyNoLock
johnstcnSep 5, 2023
6c28ce5
Merge branch 'cj/dbcrypt_redux_1' into cj/dbcrypt_redux_2
johnstcnSep 5, 2023
c54b64a
Update enterprise/cli/dbcrypt_rotate.go
johnstcnSep 5, 2023
5959b34
Update enterprise/coderd/coderd.go
johnstcnSep 5, 2023
b1546b1
add unit test for ExtractAPIKeyMW
johnstcnSep 5, 2023
3859e03
add unit test for cli.ConnectToPostgres
johnstcnSep 5, 2023
a4f93c5
Merge remote-tracking branch 'origin/main' into cj/dbcrypt_redux_2
johnstcnSep 6, 2023
55a0fd0
DON'T PANIC
johnstcnSep 6, 2023
cce0244
debug log user_ids
johnstcnSep 6, 2023
d51ec66
dbcrypt-rotate -> server dbcrypt rotate
johnstcnSep 6, 2023
aa39fcc
refactor: move rotate logic into dbcrypt
johnstcnSep 6, 2023
e69e3ef
add decrypt/delete commands
johnstcnSep 6, 2023
ebf4eef
fixup! add decrypt/delete commands
johnstcnSep 6, 2023
2de6cc3
beef up unit tests, refactor cli
johnstcnSep 6, 2023
7774811
update golden files
johnstcnSep 6, 2023
35ca78f
Update codersdk/deployment.go
johnstcnSep 6, 2023
3a92a7d
Merge remote-tracking branch 'origin/main' into cj/dbcrypt_redux_2
johnstcnSep 7, 2023
8b1f43c
revoke all active keys on dbcrypt delete
johnstcnSep 7, 2023
270cdc1
fixup! Merge remote-tracking branch 'origin/main' into cj/dbcrypt_red…
johnstcnSep 7, 2023
2514ffe
update docs
johnstcnSep 7, 2023
cd351af
fixup! update docs
johnstcnSep 7, 2023
2f5c112
fixup! update docs
johnstcnSep 7, 2023
2450d13
soft-enforce dbcrypt in license
johnstcnSep 7, 2023
e56b639
do not add external token encryption keys by default (as it will alwa…
johnstcnSep 7, 2023
441fcbf
update golden files
johnstcnSep 7, 2023
ba14128
log encryption status on startup
johnstcnSep 7, 2023
2ae45c6
modify CLI output
johnstcnSep 7, 2023
13451f0
Merge remote-tracking branch 'origin/main' into cj/dbcrypt_redux_2
johnstcnSep 7, 2023
b3ff024
rm unused golden file
johnstcnSep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
PrevPrevious commit
NextNext commit
feat(coderd): plumb through dbcrypt package
This builds upon a previous PR. It is recommended to read that first.- Adds a command dbcrypt-rotate to re-enncrypt encrypted data- Plumbs through dbcrypt in enterprise/coderd (including unit tests)- Enables database encryption in develop.sh by default- Adds documentation in admin/encryption.md
  • Loading branch information
@johnstcn
johnstcn committedSep 4, 2023
commit3b8140bb0ca967ebee5ab3f6c457810d03e066ca
4 changes: 2 additions & 2 deletionscli/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -691,7 +691,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
options.Database = dbfake.New()
options.Pubsub = pubsub.NewInMemory()
} else {
sqlDB, err :=connectToPostgres(ctx, logger, sqlDriver, vals.PostgresURL.String())
sqlDB, err :=ConnectToPostgres(ctx, logger, sqlDriver, vals.PostgresURL.String())
if err != nil {
return xerrors.Errorf("connect to postgres: %w", err)
}
Expand DownExpand Up@@ -1953,7 +1953,7 @@ func BuildLogger(inv *clibase.Invocation, cfg *codersdk.DeploymentValues) (slog.
}, nil
}

funcconnectToPostgres(ctx context.Context, logger slog.Logger, driver string, dbURL string) (*sql.DB, error) {
funcConnectToPostgres(ctx context.Context, logger slog.Logger, driver string, dbURL string) (*sql.DB, error) {
logger.Debug(ctx, "connecting to postgresql")

// Try to connect for 30 seconds.
Expand Down
2 changes: 1 addition & 1 deletioncli/server_createadminuser.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,7 +63,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *clibase.Cmd {
newUserDBURL = url
}

sqlDB, err :=connectToPostgres(ctx, logger, "postgres", newUserDBURL)
sqlDB, err :=ConnectToPostgres(ctx, logger, "postgres", newUserDBURL)
if err != nil {
return xerrors.Errorf("connect to postgres: %w", err)
}
Expand Down
8 changes: 8 additions & 0 deletionscli/testdata/coder_server_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,6 +458,14 @@ These options are only available in the Enterprise Edition.
An HTTP URL that is accessible by other replicas to relay DERP
traffic. Required for high availability.

--external-token-encryption-keys string-array, $CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS
Encrypt OIDC and Git authentication tokens with AES-256-GCM in the
database. The value must be a comma-separated list of base64-encoded
keys. Each key, when base64-decoded, must be exactly 32 bytes in
length. The first key will be used to encrypt new values. Subsequent
keys will be used as a fallback when decrypting. During normal
operation it is recommended to only set one key.

--scim-auth-header string, $CODER_SCIM_AUTH_HEADER
Enables SCIM and sets the authentication header for the built-in SCIM
server. New users are automatically created with OIDC authentication.
Expand Down
6 changes: 6 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

6 changes: 6 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

2 changes: 2 additions & 0 deletionscoderd/deployment_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@ func TestDeploymentValues(t *testing.T) {
cfg.OIDC.EmailField.Set("some_random_field_you_never_expected")
cfg.PostgresURL.Set(hi)
cfg.SCIMAPIKey.Set(hi)
cfg.ExternalTokenEncryptionKeys.Set("the_random_key_we_never_expected,an_other_key_we_never_unexpected")

client := coderdtest.New(t, &coderdtest.Options{
DeploymentValues: cfg,
Expand All@@ -44,6 +45,7 @@ func TestDeploymentValues(t *testing.T) {
require.Empty(t, scrubbed.Values.OIDC.ClientSecret.Value())
require.Empty(t, scrubbed.Values.PostgresURL.Value())
require.Empty(t, scrubbed.Values.SCIMAPIKey.Value())
require.Empty(t, scrubbed.Values.ExternalTokenEncryptionKeys.Value())
}

func TestDeploymentStats(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletionscoderd/httpmw/apikey.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -248,6 +248,12 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
UserID: key.UserID,
LoginType: key.LoginType,
})
if errors.Is(err, sql.ErrNoRows) {
return optionalWrite(http.StatusUnauthorized, codersdk.Response{
Message: SignedOutErrorMessage,
Detail: "You must re-authenticate with the login provider.",
})
}
if err != nil {
return write(http.StatusInternalServerError, codersdk.Response{
Message: "A database error occurred",
Expand Down
17 changes: 14 additions & 3 deletionscodersdk/deployment.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,8 +46,9 @@ const (
FeatureExternalProvisionerDaemons FeatureName = "external_provisioner_daemons"
FeatureAppearance FeatureName = "appearance"
FeatureAdvancedTemplateScheduling FeatureName = "advanced_template_scheduling"
FeatureTemplateAutostopRequirement FeatureName = "template_autostop_requirement"
FeatureWorkspaceProxy FeatureName = "workspace_proxy"
FeatureExternalTokenEncryption FeatureName = "external_token_encryption"
FeatureTemplateAutostopRequirement FeatureName = "template_autostop_requirement"
FeatureWorkspaceBatchActions FeatureName = "workspace_batch_actions"
)

Expand All@@ -65,6 +66,8 @@ var FeatureNames = []FeatureName{
FeatureAdvancedTemplateScheduling,
FeatureWorkspaceProxy,
FeatureUserRoleManagement,
FeatureExternalTokenEncryption,
FeatureTemplateAutostopRequirement,
FeatureWorkspaceBatchActions,
}

Expand DownExpand Up@@ -154,6 +157,7 @@ type DeploymentValues struct {
AgentFallbackTroubleshootingURL clibase.URL `json:"agent_fallback_troubleshooting_url,omitempty" typescript:",notnull"`
BrowserOnly clibase.Bool `json:"browser_only,omitempty" typescript:",notnull"`
SCIMAPIKey clibase.String `json:"scim_api_key,omitempty" typescript:",notnull"`
ExternalTokenEncryptionKeys clibase.StringArray `json:"external_token_encryption_keys,omitempty" typescript:",notnull"`
Provisioner ProvisionerConfig `json:"provisioner,omitempty" typescript:",notnull"`
RateLimit RateLimitConfig `json:"rate_limit,omitempty" typescript:",notnull"`
Experiments clibase.StringArray `json:"experiments,omitempty" typescript:",notnull"`
Expand DownExpand Up@@ -1605,7 +1609,14 @@ when required by your organization's security policy.`,
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey, "true").Mark(annotationSecretKey, "true"),
Value: &c.SCIMAPIKey,
},

{
Name: "External Token Encryption Keys",
Description: "Encrypt OIDC and Git authentication tokens with AES-256-GCM in the database. The value must be a comma-separated list of base64-encoded keys. Each key, when base64-decoded, must be exactly 32 bytes in length. The first key will be used to encrypt new values. Subsequent keys will be used as a fallback when decrypting. During normal operation it is recommended to only set one key.",
Flag: "external-token-encryption-keys",
Env: "CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS",
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey, "true").Mark(annotationSecretKey, "true"),
Value: &c.ExternalTokenEncryptionKeys,
},
{
Name: "Disable Path Apps",
Description: "Disable workspace apps that are not served from subdomains. Path-based apps can make requests to the Coder API and pose a security risk when the workspace serves malicious JavaScript. This is recommended for security purposes if a --wildcard-access-url is configured.",
Expand DownExpand Up@@ -1783,7 +1794,7 @@ func (c *DeploymentValues) WithoutSecrets() (*DeploymentValues, error) {

// This only works with string values for now.
switch v := opt.Value.(type) {
case *clibase.String:
case *clibase.String, *clibase.StringArray:
err := v.Set("")
if err != nil {
panic(err)
Expand Down
3 changes: 3 additions & 0 deletionscodersdk/deployment_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -57,6 +57,9 @@ func TestDeploymentValues_HighlyConfigurable(t *testing.T) {
"SCIM API Key": {
yaml: true,
},
"External Token Encryption Keys": {
yaml: true,
},
// These complex objects should be configured through YAML.
"Support Links": {
flag: true,
Expand Down
129 changes: 129 additions & 0 deletionsdocs/admin/encryption.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
# Database Encryption

By default, Coder stores external user tokens in plaintext in the database.
Database Encryption allows Coder administrators to encrypt these tokens at-rest,
preventing attackers with database access from using them to impersonate users.

## How it works

Coder allows administrators to specify up to two
[external token encryption keys](../cli/server.md#external-token-encryption-keys).
If configured, Coder will use these keys to encrypt external user tokens before
storing them in the database. The encryption algorithm used is AES-256-GCM with
a 32-byte key length.

Coder will use the first key provided for both encryption and decryption. If a
second key is provided, Coder will use it for decryption only. This allows
administrators to rotate encryption keys without invalidating existing tokens.

The following database fields are currently encrypted:

- `user_links.oauth_access_token`
- `user_links.oauth_refresh_token`
- `git_auth_links.oauth_access_token`
- `git_auth_links.oauth_refresh_token`

Additional database fields may be encrypted in the future.

> Implementation notes: each encrypted database column `$C` has a corresponding
> `$C_key_id` column. This column is used to determine which encryption key was
> used to encrypt the data. This allows Coder to rotate encryption keys without
> invalidating existing tokens, and provides referential integrity for encrypted
> data.
>
> The `$C_key_id` column stores the first 7 bytes of the SHA-256 hash of the
> encryption key used to encrypt the data.
>
> Encryption keys in use are stored in `dbcrypt_keys`. This table stores a
> record of all encryption keys that have been used to encrypt data. Active keys
> have a null `revoked_key_id` column, and revoked keys have a non-null
> `revoked_key_id` column. A key cannot be revoked until all rows referring to
> it have been re-encrypted with a different key.

## Enabling encryption

1. Ensure you have a valid backup of your database. **Do not skip this step.**
If you are using the built-in PostgreSQL database, you can run
[`coder server postgres-builtin-url`](../cli/server_postgres-builtin-url.md)
to get the connection URL.

1. Generate a 32-byte random key and base64-encode it. For example:

```shell
dd if=/dev/urandom bs=32 count=1 | base64
```

1. Store this key in a secure location (for example, a Kubernetes secret):

```shell
kubectl create secret generic coder-external-token-encryption-keys --from-literal=keys=<key>
```

1. In your Coder configuration set `CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS` to a
comma-separated list of base64-encoded keys. For example, in your Helm
`values.yaml`:

```yaml
coder:
env:
[...]
- name: CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS
valueFrom:
secretKeyRef:
name: coder-external-token-encryption-keys
key: keys
```

## Rotating keys

We recommend only having one active encryption key at a time normally. However,
if you need to rotate keys, you can perform the following procedure:

1. Ensure you have a valid backup of your database. **Do not skip this step.**

1. Generate a new encryption key following the same procedure as above.

1. Add the above key to the list of
[external token encryption keys](../cli/server.md#external-token-encryption-keys).
**The new key must appear first in the list**. For example, in the Kubernetes
secret created above:

```yaml
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: coder-external-token-encryption-keys
namespace: coder-namespace
data:
keys: <new-key>,<old-key1>,<old-key2>,...
```

1. After updating the configuration, restart the Coder server. The server will
now encrypt all new data with the new key, but will be able to decrypt tokens
encrypted with the old key(s).

1. To re-encrypt all encrypted database fields with the new key, run
[`coder dbcrypt-rotate`](../cli/dbcrypt-rotate.md). This command will
re-encrypt all tokens with the first key in the list of external token
encryption keys. We recommend performing this action during a maintenance
window.

> Note: this command requires direct access to the database. If you are using
> the built-in PostgreSQL database, you can run
> [`coder server postgres-builtin-url`](../cli/server_postgres-builtin-url.md)
> to get the connection URL.

1. Once the above command completes successfully, remove the old encryption key
from Coder's configuration and restart Coder once more. You can now safely
delete the old key from your secret store.

## Disabling encryption

Disabling encryption is currently not supported.

## Troubleshooting

- If Coder detects that the data stored in the database was not encrypted with
any known keys, it will refuse to start. If you are seeing this behaviour,
ensure that the encryption keys provided are correct.
1 change: 1 addition & 0 deletionsdocs/api/general.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionsdocs/api/schemas.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

1 change: 1 addition & 0 deletionsdocs/cli.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| [<code>config-ssh</code>](./cli/config-ssh.md) | Add an SSH Host entry for your workspaces "ssh coder.workspace" |
| [<code>create</code>](./cli/create.md) | Create a workspace |
| [<code>dbcrypt-rotate</code>](./cli/dbcrypt-rotate.md) | Rotate database encryption keys |
| [<code>delete</code>](./cli/delete.md) | Delete a workspace |
| [<code>dotfiles</code>](./cli/dotfiles.md) | Personalize your workspace by applying a canonical dotfiles repository |
| [<code>features</code>](./cli/features.md) | List Enterprise features |
Expand Down
31 changes: 31 additions & 0 deletionsdocs/cli/dbcrypt-rotate.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

9 changes: 9 additions & 0 deletionsdocs/cli/server.md
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionsdocs/images/icons/lock.svg
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

[8]ページ先頭

©2009-2025 Movatter.jp