Identity: check NextSigningKey existence during key rotation (#13298)

* oidc: fix key rotation panic

* refactor and update unit tests

* add changelog
This commit is contained in:
John-Michael Faircloth 2021-11-29 15:10:58 -06:00 committed by GitHub
parent 4fd241c3d3
commit 32152e10fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 24 deletions

3
changelog/13298.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
identity: Fixes a panic in the OIDC key rotation due to a missing nil check.
```

View File

@ -561,18 +561,10 @@ func (i *IdentityStore) pathOIDCCreateUpdateKey(ctx context.Context, req *logica
}
i.Logger().Debug("generated OIDC public key to sign JWTs", "key_id", signingKey.Public().KeyID)
nextSigningKey, err := generateKeys(key.Algorithm)
err = key.generateAndSetNextKey(ctx, i.Logger(), req.Storage)
if err != nil {
return nil, err
}
key.NextSigningKey = nextSigningKey
key.KeyRing = append(key.KeyRing, &expireableKey{KeyID: nextSigningKey.Public().KeyID})
if err := saveOIDCPublicKey(ctx, req.Storage, nextSigningKey.Public()); err != nil {
return nil, err
}
i.Logger().Debug("generated OIDC public key for future use", "key_id", nextSigningKey.Public().KeyID)
}
if err := i.oidcCache.Flush(ns); err != nil {
@ -1021,6 +1013,24 @@ func mergeJSONTemplates(logger hclog.Logger, output map[string]interface{}, temp
return nil
}
// generateAndSetNextKey will generate new signing and public key pairs and set
// them as the NextSigningKey.
func (k *namedKey) generateAndSetNextKey(ctx context.Context, logger hclog.Logger, s logical.Storage) error {
signingKey, err := generateKeys(k.Algorithm)
if err != nil {
return err
}
k.NextSigningKey = signingKey
k.KeyRing = append(k.KeyRing, &expireableKey{KeyID: signingKey.Public().KeyID})
if err := saveOIDCPublicKey(ctx, s, signingKey.Public()); err != nil {
return err
}
logger.Debug("generated OIDC public key for future use", "key_id", signingKey.Public().KeyID)
return nil
}
func (k *namedKey) signPayload(payload []byte) (string, error) {
signingKey := jose.SigningKey{Key: k.SigningKey, Algorithm: jose.SignatureAlgorithm(k.Algorithm)}
signer, err := jose.NewSigner(signingKey, &jose.SignerOptions{})
@ -1477,16 +1487,6 @@ func (k *namedKey) rotate(ctx context.Context, logger hclog.Logger, s logical.St
verificationTTL = overrideVerificationTTL
}
// generate new key
nextSigningKey, err := generateKeys(k.Algorithm)
if err != nil {
return err
}
if err := saveOIDCPublicKey(ctx, s, nextSigningKey.Public()); err != nil {
return err
}
logger.Debug("generated OIDC public key for future use", "key_id", nextSigningKey.Public().KeyID)
now := time.Now()
// set the previous public key's expiry time
for _, key := range k.KeyRing {
@ -1496,11 +1496,24 @@ func (k *namedKey) rotate(ctx context.Context, logger hclog.Logger, s logical.St
}
}
k.KeyRing = append(k.KeyRing, &expireableKey{KeyID: nextSigningKey.KeyID})
if k.NextSigningKey == nil {
// keys will not have a NextSigningKey if they were generated before
// vault 1.9
err := k.generateAndSetNextKey(ctx, logger, s)
if err != nil {
return err
}
}
// do the rotation
k.SigningKey = k.NextSigningKey
k.NextSigningKey = nextSigningKey
k.NextRotation = now.Add(k.RotationPeriod)
// now that we have rotated, generate a new NextSigningKey
err := k.generateAndSetNextKey(ctx, logger, s)
if err != nil {
return err
}
// store named key (it was modified when rotate was called on it)
entry, err := logical.StorageEntryJSON(namedKeyConfigPath+k.name, k)
if err != nil {

View File

@ -921,6 +921,7 @@ func TestOIDC_PeriodicFunc(t *testing.T) {
}
}{
{
// don't set NextSigningKey to ensure its non-existence can be handled
&namedKey{
name: "test-key",
Algorithm: "RS256",
@ -928,7 +929,6 @@ func TestOIDC_PeriodicFunc(t *testing.T) {
RotationPeriod: 1 * cyclePeriod,
KeyRing: nil,
SigningKey: jwk,
NextSigningKey: jwk,
NextRotation: time.Now(),
},
[]struct {
@ -936,8 +936,8 @@ func TestOIDC_PeriodicFunc(t *testing.T) {
numKeys int
numPublicKeys int
}{
{1, 1, 1},
{2, 2, 2},
{1, 2, 2},
{2, 3, 3},
{3, 3, 3},
{4, 3, 3},
{5, 3, 3},