Skip to content

Commit d4c74bf

Browse files
committed
support secondary cipher in dbcrypt
1 parent dbe6915 commit d4c74bf

File tree

8 files changed

+168
-134
lines changed

8 files changed

+168
-134
lines changed

coderd/deployment_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ func TestDeploymentValues(t *testing.T) {
2626
cfg.OIDC.EmailField.Set("some_random_field_you_never_expected")
2727
cfg.PostgresURL.Set(hi)
2828
cfg.SCIMAPIKey.Set(hi)
29-
cfg.ExternalTokenEncryptionKey.Set("the_random_key_we_never_expected") // len:32
30-
cfg.PreviousExternalTokenEncryptionKey.Set("another_random_key_we_unexpected") // len:32
29+
cfg.ExternalTokenEncryptionKeys.Set("the_random_key_we_never_expected,an_other_key_we_never_unexpected")
3130

3231
client := coderdtest.New(t, &coderdtest.Options{
3332
DeploymentValues: cfg,
@@ -46,8 +45,7 @@ func TestDeploymentValues(t *testing.T) {
4645
require.Empty(t, scrubbed.Values.OIDC.ClientSecret.Value())
4746
require.Empty(t, scrubbed.Values.PostgresURL.Value())
4847
require.Empty(t, scrubbed.Values.SCIMAPIKey.Value())
49-
require.Empty(t, scrubbed.Values.ExternalTokenEncryptionKey.Value())
50-
require.Empty(t, scrubbed.Values.PreviousExternalTokenEncryptionKey.Value())
48+
require.Empty(t, scrubbed.Values.ExternalTokenEncryptionKeys.Value())
5149
}
5250

5351
func TestDeploymentStats(t *testing.T) {

codersdk/deployment.go

Lines changed: 51 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -129,53 +129,52 @@ type DeploymentValues struct {
129129
DocsURL clibase.URL `json:"docs_url,omitempty"`
130130
RedirectToAccessURL clibase.Bool `json:"redirect_to_access_url,omitempty"`
131131
// HTTPAddress is a string because it may be set to zero to disable.
132-
HTTPAddress clibase.String `json:"http_address,omitempty" typescript:",notnull"`
133-
AutobuildPollInterval clibase.Duration `json:"autobuild_poll_interval,omitempty"`
134-
JobHangDetectorInterval clibase.Duration `json:"job_hang_detector_interval,omitempty"`
135-
DERP DERP `json:"derp,omitempty" typescript:",notnull"`
136-
Prometheus PrometheusConfig `json:"prometheus,omitempty" typescript:",notnull"`
137-
Pprof PprofConfig `json:"pprof,omitempty" typescript:",notnull"`
138-
ProxyTrustedHeaders clibase.StringArray `json:"proxy_trusted_headers,omitempty" typescript:",notnull"`
139-
ProxyTrustedOrigins clibase.StringArray `json:"proxy_trusted_origins,omitempty" typescript:",notnull"`
140-
CacheDir clibase.String `json:"cache_directory,omitempty" typescript:",notnull"`
141-
InMemoryDatabase clibase.Bool `json:"in_memory_database,omitempty" typescript:",notnull"`
142-
PostgresURL clibase.String `json:"pg_connection_url,omitempty" typescript:",notnull"`
143-
OAuth2 OAuth2Config `json:"oauth2,omitempty" typescript:",notnull"`
144-
OIDC OIDCConfig `json:"oidc,omitempty" typescript:",notnull"`
145-
Telemetry TelemetryConfig `json:"telemetry,omitempty" typescript:",notnull"`
146-
TLS TLSConfig `json:"tls,omitempty" typescript:",notnull"`
147-
Trace TraceConfig `json:"trace,omitempty" typescript:",notnull"`
148-
SecureAuthCookie clibase.Bool `json:"secure_auth_cookie,omitempty" typescript:",notnull"`
149-
StrictTransportSecurity clibase.Int64 `json:"strict_transport_security,omitempty" typescript:",notnull"`
150-
StrictTransportSecurityOptions clibase.StringArray `json:"strict_transport_security_options,omitempty" typescript:",notnull"`
151-
SSHKeygenAlgorithm clibase.String `json:"ssh_keygen_algorithm,omitempty" typescript:",notnull"`
152-
MetricsCacheRefreshInterval clibase.Duration `json:"metrics_cache_refresh_interval,omitempty" typescript:",notnull"`
153-
AgentStatRefreshInterval clibase.Duration `json:"agent_stat_refresh_interval,omitempty" typescript:",notnull"`
154-
AgentFallbackTroubleshootingURL clibase.URL `json:"agent_fallback_troubleshooting_url,omitempty" typescript:",notnull"`
155-
BrowserOnly clibase.Bool `json:"browser_only,omitempty" typescript:",notnull"`
156-
SCIMAPIKey clibase.String `json:"scim_api_key,omitempty" typescript:",notnull"`
157-
ExternalTokenEncryptionKey clibase.String `json:"external_token_encryption_key"`
158-
PreviousExternalTokenEncryptionKey clibase.String `json:"previous_external_token_encryption_key"`
159-
Provisioner ProvisionerConfig `json:"provisioner,omitempty" typescript:",notnull"`
160-
RateLimit RateLimitConfig `json:"rate_limit,omitempty" typescript:",notnull"`
161-
Experiments clibase.StringArray `json:"experiments,omitempty" typescript:",notnull"`
162-
UpdateCheck clibase.Bool `json:"update_check,omitempty" typescript:",notnull"`
163-
MaxTokenLifetime clibase.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"`
164-
Swagger SwaggerConfig `json:"swagger,omitempty" typescript:",notnull"`
165-
Logging LoggingConfig `json:"logging,omitempty" typescript:",notnull"`
166-
Dangerous DangerousConfig `json:"dangerous,omitempty" typescript:",notnull"`
167-
DisablePathApps clibase.Bool `json:"disable_path_apps,omitempty" typescript:",notnull"`
168-
SessionDuration clibase.Duration `json:"max_session_expiry,omitempty" typescript:",notnull"`
169-
DisableSessionExpiryRefresh clibase.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"`
170-
DisablePasswordAuth clibase.Bool `json:"disable_password_auth,omitempty" typescript:",notnull"`
171-
Support SupportConfig `json:"support,omitempty" typescript:",notnull"`
172-
GitAuthProviders clibase.Struct[[]GitAuthConfig] `json:"git_auth,omitempty" typescript:",notnull"`
173-
SSHConfig SSHConfig `json:"config_ssh,omitempty" typescript:",notnull"`
174-
WgtunnelHost clibase.String `json:"wgtunnel_host,omitempty" typescript:",notnull"`
175-
DisableOwnerWorkspaceExec clibase.Bool `json:"disable_owner_workspace_exec,omitempty" typescript:",notnull"`
176-
ProxyHealthStatusInterval clibase.Duration `json:"proxy_health_status_interval,omitempty" typescript:",notnull"`
177-
EnableTerraformDebugMode clibase.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"`
178-
UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"`
132+
HTTPAddress clibase.String `json:"http_address,omitempty" typescript:",notnull"`
133+
AutobuildPollInterval clibase.Duration `json:"autobuild_poll_interval,omitempty"`
134+
JobHangDetectorInterval clibase.Duration `json:"job_hang_detector_interval,omitempty"`
135+
DERP DERP `json:"derp,omitempty" typescript:",notnull"`
136+
Prometheus PrometheusConfig `json:"prometheus,omitempty" typescript:",notnull"`
137+
Pprof PprofConfig `json:"pprof,omitempty" typescript:",notnull"`
138+
ProxyTrustedHeaders clibase.StringArray `json:"proxy_trusted_headers,omitempty" typescript:",notnull"`
139+
ProxyTrustedOrigins clibase.StringArray `json:"proxy_trusted_origins,omitempty" typescript:",notnull"`
140+
CacheDir clibase.String `json:"cache_directory,omitempty" typescript:",notnull"`
141+
InMemoryDatabase clibase.Bool `json:"in_memory_database,omitempty" typescript:",notnull"`
142+
PostgresURL clibase.String `json:"pg_connection_url,omitempty" typescript:",notnull"`
143+
OAuth2 OAuth2Config `json:"oauth2,omitempty" typescript:",notnull"`
144+
OIDC OIDCConfig `json:"oidc,omitempty" typescript:",notnull"`
145+
Telemetry TelemetryConfig `json:"telemetry,omitempty" typescript:",notnull"`
146+
TLS TLSConfig `json:"tls,omitempty" typescript:",notnull"`
147+
Trace TraceConfig `json:"trace,omitempty" typescript:",notnull"`
148+
SecureAuthCookie clibase.Bool `json:"secure_auth_cookie,omitempty" typescript:",notnull"`
149+
StrictTransportSecurity clibase.Int64 `json:"strict_transport_security,omitempty" typescript:",notnull"`
150+
StrictTransportSecurityOptions clibase.StringArray `json:"strict_transport_security_options,omitempty" typescript:",notnull"`
151+
SSHKeygenAlgorithm clibase.String `json:"ssh_keygen_algorithm,omitempty" typescript:",notnull"`
152+
MetricsCacheRefreshInterval clibase.Duration `json:"metrics_cache_refresh_interval,omitempty" typescript:",notnull"`
153+
AgentStatRefreshInterval clibase.Duration `json:"agent_stat_refresh_interval,omitempty" typescript:",notnull"`
154+
AgentFallbackTroubleshootingURL clibase.URL `json:"agent_fallback_troubleshooting_url,omitempty" typescript:",notnull"`
155+
BrowserOnly clibase.Bool `json:"browser_only,omitempty" typescript:",notnull"`
156+
SCIMAPIKey clibase.String `json:"scim_api_key,omitempty" typescript:",notnull"`
157+
ExternalTokenEncryptionKeys clibase.StringArray `json:"external_token_encryption_keys"`
158+
Provisioner ProvisionerConfig `json:"provisioner,omitempty" typescript:",notnull"`
159+
RateLimit RateLimitConfig `json:"rate_limit,omitempty" typescript:",notnull"`
160+
Experiments clibase.StringArray `json:"experiments,omitempty" typescript:",notnull"`
161+
UpdateCheck clibase.Bool `json:"update_check,omitempty" typescript:",notnull"`
162+
MaxTokenLifetime clibase.Duration `json:"max_token_lifetime,omitempty" typescript:",notnull"`
163+
Swagger SwaggerConfig `json:"swagger,omitempty" typescript:",notnull"`
164+
Logging LoggingConfig `json:"logging,omitempty" typescript:",notnull"`
165+
Dangerous DangerousConfig `json:"dangerous,omitempty" typescript:",notnull"`
166+
DisablePathApps clibase.Bool `json:"disable_path_apps,omitempty" typescript:",notnull"`
167+
SessionDuration clibase.Duration `json:"max_session_expiry,omitempty" typescript:",notnull"`
168+
DisableSessionExpiryRefresh clibase.Bool `json:"disable_session_expiry_refresh,omitempty" typescript:",notnull"`
169+
DisablePasswordAuth clibase.Bool `json:"disable_password_auth,omitempty" typescript:",notnull"`
170+
Support SupportConfig `json:"support,omitempty" typescript:",notnull"`
171+
GitAuthProviders clibase.Struct[[]GitAuthConfig] `json:"git_auth,omitempty" typescript:",notnull"`
172+
SSHConfig SSHConfig `json:"config_ssh,omitempty" typescript:",notnull"`
173+
WgtunnelHost clibase.String `json:"wgtunnel_host,omitempty" typescript:",notnull"`
174+
DisableOwnerWorkspaceExec clibase.Bool `json:"disable_owner_workspace_exec,omitempty" typescript:",notnull"`
175+
ProxyHealthStatusInterval clibase.Duration `json:"proxy_health_status_interval,omitempty" typescript:",notnull"`
176+
EnableTerraformDebugMode clibase.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"`
177+
UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"`
179178

180179
Config clibase.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"`
181180
WriteConfig clibase.Bool `json:"write_config,omitempty" typescript:",notnull"`
@@ -1591,20 +1590,12 @@ when required by your organization's security policy.`,
15911590
Value: &c.SCIMAPIKey,
15921591
},
15931592
{
1594-
Name: "External Token Encryption Key",
1595-
Description: "Encrypt OIDC and Git authentication tokens with AES-256-GCM in the database. The value must be a base64-encoded key exactly 32 bytes in length.",
1596-
Flag: "external-token-encryption-key",
1597-
Env: "CODER_EXTERNAL_TOKEN_ENCRYPTION_KEY",
1593+
Name: "External Token Encryption Keys",
1594+
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. A maximum of two keys may be provided. 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.",
1595+
Flag: "external-token-encryption-keys",
1596+
Env: "CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS",
15981597
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey, "true").Mark(annotationSecretKey, "true"),
1599-
Value: &c.ExternalTokenEncryptionKey,
1600-
},
1601-
{
1602-
Name: "Previous External Token Encryption Key",
1603-
Description: "When rotating external token encryption key, provide the previous encryption key. The value must be a base64-encoded key exactly 32 bytes in length.",
1604-
Flag: "previous-external-token-encryption-key",
1605-
Env: "CODER_PREVIOUS_EXTERNAL_TOKEN_ENCRYPTION_KEY",
1606-
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey, "true").Mark(annotationSecretKey, "true"),
1607-
Value: &c.PreviousExternalTokenEncryptionKey,
1598+
Value: &c.ExternalTokenEncryptionKeys,
16081599
},
16091600
{
16101601
Name: "Disable Path Apps",

codersdk/deployment_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,7 @@ func TestDeploymentValues_HighlyConfigurable(t *testing.T) {
5757
"SCIM API Key": {
5858
yaml: true,
5959
},
60-
"External Token Encryption Key": {
61-
yaml: true,
62-
},
63-
"Previous External Token Encryption Key": {
60+
"External Token Encryption Keys": {
6461
yaml: true,
6562
},
6663
// These complex objects should be configured through YAML.

enterprise/cli/server.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,28 @@ func (r *RootCmd) server() *clibase.Cmd {
7171
ProvisionerDaemonPSK: options.DeploymentValues.Provisioner.DaemonPSK.Value(),
7272
}
7373

74-
if options.DeploymentValues.ExternalTokenEncryptionKey.Value() != "" {
75-
key, err := base64.StdEncoding.DecodeString(options.DeploymentValues.ExternalTokenEncryptionKey.String())
74+
if encKeys := options.DeploymentValues.ExternalTokenEncryptionKeys.Value(); len(encKeys) != 0 {
75+
if len(encKeys) > 2 {
76+
return nil, nil, xerrors.Errorf("at most 2 external-token-encryption-keys may be specified")
77+
}
78+
k1, err := base64.StdEncoding.DecodeString(encKeys[0])
7679
if err != nil {
7780
return nil, nil, xerrors.Errorf("decode external-token-encryption-key: %w", err)
7881
}
79-
o.ExternalTokenEncryption, err = dbcrypt.CipherAES256(key)
82+
o.PrimaryExternalTokenEncryption, err = dbcrypt.CipherAES256(k1)
8083
if err != nil {
8184
return nil, nil, xerrors.Errorf("create external-token-encryption-key cipher: %w", err)
8285
}
86+
if len(encKeys) > 1 {
87+
k2, err := base64.StdEncoding.DecodeString(encKeys[0])
88+
if err != nil {
89+
return nil, nil, xerrors.Errorf("decode external-token-encryption-key: %w", err)
90+
}
91+
o.SecondaryExternalTokenEncryption, err = dbcrypt.CipherAES256(k2)
92+
if err != nil {
93+
return nil, nil, xerrors.Errorf("create external-token-encryption-key cipher: %w", err)
94+
}
95+
}
8396
}
8497

8598
api, err := coderd.New(ctx, o)

enterprise/coderd/coderd.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
6666

6767
externalTokenCipher := &atomic.Pointer[dbcrypt.Cipher]{}
6868
cryptDB, err := dbcrypt.New(ctx, options.Database, &dbcrypt.Options{
69-
ExternalTokenCipher: externalTokenCipher,
69+
PrimaryCipher: externalTokenCipher,
7070
})
7171
if err != nil {
7272
cancelFunc()
@@ -375,9 +375,12 @@ type Options struct {
375375
RBAC bool
376376
AuditLogging bool
377377
// Whether to block non-browser connections.
378-
BrowserOnly bool
379-
SCIMAPIKey []byte
380-
ExternalTokenEncryption dbcrypt.Cipher
378+
BrowserOnly bool
379+
SCIMAPIKey []byte
380+
381+
// TODO: wire these up properly
382+
PrimaryExternalTokenEncryption dbcrypt.Cipher
383+
SecondaryExternalTokenEncryption dbcrypt.Cipher
381384

382385
// Used for high availability.
383386
ReplicaSyncUpdateInterval time.Duration
@@ -447,7 +450,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
447450
codersdk.FeatureHighAvailability: api.DERPServerRelayAddress != "",
448451
codersdk.FeatureMultipleGitAuth: len(api.GitAuthConfigs) > 1,
449452
codersdk.FeatureTemplateRBAC: api.RBAC,
450-
codersdk.FeatureExternalTokenEncryption: api.ExternalTokenEncryption != nil,
453+
codersdk.FeatureExternalTokenEncryption: api.PrimaryExternalTokenEncryption != nil,
451454
codersdk.FeatureExternalProvisionerDaemons: true,
452455
codersdk.FeatureAdvancedTemplateScheduling: true,
453456
// FeatureTemplateRestartRequirement depends on

0 commit comments

Comments
 (0)