Skip to content

Commit a50b1f0

Browse files
committed
Refactor key cache management for better clarity
- Consolidates key cache handling by replacing legacy key cache references with central key caches. - Enhances modularity and maintainability by using consistent key management methods. - Removes redundant `StaticKeyManager` implementation for streamlined code. - Adjusts cryptographic key generation and cache utilization across critical components.
1 parent 5567bc7 commit a50b1f0

File tree

14 files changed

+51
-67
lines changed

14 files changed

+51
-67
lines changed

coderd/coderd.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ type Options struct {
250250

251251
// OneTimePasscodeValidityPeriod specifies how long a one time passcode should be valid for.
252252
OneTimePasscodeValidityPeriod time.Duration
253+
254+
// Keycaches
255+
AppSigningKeyCache cryptokeys.SigningKeycache
256+
AppEncryptionKeyCache cryptokeys.EncryptionKeycache
257+
OIDCConvertKeyCache cryptokeys.SigningKeycache
253258
}
254259

255260
// @title Coder API
@@ -622,12 +627,6 @@ func New(options *Options) *API {
622627
api.Logger.Fatal(api.ctx, "start key rotator", slog.Error(err))
623628
}
624629

625-
api.oauthConvertKeycache, err = cryptokeys.NewSigningCache(api.Logger.Named("oauth_convert_keycache"), api.Database, database.CryptoKeyFeatureOIDCConvert)
626-
if err != nil {
627-
api.Logger.Fatal(api.ctx, "failed to initialize oauth convert key cache", slog.Error(err))
628-
}
629-
api.workspaceAppsKeyCache = appEncryptingKeyCache
630-
631630
api.statsReporter = workspacestats.NewReporter(workspacestats.ReporterOptions{
632631
Database: options.Database,
633632
Logger: options.Logger.Named("workspacestats"),
@@ -1406,12 +1405,6 @@ type API struct {
14061405
// dbRolluper rolls up template usage stats from raw agent and app
14071406
// stats. This is used to provide insights in the WebUI.
14081407
dbRolluper *dbrollup.Rolluper
1409-
1410-
// resumeTokenKeycache is used to fetch and cache keys used for signing JWTs
1411-
// oauthConvertKeycache is used to fetch and cache keys used for signing JWTs
1412-
// during OAuth conversions. See userauth.go.convertUserToOauth.
1413-
oauthConvertKeycache cryptokeys.SigningKeycache
1414-
workspaceAppsKeyCache cryptokeys.EncryptionKeycache
14151408
}
14161409

14171410
// Close waits for all WebSocket connections to drain before returning.

coderd/coderdtest/coderdtest.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import (
5555
"github.com/coder/coder/v2/coderd/audit"
5656
"github.com/coder/coder/v2/coderd/autobuild"
5757
"github.com/coder/coder/v2/coderd/awsidentity"
58+
"github.com/coder/coder/v2/coderd/cryptokeys"
5859
"github.com/coder/coder/v2/coderd/database"
5960
"github.com/coder/coder/v2/coderd/database/db2sdk"
6061
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -157,8 +158,7 @@ type Options struct {
157158
DatabaseRolluper *dbrollup.Rolluper
158159
WorkspaceUsageTrackerFlush chan int
159160
WorkspaceUsageTrackerTick chan time.Time
160-
161-
NotificationsEnqueuer notifications.Enqueuer
161+
NotificationsEnqueuer notifications.Enqueuer
162162
}
163163

164164
// New constructs a codersdk client connected to an in-memory API instance.
@@ -326,6 +326,13 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
326326
}
327327
auditor.Store(&options.Auditor)
328328

329+
oidcConvertKeyCache, err := cryptokeys.NewSigningCache(options.Logger.Named("oidc_convert_keycache"), options.Database, database.CryptoKeyFeatureOIDCConvert)
330+
require.NoError(t, err)
331+
appSigningKeyCache, err := cryptokeys.NewSigningCache(options.Logger.Named("app_signing_keycache"), options.Database, database.CryptoKeyFeatureWorkspaceAppsToken)
332+
require.NoError(t, err)
333+
appEncryptionKeyCache, err := cryptokeys.NewEncryptionCache(options.Logger.Named("app_encryption_keycache"), options.Database, database.CryptoKeyFeatureWorkspaceAppsAPIKey)
334+
require.NoError(t, err)
335+
329336
ctx, cancelFunc := context.WithCancel(context.Background())
330337
lifecycleExecutor := autobuild.NewExecutor(
331338
ctx,
@@ -533,6 +540,9 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
533540
WorkspaceUsageTracker: wuTracker,
534541
NotificationsEnqueuer: options.NotificationsEnqueuer,
535542
OneTimePasscodeValidityPeriod: options.OneTimePasscodeValidityPeriod,
543+
AppSigningKeyCache: appSigningKeyCache,
544+
AppEncryptionKeyCache: appEncryptionKeyCache,
545+
OIDCConvertKeyCache: oidcConvertKeyCache,
536546
}
537547
}
538548

coderd/cryptokeys/dbkeycache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ func isEncryptionKeyFeature(feature database.CryptoKeyFeature) bool {
269269

270270
func isSigningKeyFeature(feature database.CryptoKeyFeature) bool {
271271
switch feature {
272-
case database.CryptoKeyFeatureTailnetResume, database.CryptoKeyFeatureOIDCConvert:
272+
case database.CryptoKeyFeatureTailnetResume, database.CryptoKeyFeatureOIDCConvert, database.CryptoKeyFeatureWorkspaceAppsToken:
273273
return true
274274
default:
275275
return false

coderd/cryptokeys/rotate.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ func generateNewSecret(feature database.CryptoKeyFeature) (string, error) {
231231
switch feature {
232232
case database.CryptoKeyFeatureWorkspaceAppsAPIKey:
233233
return generateKey(32)
234+
case database.CryptoKeyFeatureWorkspaceAppsToken:
235+
return generateKey(64)
234236
case database.CryptoKeyFeatureOIDCConvert:
235237
return generateKey(64)
236238
case database.CryptoKeyFeatureTailnetResume:
@@ -252,6 +254,8 @@ func tokenDuration(feature database.CryptoKeyFeature) time.Duration {
252254
switch feature {
253255
case database.CryptoKeyFeatureWorkspaceAppsAPIKey:
254256
return WorkspaceAppsTokenDuration
257+
case database.CryptoKeyFeatureWorkspaceAppsToken:
258+
return WorkspaceAppsTokenDuration
255259
case database.CryptoKeyFeatureOIDCConvert:
256260
return OIDCConvertTokenDuration
257261
case database.CryptoKeyFeatureTailnetResume:

coderd/jwtutils/jwe.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -130,30 +130,3 @@ func Decrypt(ctx context.Context, d DecryptKeyProvider, token string, claims Cla
130130

131131
return claims.Validate(options.RegisteredClaims)
132132
}
133-
134-
type StaticKeyManager struct {
135-
ID string
136-
Key interface{}
137-
}
138-
139-
func (s StaticKeyManager) SigningKey(_ context.Context) (string, interface{}, error) {
140-
return s.ID, s.Key, nil
141-
}
142-
143-
func (s StaticKeyManager) VerifyingKey(_ context.Context, id string) (interface{}, error) {
144-
if id != s.ID {
145-
return nil, xerrors.Errorf("invalid id %q", id)
146-
}
147-
return s.Key, nil
148-
}
149-
150-
func (s StaticKeyManager) EncryptingKey(_ context.Context) (string, interface{}, error) {
151-
return s.ID, s.Key, nil
152-
}
153-
154-
func (s StaticKeyManager) DecryptingKey(_ context.Context, id string) (interface{}, error) {
155-
if id != s.ID {
156-
return nil, xerrors.Errorf("invalid id %q", id)
157-
}
158-
return s.Key, nil
159-
}

coderd/userauth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ func (api *API) postConvertLoginType(rw http.ResponseWriter, r *http.Request) {
167167
ToLoginType: req.ToType,
168168
}
169169

170-
token, err := jwtutils.Sign(dbauthz.AsKeyRotator(ctx), api.oauthConvertKeycache, claims)
170+
token, err := jwtutils.Sign(dbauthz.AsKeyRotator(ctx), api.OIDCConvertKeyCache, claims)
171171
if err != nil {
172172
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
173173
Message: "Internal error signing state jwt.",
@@ -1676,7 +1676,7 @@ func (api *API) convertUserToOauth(ctx context.Context, r *http.Request, db data
16761676
}
16771677
}
16781678
var claims OAuthConvertStateClaims
1679-
err = jwtutils.Verify(dbauthz.AsKeyRotator(ctx), api.oauthConvertKeycache, jwtCookie.Value, &claims)
1679+
err = jwtutils.Verify(dbauthz.AsKeyRotator(ctx), api.OIDCConvertKeyCache, jwtCookie.Value, &claims)
16801680
if xerrors.Is(err, cryptokeys.ErrKeyNotFound) || xerrors.Is(err, cryptokeys.ErrKeyInvalid) || xerrors.Is(err, jose.ErrCryptoFailure) {
16811681
// These errors are probably because the user is mixing 2 coder deployments.
16821682
return database.User{}, idpsync.HTTPError{

coderd/workspaceagents_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ func TestWorkspaceAgentClientCoordinate_ResumeToken(t *testing.T) {
560560
resumeTokenSigningKey, err := tailnet.GenerateResumeTokenSigningKey()
561561
mgr := jwtutils.StaticKeyManager{
562562
ID: uuid.New().String(),
563-
Key: resumeTokenSigningKey,
563+
Key: resumeTokenSigningKey[:],
564564
}
565565
require.NoError(t, err)
566566
resumeTokenProvider := newResumeTokenRecordingProvider(

coderd/workspaceapps.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request
123123
return
124124
}
125125

126-
encryptedAPIKey, err := jwtutils.Encrypt(ctx, api.workspaceAppsKeyCache, workspaceapps.EncryptedAPIKeyPayload{
126+
encryptedAPIKey, err := jwtutils.Encrypt(ctx, api.AppEncryptionKeyCache, workspaceapps.EncryptedAPIKeyPayload{
127127
APIKey: cookie.Value,
128128
})
129129
if err != nil {

coderd/workspaceapps/db.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"golang.org/x/exp/slices"
1414
"golang.org/x/xerrors"
1515

16+
"github.com/go-jose/go-jose/v4/jwt"
17+
1618
"cdr.dev/slog"
1719
"github.com/coder/coder/v2/coderd/database"
1820
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -22,7 +24,6 @@ import (
2224
"github.com/coder/coder/v2/coderd/rbac"
2325
"github.com/coder/coder/v2/coderd/rbac/policy"
2426
"github.com/coder/coder/v2/codersdk"
25-
"github.com/go-jose/go-jose/v4/jwt"
2627
)
2728

2829
// DBTokenProvider provides authentication and authorization for workspace apps

coderd/workspaceapps/db_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/coder/coder/v2/agent/agenttest"
2222
"github.com/coder/coder/v2/coderd/coderdtest"
2323
"github.com/coder/coder/v2/coderd/httpmw"
24+
"github.com/coder/coder/v2/coderd/jwtutils"
2425
"github.com/coder/coder/v2/coderd/workspaceapps"
2526
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
2627
"github.com/coder/coder/v2/codersdk"
@@ -295,7 +296,8 @@ func Test_ResolveRequest(t *testing.T) {
295296
require.Equal(t, codersdk.SignedAppTokenCookie, cookie.Name)
296297
require.Equal(t, req.BasePath, cookie.Path)
297298

298-
parsedToken, err := api.workspaceAppsKeyCache.VerifySignedToken(cookie.Value)
299+
var parsedToken workspaceapps.SignedToken
300+
err := jwtutils.Verify(ctx, api.AppSigningKeyCache, cookie.Value, &parsedToken)
299301
require.NoError(t, err)
300302
// normalize expiry
301303
require.WithinDuration(t, token.Expiry.Time(), parsedToken.Expiry.Time(), 2*time.Second)
@@ -551,7 +553,8 @@ func Test_ResolveRequest(t *testing.T) {
551553
AgentID: agentID,
552554
AppURL: appURL,
553555
}
554-
badTokenStr, err := api.AppSecurityKey.SignToken(badToken)
556+
557+
badTokenStr, err := jwtutils.Sign(ctx, api.AppSigningKeyCache, badToken)
555558
require.NoError(t, err)
556559

557560
req := (workspaceapps.Request{
@@ -594,7 +597,8 @@ func Test_ResolveRequest(t *testing.T) {
594597
require.Len(t, cookies, 1)
595598
require.Equal(t, cookies[0].Name, codersdk.SignedAppTokenCookie)
596599
require.NotEqual(t, cookies[0].Value, badTokenStr)
597-
parsedToken, err := api.AppSecurityKey.VerifySignedToken(cookies[0].Value)
600+
var parsedToken workspaceapps.SignedToken
601+
err = jwtutils.Verify(ctx, api.AppSigningKeyCache, cookies[0].Value, &parsedToken)
598602
require.NoError(t, err)
599603
require.Equal(t, appNameOwner, parsedToken.AppSlugOrPort)
600604
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy