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

feat: enable key rotation#15066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
sreya merged 42 commits intomainfromjon/glue
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
42 commits
Select commitHold shift + click to select a range
b745c8e
feat: enable key rotation
sreyaOct 5, 2024
b98bff0
add migration
sreyaOct 5, 2024
0646b30
Refactor cryptographic key handling for OIDC and API keys
sreyaOct 5, 2024
b73b210
the end is nigh
sreyaOct 5, 2024
7fe88ea
fix migrations
sreyaOct 14, 2024
0323f79
Refactor key cache management for better clarity
sreyaOct 14, 2024
7413907
hm
sreyaOct 14, 2024
2ea31ab
fixing tests
sreyaOct 14, 2024
d0d168b
time to fix it
sreyaOct 15, 2024
33cdb96
Refactor cryptokeys Fetcher to include feature param
sreyaOct 16, 2024
08570b7
Refactor key caching and logging behavior
sreyaOct 16, 2024
b770762
Refactor crypto_key_feature migration logic
sreyaOct 16, 2024
7557ed2
Refactor key management and enhance logging
sreyaOct 16, 2024
fa9a75d
Update cryptokey feature test and migration logic
sreyaOct 17, 2024
94987b6
gen
sreyaOct 17, 2024
76561ac
Refactor cryptokeys cache to include key reader context
sreyaOct 17, 2024
53dcf36
Refactor jwtutils to remove redundant key reader
sreyaOct 17, 2024
c656d00
Remove unused cryptokey feature fixtures
sreyaOct 17, 2024
e7cfb46
Refactor cryptokeys comments and variable typo
sreyaOct 17, 2024
6432b0d
fix comments
sreyaOct 17, 2024
9ad187d
move rotator out of coderd
sreyaOct 17, 2024
d16e98f
remove composite jwtutil interfaces
sreyaOct 17, 2024
3809cd5
fix tests caused by moving rotator initiation out of coderd
sreyaOct 17, 2024
6d3c103
pr comments
sreyaOct 20, 2024
a3020fc
Merge branch 'main' into jon/glue
sreyaOct 20, 2024
bfa88b7
Refactor tests to remove direct database setup
sreyaOct 20, 2024
495c28f
Rename cryptokey migration files to update sequence
sreyaOct 20, 2024
692bb36
Fix conditional logging in key cache initialization
sreyaOct 20, 2024
4028995
Refactor crypto key management in tests
sreyaOct 20, 2024
6b9a3e4
add test for oidc jwt
sreyaOct 21, 2024
5ee6ad5
add test for tailnet_resume jwt
sreyaOct 21, 2024
886d87c
add test for workspaceapps
sreyaOct 21, 2024
5261442
add test for signedtoken
sreyaOct 22, 2024
4a1d974
fix migrations
sreyaOct 22, 2024
092a241
fmt
sreyaOct 22, 2024
87828a2
pr comments
sreyaOct 22, 2024
6aa90bc
Merge branch 'main' into jon/glue
sreyaOct 22, 2024
358aaa8
Rename cryptokey migration files to update sequence
sreyaOct 22, 2024
ad237ad
Merge branch 'main' into jon/glue
sreyaOct 24, 2024
5798a33
migrations
sreyaOct 24, 2024
200cd68
Refactor StaticKey to jwtutils package
sreyaOct 24, 2024
2194b4d
fix tests
sreyaOct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 21 additions & 81 deletionscli/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@ import (
"crypto/tls"
"crypto/x509"
"database/sql"
"encoding/hex"
"errors"
"flag"
"fmt"
Expand DownExpand Up@@ -62,6 +61,7 @@ import (
"github.com/coder/serpent"
"github.com/coder/wgtunnel/tunnelsdk"

"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/entitlements"
"github.com/coder/coder/v2/coderd/notifications/reports"
"github.com/coder/coder/v2/coderd/runtimeconfig"
Expand DownExpand Up@@ -97,7 +97,6 @@ import (
"github.com/coder/coder/v2/coderd/updatecheck"
"github.com/coder/coder/v2/coderd/util/slice"
stringutil "github.com/coder/coder/v2/coderd/util/strings"
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/coderd/workspacestats"
"github.com/coder/coder/v2/codersdk"
Expand DownExpand Up@@ -741,90 +740,31 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
return xerrors.Errorf("set deployment id: %w", err)
}
}

// Read the app signing key from the DB. We store it hex encoded
// since the config table uses strings for the value and we
// don't want to deal with automatic encoding issues.
appSecurityKeyStr, err := tx.GetAppSecurityKey(ctx)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get app signing key: %w", err)
}
// If the string in the DB is an invalid hex string or the
// length is not equal to the current key length, generate a new
// one.
//
// If the key is regenerated, old signed tokens and encrypted
// strings will become invalid. New signed app tokens will be
// generated automatically on failure. Any workspace app token
// smuggling operations in progress may fail, although with a
// helpful error.
if decoded, err := hex.DecodeString(appSecurityKeyStr); err != nil || len(decoded) != len(workspaceapps.SecurityKey{}) {
b := make([]byte, len(workspaceapps.SecurityKey{}))
_, err := rand.Read(b)
if err != nil {
return xerrors.Errorf("generate fresh app signing key: %w", err)
}

appSecurityKeyStr = hex.EncodeToString(b)
err = tx.UpsertAppSecurityKey(ctx, appSecurityKeyStr)
if err != nil {
return xerrors.Errorf("insert freshly generated app signing key to database: %w", err)
}
}

appSecurityKey, err := workspaceapps.KeyFromString(appSecurityKeyStr)
if err != nil {
return xerrors.Errorf("decode app signing key from database: %w", err)
}

options.AppSecurityKey = appSecurityKey

// Read the oauth signing key from the database. Like the app security, generate a new one
// if it is invalid for any reason.
oauthSigningKeyStr, err := tx.GetOAuthSigningKey(ctx)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get app oauth signing key: %w", err)
}
if decoded, err := hex.DecodeString(oauthSigningKeyStr); err != nil || len(decoded) != len(options.OAuthSigningKey) {
b := make([]byte, len(options.OAuthSigningKey))
_, err := rand.Read(b)
if err != nil {
return xerrors.Errorf("generate fresh oauth signing key: %w", err)
}

oauthSigningKeyStr = hex.EncodeToString(b)
err = tx.UpsertOAuthSigningKey(ctx, oauthSigningKeyStr)
if err != nil {
return xerrors.Errorf("insert freshly generated oauth signing key to database: %w", err)
}
}

oauthKeyBytes, err := hex.DecodeString(oauthSigningKeyStr)
if err != nil {
return xerrors.Errorf("decode oauth signing key from database: %w", err)
}
if len(oauthKeyBytes) != len(options.OAuthSigningKey) {
return xerrors.Errorf("oauth signing key in database is not the correct length, expect %d got %d", len(options.OAuthSigningKey), len(oauthKeyBytes))
}
copy(options.OAuthSigningKey[:], oauthKeyBytes)
if options.OAuthSigningKey == [32]byte{} {
return xerrors.Errorf("oauth signing key in database is empty")
}

// Read the coordinator resume token signing key from the
// database.
resumeTokenKey, err := tailnet.ResumeTokenSigningKeyFromDatabase(ctx, tx)
if err != nil {
return xerrors.Errorf("get coordinator resume token key from database: %w", err)
}
options.CoordinatorResumeTokenProvider = tailnet.NewResumeTokenKeyProvider(resumeTokenKey, quartz.NewReal(), tailnet.DefaultResumeTokenExpiry)

return nil
}, nil)
if err != nil {
return err
return xerrors.Errorf("set deployment id: %w", err)
}

fetcher := &cryptokeys.DBFetcher{
DB: options.Database,
}

resumeKeycache, err := cryptokeys.NewSigningCache(ctx,
logger,
fetcher,
codersdk.CryptoKeyFeatureTailnetResume,
)
if err != nil {
logger.Critical(ctx, "failed to properly instantiate tailnet resume signing cache", slog.Error(err))
}

options.CoordinatorResumeTokenProvider = tailnet.NewResumeTokenKeyProvider(
resumeKeycache,
quartz.NewReal(),
tailnet.DefaultResumeTokenExpiry,
)

options.RuntimeConfig = runtimeconfig.NewManager()

// This should be output before the logs start streaming.
Expand Down
18 changes: 13 additions & 5 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

22 changes: 17 additions & 5 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

