Skip to content

Commit fe28e42

Browse files
committed
chore: Rename AppSigningKey to AppSecurityKey
1 parent a933d6d commit fe28e42

File tree

14 files changed

+84
-74
lines changed

14 files changed

+84
-74
lines changed

cli/server.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
781781
// Read the app signing key from the DB. We store it hex encoded
782782
// since the config table uses strings for the value and we
783783
// don't want to deal with automatic encoding issues.
784-
appSigningKeyStr, err := tx.GetAppSigningKey(ctx)
784+
appSecurityKeyStr, err := tx.GetAppSecurityKey(ctx)
785785
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
786786
return xerrors.Errorf("get app signing key: %w", err)
787787
}
@@ -794,26 +794,26 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
794794
// generated automatically on failure. Any workspace app token
795795
// smuggling operations in progress may fail, although with a
796796
// helpful error.
797-
if decoded, err := hex.DecodeString(appSigningKeyStr); err != nil || len(decoded) != len(workspaceapps.SigningKey{}) {
798-
b := make([]byte, len(workspaceapps.SigningKey{}))
797+
if decoded, err := hex.DecodeString(appSecurityKeyStr); err != nil || len(decoded) != len(workspaceapps.SecurityKey{}) {
798+
b := make([]byte, len(workspaceapps.SecurityKey{}))
799799
_, err := rand.Read(b)
800800
if err != nil {
801801
return xerrors.Errorf("generate fresh app signing key: %w", err)
802802
}
803803

804-
appSigningKeyStr = hex.EncodeToString(b)
805-
err = tx.UpsertAppSigningKey(ctx, appSigningKeyStr)
804+
appSecurityKeyStr = hex.EncodeToString(b)
805+
err = tx.UpsertAppSecurityKey(ctx, appSecurityKeyStr)
806806
if err != nil {
807807
return xerrors.Errorf("insert freshly generated app signing key to database: %w", err)
808808
}
809809
}
810810

811-
appSigningKey, err := workspaceapps.KeyFromString(appSigningKeyStr)
811+
appSecurityKey, err := workspaceapps.KeyFromString(appSecurityKeyStr)
812812
if err != nil {
813813
return xerrors.Errorf("decode app signing key from database: %w", err)
814814
}
815815

816-
options.AppSigningKey = appSigningKey
816+
options.AppSecurityKey = appSecurityKey
817817
return nil
818818
}, nil)
819819
if err != nil {

coderd/coderd.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ type Options struct {
123123
SwaggerEndpoint bool
124124
SetUserGroups func(ctx context.Context, tx database.Store, userID uuid.UUID, groupNames []string) error
125125
TemplateScheduleStore schedule.TemplateScheduleStore
126-
// AppSigningKey denotes the symmetric key to use for signing temporary app
127-
// tokens.
128-
AppSigningKey workspaceapps.SigningKey
126+
// AppSecurityKey is the crypto key used to sign and encrypt tokens related to
127+
// workspace applications. It consists of both a signing and encryption key.
128+
AppSecurityKey workspaceapps.SecurityKey
129129
HealthcheckFunc func(ctx context.Context) (*healthcheck.Report, error)
130130
HealthcheckTimeout time.Duration
131131
HealthcheckRefresh time.Duration
@@ -302,7 +302,7 @@ func New(options *Options) *API {
302302
options.DeploymentValues,
303303
oauthConfigs,
304304
options.AgentInactiveDisconnectTimeout,
305-
options.AppSigningKey,
305+
options.AppSecurityKey,
306306
),
307307
metricsCache: metricsCache,
308308
Auditor: atomic.Pointer[audit.Auditor]{},
@@ -340,7 +340,7 @@ func New(options *Options) *API {
340340

341341
SignedTokenProvider: api.WorkspaceAppsProvider,
342342
WorkspaceConnCache: api.workspaceAgentCache,
343-
AppSigningKey: options.AppSigningKey,
343+
AppSecurityKey: options.AppSecurityKey,
344344
}
345345

346346
apiKeyMiddleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{

coderd/coderdtest/coderdtest.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ import (
8080
"github.com/coder/coder/testutil"
8181
)
8282

83-
// AppSigningKey is a 64-byte key used to sign JWTs and encrypt JWEs for
83+
// AppSecurityKey is a 96-byte key used to sign JWTs and encrypt JWEs for
8484
// workspace app tokens in tests.
85-
var AppSigningKey = must(workspaceapps.KeyFromString("6465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e2077617320686572"))
85+
var AppSecurityKey = must(workspaceapps.KeyFromString("6465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e207761732068657265206465616e2077617320686572"))
8686

8787
type Options struct {
8888
// AccessURL denotes a custom access URL. By default we use the httptest
@@ -338,7 +338,7 @@ func NewOptions(t *testing.T, options *Options) (func(http.Handler), context.Can
338338
DeploymentValues: options.DeploymentValues,
339339
UpdateCheckOptions: options.UpdateCheckOptions,
340340
SwaggerEndpoint: options.SwaggerEndpoint,
341-
AppSigningKey: AppSigningKey,
341+
AppSecurityKey: AppSecurityKey,
342342
SSHConfig: options.ConfigSSH,
343343
HealthcheckFunc: options.HealthcheckFunc,
344344
HealthcheckTimeout: options.HealthcheckTimeout,

coderd/database/dbauthz/querier.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,14 @@ func (q *querier) GetLogoURL(ctx context.Context) (string, error) {
379379
return q.db.GetLogoURL(ctx)
380380
}
381381

382-
func (q *querier) GetAppSigningKey(ctx context.Context) (string, error) {
382+
func (q *querier) GetAppSecurityKey(ctx context.Context) (string, error) {
383383
// No authz checks
384-
return q.db.GetAppSigningKey(ctx)
384+
return q.db.GetAppSecurityKey(ctx)
385385
}
386386

387-
func (q *querier) UpsertAppSigningKey(ctx context.Context, data string) error {
387+
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
388388
// No authz checks as this is done during startup
389-
return q.db.UpsertAppSigningKey(ctx, data)
389+
return q.db.UpsertAppSecurityKey(ctx, data)
390390
}
391391

392392
func (q *querier) GetServiceBanner(ctx context.Context) (string, error) {

coderd/database/dbfake/databasefake.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ type data struct {
141141
lastUpdateCheck []byte
142142
serviceBanner []byte
143143
logoURL string
144-
appSigningKey string
144+
appSecurityKey string
145145
lastLicenseID int32
146146
}
147147

@@ -4444,18 +4444,18 @@ func (q *fakeQuerier) GetLogoURL(_ context.Context) (string, error) {
44444444
return q.logoURL, nil
44454445
}
44464446

4447-
func (q *fakeQuerier) GetAppSigningKey(_ context.Context) (string, error) {
4447+
func (q *fakeQuerier) GetAppSecurityKey(_ context.Context) (string, error) {
44484448
q.mutex.RLock()
44494449
defer q.mutex.RUnlock()
44504450

4451-
return q.appSigningKey, nil
4451+
return q.appSecurityKey, nil
44524452
}
44534453

4454-
func (q *fakeQuerier) UpsertAppSigningKey(_ context.Context, data string) error {
4454+
func (q *fakeQuerier) UpsertAppSecurityKey(_ context.Context, data string) error {
44554455
q.mutex.Lock()
44564456
defer q.mutex.Unlock()
44574457

4458-
q.appSigningKey = data
4458+
q.appSecurityKey = data
44594459
return nil
44604460
}
44614461

coderd/database/querier.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/siteconfig.sql

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'logo_url';
3131
-- name: GetLogoURL :one
3232
SELECT value FROM site_configs WHERE key = 'logo_url';
3333

34-
-- name: GetAppSigningKey :one
34+
-- name: GetAppSecurityKey :one
3535
SELECT value FROM site_configs WHERE key = 'app_signing_key';
3636

37-
-- name: UpsertAppSigningKey :exec
37+
-- name: UpsertAppSecurityKey :exec
3838
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
3939
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'app_signing_key';

coderd/workspaceapps.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request
123123
}
124124

125125
// Encrypt the API key.
126-
encryptedAPIKey, err := api.AppSigningKey.EncryptAPIKey(workspaceapps.EncryptedAPIKeyPayload{
126+
encryptedAPIKey, err := api.AppSecurityKey.EncryptAPIKey(workspaceapps.EncryptedAPIKeyPayload{
127127
APIKey: cookie.Value,
128128
})
129129
if err != nil {

coderd/workspaceapps/db.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ type DBTokenProvider struct {
3232
DeploymentValues *codersdk.DeploymentValues
3333
OAuth2Configs *httpmw.OAuth2Configs
3434
WorkspaceAgentInactiveTimeout time.Duration
35-
SigningKey SigningKey
35+
SigningKey SecurityKey
3636
}
3737

3838
var _ SignedTokenProvider = &DBTokenProvider{}
3939

40-
func NewDBTokenProvider(log slog.Logger, accessURL *url.URL, authz rbac.Authorizer, db database.Store, cfg *codersdk.DeploymentValues, oauth2Cfgs *httpmw.OAuth2Configs, workspaceAgentInactiveTimeout time.Duration, signingKey SigningKey) SignedTokenProvider {
40+
func NewDBTokenProvider(log slog.Logger, accessURL *url.URL, authz rbac.Authorizer, db database.Store, cfg *codersdk.DeploymentValues, oauth2Cfgs *httpmw.OAuth2Configs, workspaceAgentInactiveTimeout time.Duration, signingKey SecurityKey) SignedTokenProvider {
4141
if workspaceAgentInactiveTimeout == 0 {
4242
workspaceAgentInactiveTimeout = 1 * time.Minute
4343
}

coderd/workspaceapps/db_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func Test_ResolveRequest(t *testing.T) {
263263
require.Equal(t, codersdk.DevURLSignedAppTokenCookie, cookie.Name)
264264
require.Equal(t, req.BasePath, cookie.Path)
265265

266-
parsedToken, err := api.AppSigningKey.VerifySignedToken(cookie.Value)
266+
parsedToken, err := api.AppSecurityKey.VerifySignedToken(cookie.Value)
267267
require.NoError(t, err)
268268
// normalize expiry
269269
require.WithinDuration(t, token.Expiry, parsedToken.Expiry, 2*time.Second)
@@ -482,7 +482,7 @@ func Test_ResolveRequest(t *testing.T) {
482482
AgentID: agentID,
483483
AppURL: appURL,
484484
}
485-
badTokenStr, err := api.AppSigningKey.SignToken(badToken)
485+
badTokenStr, err := api.AppSecurityKey.SignToken(badToken)
486486
require.NoError(t, err)
487487

488488
req := workspaceapps.Request{
@@ -518,7 +518,7 @@ func Test_ResolveRequest(t *testing.T) {
518518
require.Len(t, cookies, 1)
519519
require.Equal(t, cookies[0].Name, codersdk.DevURLSignedAppTokenCookie)
520520
require.NotEqual(t, cookies[0].Value, badTokenStr)
521-
parsedToken, err := api.AppSigningKey.VerifySignedToken(cookies[0].Value)
521+
parsedToken, err := api.AppSecurityKey.VerifySignedToken(cookies[0].Value)
522522
require.NoError(t, err)
523523
require.Equal(t, appNameOwner, parsedToken.AppSlugOrPort)
524524
})

coderd/workspaceapps/proxy.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ type Server struct {
8383
RealIPConfig *httpmw.RealIPConfig
8484

8585
SignedTokenProvider SignedTokenProvider
86-
WorkspaceConnCache *wsconncache.Cache
87-
AppSigningKey SigningKey
86+
WorkspaceConnCache *wsconncache.Cache
87+
AppSecurityKey SecurityKey
8888

8989
websocketWaitMutex sync.Mutex
9090
websocketWaitGroup sync.WaitGroup
@@ -245,7 +245,7 @@ func (s *Server) SubdomainAppMW(middlewares ...func(http.Handler) http.Handler)
245245
// cookie and strip that query parameter.
246246
if encryptedAPIKey := r.URL.Query().Get(SubdomainProxyAPIKeyParam); encryptedAPIKey != "" {
247247
// Exchange the encoded API key for a real one.
248-
token, err := s.AppSigningKey.DecryptAPIKey(encryptedAPIKey)
248+
token, err := s.AppSecurityKey.DecryptAPIKey(encryptedAPIKey)
249249
if err != nil {
250250
s.Logger.Debug(ctx, "could not decrypt API key", slog.Error(err))
251251
site.RenderStaticErrorPage(rw, r, site.ErrorPageData{

coderd/workspaceapps/token.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,24 @@ func (t SignedToken) MatchesRequest(req Request) bool {
4444
t.AppSlugOrPort == req.AppSlugOrPort
4545
}
4646

47-
// AppSigningKey is used for signing and encrypting app tokens and API keys.
47+
// SecurityKey is used for signing and encrypting app tokens and API keys.
4848
//
4949
// The first 64 bytes of the key are used for signing tokens with HMAC-SHA256,
5050
// and the last 32 bytes are used for encrypting API keys with AES-256-GCM.
51-
type SigningKey [96]byte
51+
// We use a single key for both operations to avoid having to store and manage
52+
// two keys.
53+
type SecurityKey [96]byte
5254

53-
func KeyFromString(str string) (SigningKey, error) {
54-
var key SigningKey
55+
func (k SecurityKey) signingKey() []byte {
56+
return k[:64]
57+
}
58+
59+
func (k SecurityKey) encryptionKey() []byte {
60+
return k[64:]
61+
}
62+
63+
func KeyFromString(str string) (SecurityKey, error) {
64+
var key SecurityKey
5565
decoded, err := hex.DecodeString(str)
5666
if err != nil {
5767
return key, xerrors.Errorf("decode key: %w", err)
@@ -67,7 +77,7 @@ func KeyFromString(str string) (SigningKey, error) {
6777
// SignToken generates a signed workspace app token with the given payload. If
6878
// the payload doesn't have an expiry, it will be set to the current time plus
6979
// the default expiry.
70-
func (k SigningKey) SignToken(payload SignedToken) (string, error) {
80+
func (k SecurityKey) SignToken(payload SignedToken) (string, error) {
7181
if payload.Expiry.IsZero() {
7282
payload.Expiry = time.Now().Add(DefaultTokenExpiry)
7383
}
@@ -78,7 +88,7 @@ func (k SigningKey) SignToken(payload SignedToken) (string, error) {
7888

7989
signer, err := jose.NewSigner(jose.SigningKey{
8090
Algorithm: tokenSigningAlgorithm,
81-
Key: k[:64],
91+
Key: k.signingKey(),
8292
}, nil)
8393
if err != nil {
8494
return "", xerrors.Errorf("create signer: %w", err)
@@ -100,7 +110,7 @@ func (k SigningKey) SignToken(payload SignedToken) (string, error) {
100110
// VerifySignedToken parses a signed workspace app token with the given key and
101111
// returns the payload. If the token is invalid or expired, an error is
102112
// returned.
103-
func (k SigningKey) VerifySignedToken(str string) (SignedToken, error) {
113+
func (k SecurityKey) VerifySignedToken(str string) (SignedToken, error) {
104114
object, err := jose.ParseSigned(str)
105115
if err != nil {
106116
return SignedToken{}, xerrors.Errorf("parse JWS: %w", err)
@@ -112,7 +122,7 @@ func (k SigningKey) VerifySignedToken(str string) (SignedToken, error) {
112122
return SignedToken{}, xerrors.Errorf("expected token signing algorithm to be %q, got %q", tokenSigningAlgorithm, object.Signatures[0].Header.Algorithm)
113123
}
114124

115-
output, err := object.Verify(k[:64])
125+
output, err := object.Verify(k.signingKey())
116126
if err != nil {
117127
return SignedToken{}, xerrors.Errorf("verify JWS: %w", err)
118128
}
@@ -135,7 +145,7 @@ type EncryptedAPIKeyPayload struct {
135145
}
136146

137147
// EncryptAPIKey encrypts an API key for subdomain token smuggling.
138-
func (k SigningKey) EncryptAPIKey(payload EncryptedAPIKeyPayload) (string, error) {
148+
func (k SecurityKey) EncryptAPIKey(payload EncryptedAPIKeyPayload) (string, error) {
139149
if payload.APIKey == "" {
140150
return "", xerrors.New("API key is empty")
141151
}
@@ -155,7 +165,7 @@ func (k SigningKey) EncryptAPIKey(payload EncryptedAPIKeyPayload) (string, error
155165
jose.A256GCM,
156166
jose.Recipient{
157167
Algorithm: apiKeyEncryptionAlgorithm,
158-
Key: k[64:],
168+
Key: k.encryptionKey(),
159169
},
160170
&jose.EncrypterOptions{
161171
Compression: jose.DEFLATE,
@@ -174,7 +184,7 @@ func (k SigningKey) EncryptAPIKey(payload EncryptedAPIKeyPayload) (string, error
174184
}
175185

176186
// DecryptAPIKey undoes EncryptAPIKey and is used in the subdomain app handler.
177-
func (k SigningKey) DecryptAPIKey(encryptedAPIKey string) (string, error) {
187+
func (k SecurityKey) DecryptAPIKey(encryptedAPIKey string) (string, error) {
178188
encrypted, err := base64.RawURLEncoding.DecodeString(encryptedAPIKey)
179189
if err != nil {
180190
return "", xerrors.Errorf("base64 decode encrypted API key: %w", err)
@@ -189,7 +199,7 @@ func (k SigningKey) DecryptAPIKey(encryptedAPIKey string) (string, error) {
189199
}
190200

191201
// Decrypt using the hashed secret.
192-
decrypted, err := object.Decrypt(k[64:])
202+
decrypted, err := object.Decrypt(k.encryptionKey())
193203
if err != nil {
194204
return "", xerrors.Errorf("decrypt API key: %w", err)
195205
}

0 commit comments

Comments
 (0)