@@ -40,6 +40,7 @@ import (
4040 "github.com/coder/quartz"
4141 "github.com/coder/serpent"
4242
43+ "github.com/coder/coder/v2/coderd/cryptokeys"
4344 "github.com/coder/coder/v2/coderd/entitlements"
4445 "github.com/coder/coder/v2/coderd/idpsync"
4546 "github.com/coder/coder/v2/coderd/runtimeconfig"
@@ -185,9 +186,6 @@ type Options struct {
185186 TemplateScheduleStore * atomic.Pointer [schedule.TemplateScheduleStore ]
186187 UserQuietHoursScheduleStore * atomic.Pointer [schedule.UserQuietHoursScheduleStore ]
187188 AccessControlStore * atomic.Pointer [dbauthz.AccessControlStore ]
188- // AppSecurityKey is the crypto key used to sign and encrypt tokens related to
189- // workspace applications. It consists of both a signing and encryption key.
190- AppSecurityKey workspaceapps.SecurityKey
191189 // CoordinatorResumeTokenProvider is used to provide and validate resume
192190 // tokens issued by and passed to the coordinator DRPC API.
193191 CoordinatorResumeTokenProvider tailnet.ResumeTokenProvider
@@ -251,6 +249,12 @@ type Options struct {
251249
252250 // OneTimePasscodeValidityPeriod specifies how long a one time passcode should be valid for.
253251 OneTimePasscodeValidityPeriod time.Duration
252+
253+ // Keycaches
254+ AppSigningKeyCache cryptokeys.SigningKeycache
255+ AppEncryptionKeyCache cryptokeys.EncryptionKeycache
256+ OIDCConvertKeyCache cryptokeys.SigningKeycache
257+ Clock quartz.Clock
254258}
255259
256260// @title Coder API
@@ -352,6 +356,9 @@ func New(options *Options) *API {
352356 if options .PrometheusRegistry == nil {
353357 options .PrometheusRegistry = prometheus .NewRegistry ()
354358 }
359+ if options .Clock == nil {
360+ options .Clock = quartz .NewReal ()
361+ }
355362 if options .DERPServer == nil && options .DeploymentValues .DERP .Server .Enable {
356363 options .DERPServer = derp .NewServer (key .NewNode (), tailnet .Logger (options .Logger .Named ("derp" )))
357364 }
@@ -444,6 +451,49 @@ func New(options *Options) *API {
444451 if err != nil {
445452 panic (xerrors .Errorf ("get deployment ID: %w" , err ))
446453 }
454+
455+ fetcher := & cryptokeys.DBFetcher {
456+ DB : options .Database ,
457+ }
458+
459+ if options .OIDCConvertKeyCache == nil {
460+ options .OIDCConvertKeyCache , err = cryptokeys .NewSigningCache (ctx ,
461+ options .Logger .Named ("oidc_convert_keycache" ),
462+ fetcher ,
463+ codersdk .CryptoKeyFeatureOIDCConvert ,
464+ )
465+ if err != nil {
466+ options .Logger .Critical (ctx , "failed to properly instantiate oidc convert signing cache" , slog .Error (err ))
467+ }
468+ }
469+
470+ if options .AppSigningKeyCache == nil {
471+ options .AppSigningKeyCache , err = cryptokeys .NewSigningCache (ctx ,
472+ options .Logger .Named ("app_signing_keycache" ),
473+ fetcher ,
474+ codersdk .CryptoKeyFeatureWorkspaceAppsToken ,
475+ )
476+ if err != nil {
477+ options .Logger .Critical (ctx , "failed to properly instantiate app signing key cache" , slog .Error (err ))
478+ }
479+ }
480+
481+ if options .AppEncryptionKeyCache == nil {
482+ options .AppEncryptionKeyCache , err = cryptokeys .NewEncryptionCache (ctx ,
483+ options .Logger ,
484+ fetcher ,
485+ codersdk .CryptoKeyFeatureWorkspaceAppsAPIKey ,
486+ )
487+ if err != nil {
488+ options .Logger .Critical (ctx , "failed to properly instantiate app encryption key cache" , slog .Error (err ))
489+ }
490+ }
491+
492+ // Start a background process that rotates keys. We intentionally start this after the caches
493+ // are created to force initial requests for a key to populate the caches. This helps catch
494+ // bugs that may only occur when a key isn't precached in tests and the latency cost is minimal.
495+ cryptokeys .StartRotator (ctx , options .Logger , options .Database )
496+
447497 api := & API {
448498 ctx : ctx ,
449499 cancel : cancel ,
@@ -464,7 +514,7 @@ func New(options *Options) *API {
464514 options .DeploymentValues ,
465515 oauthConfigs ,
466516 options .AgentInactiveDisconnectTimeout ,
467- options .AppSecurityKey ,
517+ options .AppSigningKeyCache ,
468518 ),
469519 metricsCache : metricsCache ,
470520 Auditor : atomic.Pointer [audit.Auditor ]{},
@@ -606,7 +656,7 @@ func New(options *Options) *API {
606656 ResumeTokenProvider : api .Options .CoordinatorResumeTokenProvider ,
607657 })
608658 if err != nil {
609- api .Logger .Fatal (api . ctx , "failed to initialize tailnet client service" , slog .Error (err ))
659+ api .Logger .Fatal (context . Background () , "failed to initialize tailnet client service" , slog .Error (err ))
610660 }
611661
612662 api .statsReporter = workspacestats .NewReporter (workspacestats.ReporterOptions {
@@ -628,9 +678,6 @@ func New(options *Options) *API {
628678 options .WorkspaceAppsStatsCollectorOptions .Reporter = api .statsReporter
629679 }
630680
631- if options .AppSecurityKey .IsZero () {
632- api .Logger .Fatal (api .ctx , "app security key cannot be zero" )
633- }
634681 api .workspaceAppServer = & workspaceapps.Server {
635682 Logger : workspaceAppsLogger ,
636683
@@ -642,11 +689,11 @@ func New(options *Options) *API {
642689
643690 SignedTokenProvider : api .WorkspaceAppsProvider ,
644691 AgentProvider : api .agentProvider ,
645- AppSecurityKey : options .AppSecurityKey ,
646692 StatsCollector : workspaceapps .NewStatsCollector (options .WorkspaceAppsStatsCollectorOptions ),
647693
648- DisablePathApps : options .DeploymentValues .DisablePathApps .Value (),
649- SecureAuthCookie : options .DeploymentValues .SecureAuthCookie .Value (),
694+ DisablePathApps : options .DeploymentValues .DisablePathApps .Value (),
695+ SecureAuthCookie : options .DeploymentValues .SecureAuthCookie .Value (),
696+ APIKeyEncryptionKeycache : options .AppEncryptionKeyCache ,
650697 }
651698
652699 apiKeyMiddleware := httpmw .ExtractAPIKeyMW (httpmw.ExtractAPIKeyConfig {
@@ -1434,6 +1481,9 @@ func (api *API) Close() error {
14341481 _ = api .agentProvider .Close ()
14351482 _ = api .statsReporter .Close ()
14361483 _ = api .NetworkTelemetryBatcher .Close ()
1484+ _ = api .OIDCConvertKeyCache .Close ()
1485+ _ = api .AppSigningKeyCache .Close ()
1486+ _ = api .AppEncryptionKeyCache .Close ()
14371487 return nil
14381488}
14391489
0 commit comments