72 changes: 61 additions & 11 deletionscoderd/coderd.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,7 @@ import (
"github.com/coder/quartz"
"github.com/coder/serpent"

"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/entitlements"
"github.com/coder/coder/v2/coderd/idpsync"
"github.com/coder/coder/v2/coderd/runtimeconfig"
Expand DownExpand Up@@ -185,9 +186,6 @@ type Options struct {
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
// AppSecurityKey is the crypto key used to sign and encrypt tokens related to
// workspace applications. It consists of both a signing and encryption key.
AppSecurityKey workspaceapps.SecurityKey
// CoordinatorResumeTokenProvider is used to provide and validate resume
// tokens issued by and passed to the coordinator DRPC API.
CoordinatorResumeTokenProvider tailnet.ResumeTokenProvider
Expand DownExpand Up@@ -251,6 +249,12 @@ type Options struct {

// OneTimePasscodeValidityPeriod specifies how long a one time passcode should be valid for.
OneTimePasscodeValidityPeriod time.Duration

// Keycaches
AppSigningKeyCache cryptokeys.SigningKeycache
AppEncryptionKeyCache cryptokeys.EncryptionKeycache
OIDCConvertKeyCache cryptokeys.SigningKeycache
Clock quartz.Clock
}

// @title Coder API
Expand DownExpand Up@@ -352,6 +356,9 @@ func New(options *Options) *API {
if options.PrometheusRegistry == nil {
options.PrometheusRegistry = prometheus.NewRegistry()
}
if options.Clock == nil {
options.Clock = quartz.NewReal()
}
if options.DERPServer == nil && options.DeploymentValues.DERP.Server.Enable {
options.DERPServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger.Named("derp")))
}
Expand DownExpand Up@@ -444,6 +451,49 @@ func New(options *Options) *API {
if err != nil {
panic(xerrors.Errorf("get deployment ID: %w", err))
}

fetcher := &cryptokeys.DBFetcher{
DB: options.Database,
}

if options.OIDCConvertKeyCache == nil {
options.OIDCConvertKeyCache, err = cryptokeys.NewSigningCache(ctx,
options.Logger.Named("oidc_convert_keycache"),
fetcher,
codersdk.CryptoKeyFeatureOIDCConvert,
)
if err != nil {
options.Logger.Critical(ctx, "failed to properly instantiate oidc convert signing cache", slog.Error(err))
}
}

if options.AppSigningKeyCache == nil {
options.AppSigningKeyCache, err = cryptokeys.NewSigningCache(ctx,
options.Logger.Named("app_signing_keycache"),
fetcher,
codersdk.CryptoKeyFeatureWorkspaceAppsToken,
)
if err != nil {
options.Logger.Critical(ctx, "failed to properly instantiate app signing key cache", slog.Error(err))
}
}

if options.AppEncryptionKeyCache == nil {
options.AppEncryptionKeyCache, err = cryptokeys.NewEncryptionCache(ctx,
options.Logger,
fetcher,
codersdk.CryptoKeyFeatureWorkspaceAppsAPIKey,
)
if err != nil {
options.Logger.Critical(ctx, "failed to properly instantiate app encryption key cache", slog.Error(err))
}
}

// Start a background process that rotates keys. We intentionally start this after the caches
// are created to force initial requests for a key to populate the caches. This helps catch
// bugs that may only occur when a key isn't precached in tests and the latency cost is minimal.
cryptokeys.StartRotator(ctx, options.Logger, options.Database)

api := &API{
ctx: ctx,
cancel: cancel,
Expand All@@ -464,7 +514,7 @@ func New(options *Options) *API {
options.DeploymentValues,
oauthConfigs,
options.AgentInactiveDisconnectTimeout,
options.AppSecurityKey,
options.AppSigningKeyCache,
),
metricsCache: metricsCache,
Auditor: atomic.Pointer[audit.Auditor]{},
Expand DownExpand Up@@ -606,7 +656,7 @@ func New(options *Options) *API {
ResumeTokenProvider: api.Options.CoordinatorResumeTokenProvider,
})
if err != nil {
api.Logger.Fatal(api.ctx, "failed to initialize tailnet client service", slog.Error(err))
api.Logger.Fatal(context.Background(), "failed to initialize tailnet client service", slog.Error(err))
}

api.statsReporter = workspacestats.NewReporter(workspacestats.ReporterOptions{
Expand All@@ -628,9 +678,6 @@ func New(options *Options) *API {
options.WorkspaceAppsStatsCollectorOptions.Reporter = api.statsReporter
}

if options.AppSecurityKey.IsZero() {
api.Logger.Fatal(api.ctx, "app security key cannot be zero")
}
api.workspaceAppServer = &workspaceapps.Server{
Logger: workspaceAppsLogger,

Expand All@@ -642,11 +689,11 @@ func New(options *Options) *API {

SignedTokenProvider: api.WorkspaceAppsProvider,
AgentProvider: api.agentProvider,
AppSecurityKey: options.AppSecurityKey,
StatsCollector: workspaceapps.NewStatsCollector(options.WorkspaceAppsStatsCollectorOptions),

DisablePathApps: options.DeploymentValues.DisablePathApps.Value(),
SecureAuthCookie: options.DeploymentValues.SecureAuthCookie.Value(),
DisablePathApps: options.DeploymentValues.DisablePathApps.Value(),
SecureAuthCookie: options.DeploymentValues.SecureAuthCookie.Value(),
APIKeyEncryptionKeycache: options.AppEncryptionKeyCache,
}

apiKeyMiddleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
Expand DownExpand Up@@ -1434,6 +1481,9 @@ func (api *API) Close() error {
_ = api.agentProvider.Close()
_ = api.statsReporter.Close()
_ = api.NetworkTelemetryBatcher.Close()
_ = api.OIDCConvertKeyCache.Close()
_ = api.AppSigningKeyCache.Close()
_ = api.AppEncryptionKeyCache.Close()
return nil
}

Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp