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

Commit2e2ed4a

Browse files
committed
plumb through dbcrypt in enterprise/coderd
1 parent8d5701a commit2e2ed4a

28 files changed

+485
-30
lines changed

‎cli/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
685685
options.Database=dbfake.New()
686686
options.Pubsub=pubsub.NewInMemory()
687687
}else {
688-
sqlDB,err:=connectToPostgres(ctx,logger,sqlDriver,vals.PostgresURL.String())
688+
sqlDB,err:=ConnectToPostgres(ctx,logger,sqlDriver,vals.PostgresURL.String())
689689
iferr!=nil {
690690
returnxerrors.Errorf("connect to postgres: %w",err)
691691
}
@@ -1950,7 +1950,7 @@ func BuildLogger(inv *clibase.Invocation, cfg *codersdk.DeploymentValues) (slog.
19501950
},nil
19511951
}
19521952

1953-
funcconnectToPostgres(ctx context.Context,logger slog.Logger,driverstring,dbURLstring) (*sql.DB,error) {
1953+
funcConnectToPostgres(ctx context.Context,logger slog.Logger,driverstring,dbURLstring) (*sql.DB,error) {
19541954
logger.Debug(ctx,"connecting to postgresql")
19551955

19561956
// Try to connect for 30 seconds.

‎cli/server_createadminuser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *clibase.Cmd {
6262
newUserDBURL=url
6363
}
6464

65-
sqlDB,err:=connectToPostgres(ctx,logger,"postgres",newUserDBURL)
65+
sqlDB,err:=ConnectToPostgres(ctx,logger,"postgres",newUserDBURL)
6666
iferr!=nil {
6767
returnxerrors.Errorf("connect to postgres: %w",err)
6868
}

‎cli/testdata/coder_server_--help.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,14 @@ These options are only available in the Enterprise Edition.
458458
An HTTP URL that is accessible by other replicas to relay DERP
459459
traffic. Required for high availability.
460460

461+
--external-token-encryption-keys string-array, $CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS
462+
Encrypt OIDC and Git authentication tokens with AES-256-GCM in the
463+
database. The value must be a comma-separated list of base64-encoded
464+
keys. A maximum of two keys may be provided. Each key, when
465+
base64-decoded, must be exactly 32 bytes in length. The first key will
466+
be used to encrypt new values. Subsequent keys will be used as a
467+
fallback when decrypting.
468+
461469
--scim-auth-header string, $CODER_SCIM_AUTH_HEADER
462470
Enables SCIM and sets the authentication header for the built-in SCIM
463471
server. New users are automatically created with OIDC authentication.

‎coderd/apidoc/docs.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/deployment_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +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.ExternalTokenEncryptionKeys.Set("the_random_key_we_never_expected,an_other_key_we_never_unexpected")
2930

3031
client:=coderdtest.New(t,&coderdtest.Options{
3132
DeploymentValues:cfg,
@@ -44,6 +45,7 @@ func TestDeploymentValues(t *testing.T) {
4445
require.Empty(t,scrubbed.Values.OIDC.ClientSecret.Value())
4546
require.Empty(t,scrubbed.Values.PostgresURL.Value())
4647
require.Empty(t,scrubbed.Values.SCIMAPIKey.Value())
48+
require.Empty(t,scrubbed.Values.ExternalTokenEncryptionKeys.Value())
4749
}
4850

4951
funcTestDeploymentStats(t*testing.T) {

‎coderd/httpmw/apikey.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,12 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
247247
UserID:key.UserID,
248248
LoginType:key.LoginType,
249249
})
250+
iferrors.Is(err,sql.ErrNoRows) {
251+
returnoptionalWrite(http.StatusUnauthorized, codersdk.Response{
252+
Message:SignedOutErrorMessage,
253+
Detail:"You must re-authenticate with the login provider.",
254+
})
255+
}
250256
iferr!=nil {
251257
returnwrite(http.StatusInternalServerError, codersdk.Response{
252258
Message:"A database error occurred",

‎codersdk/deployment.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ const (
4646
FeatureExternalProvisionerDaemonsFeatureName="external_provisioner_daemons"
4747
FeatureAppearanceFeatureName="appearance"
4848
FeatureAdvancedTemplateSchedulingFeatureName="advanced_template_scheduling"
49-
FeatureTemplateAutostopRequirementFeatureName="template_autostop_requirement"
5049
FeatureWorkspaceProxyFeatureName="workspace_proxy"
50+
FeatureExternalTokenEncryptionFeatureName="external_token_encryption"
51+
FeatureTemplateAutostopRequirementFeatureName="template_autostop_requirement"
5152
)
5253

5354
// FeatureNames must be kept in-sync with the Feature enum above.
@@ -64,6 +65,8 @@ var FeatureNames = []FeatureName{
6465
FeatureAdvancedTemplateScheduling,
6566
FeatureWorkspaceProxy,
6667
FeatureUserRoleManagement,
68+
FeatureExternalTokenEncryption,
69+
FeatureTemplateAutostopRequirement,
6770
}
6871

6972
// Humanize returns the feature name in a human-readable format.
@@ -152,6 +155,7 @@ type DeploymentValues struct {
152155
AgentFallbackTroubleshootingURL clibase.URL`json:"agent_fallback_troubleshooting_url,omitempty" typescript:",notnull"`
153156
BrowserOnly clibase.Bool`json:"browser_only,omitempty" typescript:",notnull"`
154157
SCIMAPIKey clibase.String`json:"scim_api_key,omitempty" typescript:",notnull"`
158+
ExternalTokenEncryptionKeys clibase.StringArray`json:"external_token_encryption_keys,omitempty" typescript:",notnull"`
155159
ProvisionerProvisionerConfig`json:"provisioner,omitempty" typescript:",notnull"`
156160
RateLimitRateLimitConfig`json:"rate_limit,omitempty" typescript:",notnull"`
157161
Experiments clibase.StringArray`json:"experiments,omitempty" typescript:",notnull"`
@@ -1603,7 +1607,14 @@ when required by your organization's security policy.`,
16031607
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey,"true").Mark(annotationSecretKey,"true"),
16041608
Value:&c.SCIMAPIKey,
16051609
},
1606-
1610+
{
1611+
Name:"External Token Encryption Keys",
1612+
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.",
1613+
Flag:"external-token-encryption-keys",
1614+
Env:"CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS",
1615+
Annotations: clibase.Annotations{}.Mark(annotationEnterpriseKey,"true").Mark(annotationSecretKey,"true"),
1616+
Value:&c.ExternalTokenEncryptionKeys,
1617+
},
16071618
{
16081619
Name:"Disable Path Apps",
16091620
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.",
@@ -1781,7 +1792,7 @@ func (c *DeploymentValues) WithoutSecrets() (*DeploymentValues, error) {
17811792

17821793
// This only works with string values for now.
17831794
switchv:=opt.Value.(type) {
1784-
case*clibase.String:
1795+
case*clibase.String,*clibase.StringArray:
17851796
err:=v.Set("")
17861797
iferr!=nil {
17871798
panic(err)

‎codersdk/deployment_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func TestDeploymentValues_HighlyConfigurable(t *testing.T) {
5757
"SCIM API Key": {
5858
yaml:true,
5959
},
60+
"External Token Encryption Keys": {
61+
yaml:true,
62+
},
6063
// These complex objects should be configured through YAML.
6164
"Support Links": {
6265
flag:true,

‎docs/api/general.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎docs/api/schemas.md

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎docs/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
2727
| ------------------------------------------------------| -----------------------------------------------------------------------------------------------------|
2828
|[<code>config-ssh</code>](./cli/config-ssh.md)| Add an SSH Host entry for your workspaces "ssh coder.workspace"|
2929
|[<code>create</code>](./cli/create.md)| Create a workspace|
30+
|[<code>dbcrypt-rotate</code>](./cli/dbcrypt-rotate.md)| Rotate database encryption keys|
3031
|[<code>delete</code>](./cli/delete.md)| Delete a workspace|
3132
|[<code>dotfiles</code>](./cli/dotfiles.md)| Personalize your workspace by applying a canonical dotfiles repository|
3233
|[<code>features</code>](./cli/features.md)| List Enterprise features|

‎docs/cli/dbcrypt-rotate.md

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎docs/cli/server.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎enterprise/cli/dbcrypt_rotate.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//go:build !slim
2+
3+
package cli
4+
5+
import (
6+
"bytes"
7+
"context"
8+
"encoding/base64"
9+
10+
"cdr.dev/slog"
11+
"cdr.dev/slog/sloggers/sloghuman"
12+
13+
"github.com/coder/coder/v2/cli"
14+
"github.com/coder/coder/v2/cli/clibase"
15+
"github.com/coder/coder/v2/coderd/database"
16+
"github.com/coder/coder/v2/codersdk"
17+
"github.com/coder/coder/v2/enterprise/dbcrypt"
18+
19+
"golang.org/x/xerrors"
20+
)
21+
22+
func (*RootCmd)dbcryptRotate()*clibase.Cmd {
23+
var (
24+
vals=new(codersdk.DeploymentValues)
25+
opts=vals.Options()
26+
)
27+
cmd:=&clibase.Cmd{
28+
Use:"dbcrypt-rotate --postgres-url <postgres_url> --external-token-encryption-keys <new-key>,<old-key>",
29+
Short:"Rotate database encryption keys",
30+
Options: clibase.OptionSet{
31+
*opts.ByName("Postgres Connection URL"),
32+
*opts.ByName("External Token Encryption Keys"),
33+
},
34+
Middleware:clibase.Chain(
35+
clibase.RequireNArgs(0),
36+
),
37+
Handler:func(inv*clibase.Invocation)error {
38+
ctx,cancel:=context.WithCancel(inv.Context())
39+
defercancel()
40+
logger:=slog.Make(sloghuman.Sink(inv.Stdout))
41+
42+
ifvals.PostgresURL=="" {
43+
returnxerrors.Errorf("no database configured")
44+
}
45+
46+
ifvals.ExternalTokenEncryptionKeys==nil||len(vals.ExternalTokenEncryptionKeys)!=2 {
47+
returnxerrors.Errorf("dbcrypt-rotate requires exactly two external token encryption keys")
48+
}
49+
50+
newKey,err:=base64.StdEncoding.DecodeString(vals.ExternalTokenEncryptionKeys[0])
51+
iferr!=nil {
52+
returnxerrors.Errorf("new key must be base64-encoded")
53+
}
54+
oldKey,err:=base64.StdEncoding.DecodeString(vals.ExternalTokenEncryptionKeys[1])
55+
iferr!=nil {
56+
returnxerrors.Errorf("old key must be base64-encoded")
57+
}
58+
ifbytes.Equal(newKey,oldKey) {
59+
returnxerrors.Errorf("old and new keys must be different")
60+
}
61+
62+
primaryCipher,err:=dbcrypt.CipherAES256(newKey)
63+
iferr!=nil {
64+
returnxerrors.Errorf("create primary cipher: %w",err)
65+
}
66+
secondaryCipher,err:=dbcrypt.CipherAES256(oldKey)
67+
iferr!=nil {
68+
returnxerrors.Errorf("create secondary cipher: %w",err)
69+
}
70+
ciphers:=dbcrypt.NewCiphers(primaryCipher,secondaryCipher)
71+
72+
sqlDB,err:=cli.ConnectToPostgres(inv.Context(),logger,"postgres",vals.PostgresURL.Value())
73+
iferr!=nil {
74+
returnxerrors.Errorf("connect to postgres: %w",err)
75+
}
76+
deferfunc() {
77+
_=sqlDB.Close()
78+
}()
79+
logger.Info(ctx,"connected to postgres")
80+
81+
db:=database.New(sqlDB)
82+
83+
cryptDB,err:=dbcrypt.New(ctx,db,ciphers)
84+
iferr!=nil {
85+
returnxerrors.Errorf("create cryptdb: %w",err)
86+
}
87+
88+
users,err:=cryptDB.GetUsers(ctx, database.GetUsersParams{})
89+
iferr!=nil {
90+
returnxerrors.Errorf("get users: %w",err)
91+
}
92+
foridx,usr:=rangeusers {
93+
userLinks,err:=cryptDB.GetUserLinksByUserID(ctx,usr.ID)
94+
iferr!=nil {
95+
returnxerrors.Errorf("get user links for user: %w",err)
96+
}
97+
for_,userLink:=rangeuserLinks {
98+
if_,err:=cryptDB.UpdateUserLink(ctx, database.UpdateUserLinkParams{
99+
OAuthAccessToken:userLink.OAuthAccessToken,
100+
OAuthRefreshToken:userLink.OAuthRefreshToken,
101+
OAuthExpiry:userLink.OAuthExpiry,
102+
UserID:usr.ID,
103+
LoginType:usr.LoginType,
104+
});err!=nil {
105+
returnxerrors.Errorf("update user link: %w",err)
106+
}
107+
}
108+
gitAuthLinks,err:=cryptDB.GetGitAuthLinksByUserID(ctx,usr.ID)
109+
iferr!=nil {
110+
returnxerrors.Errorf("get git auth links for user: %w",err)
111+
}
112+
for_,gitAuthLink:=rangegitAuthLinks {
113+
if_,err:=cryptDB.UpdateGitAuthLink(ctx, database.UpdateGitAuthLinkParams{
114+
ProviderID:gitAuthLink.ProviderID,
115+
UserID:usr.ID,
116+
UpdatedAt:gitAuthLink.UpdatedAt,
117+
OAuthAccessToken:gitAuthLink.OAuthAccessToken,
118+
OAuthRefreshToken:gitAuthLink.OAuthRefreshToken,
119+
OAuthExpiry:gitAuthLink.OAuthExpiry,
120+
});err!=nil {
121+
returnxerrors.Errorf("update git auth link: %w",err)
122+
}
123+
}
124+
logger.Info(ctx,"encrypted user tokens",slog.F("current",idx+1),slog.F("of",len(users)))
125+
}
126+
logger.Info(ctx,"operation completed successfully")
127+
returnnil
128+
},
129+
}
130+
returncmd
131+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp