82ae98d9d0
We believe this API is more forwards compatible with future secrets management use cases. The implementation is a cry for help, but I was trying to follow the previously established pattern of encapsulating the secret generation functionality to a single group of packages. This commit makes a breaking change to the current OIDCProvider API, but that OIDCProvider API was added after the latest release, so it is technically still in development until we release, and therefore we can continue to thrash on it. I also took this opportunity to make some things private that didn't need to be public. Signed-off-by: Andrew Keesler <akeesler@vmware.com>
152 lines
4.6 KiB
Go
152 lines
4.6 KiB
Go
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
configv1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/config/v1alpha1"
|
|
"go.pinniped.dev/internal/plog"
|
|
)
|
|
|
|
// SecretHelper describes an object that can Generate() a Secret and determine whether a Secret
|
|
// IsValid(). It can also be Notify()'d about a Secret being persisted.
|
|
//
|
|
// A SecretHelper has a NamePrefix() that can be used to identify it from other SecretHelper instances.
|
|
type SecretHelper interface {
|
|
NamePrefix() string
|
|
Generate(*configv1alpha1.OIDCProvider) (*corev1.Secret, error)
|
|
IsValid(*configv1alpha1.OIDCProvider, *corev1.Secret) bool
|
|
ObserveActiveSecretAndUpdateParentOIDCProvider(*configv1alpha1.OIDCProvider, *corev1.Secret) *configv1alpha1.OIDCProvider
|
|
}
|
|
|
|
const (
|
|
// symmetricSecretType is corev1.Secret.Type of all corev1.Secret's generated by this helper.
|
|
symmetricSecretType = "secrets.pinniped.dev/symmetric"
|
|
// symmetricSecretDataKey is the corev1.Secret.Data key for the symmetric key value generated by this helper.
|
|
symmetricSecretDataKey = "key"
|
|
|
|
// symmetricKeySize is the default length, in bytes, of generated keys. It is set to 32 since this
|
|
// seems like reasonable entropy for our keys, and a 32-byte key will allow for AES-256
|
|
// to be used in our codecs (see dynamiccodec.Codec).
|
|
symmetricKeySize = 32
|
|
)
|
|
|
|
// SecretUsage describes how a cryptographic secret is going to be used. It is currently used to
|
|
// indicate to a SecretHelper which status field to set on the parent OIDCProvider for a Secret.
|
|
type SecretUsage int
|
|
|
|
const (
|
|
SecretUsageTokenSigningKey SecretUsage = iota
|
|
SecretUsageStateSigningKey
|
|
SecretUsageStateEncryptionKey
|
|
)
|
|
|
|
// New returns a SecretHelper that has been parameterized with common symmetric secret generation
|
|
// knobs.
|
|
func NewSymmetricSecretHelper(
|
|
namePrefix string,
|
|
labels map[string]string,
|
|
rand io.Reader,
|
|
secretUsage SecretUsage,
|
|
updateCacheFunc func(cacheKey string, cacheValue []byte),
|
|
) SecretHelper {
|
|
return &symmetricSecretHelper{
|
|
namePrefix: namePrefix,
|
|
labels: labels,
|
|
rand: rand,
|
|
secretUsage: secretUsage,
|
|
updateCacheFunc: updateCacheFunc,
|
|
}
|
|
}
|
|
|
|
type symmetricSecretHelper struct {
|
|
namePrefix string
|
|
labels map[string]string
|
|
rand io.Reader
|
|
secretUsage SecretUsage
|
|
updateCacheFunc func(cacheKey string, cacheValue []byte)
|
|
}
|
|
|
|
func (s *symmetricSecretHelper) NamePrefix() string { return s.namePrefix }
|
|
|
|
// Generate implements SecretHelper.Generate().
|
|
func (s *symmetricSecretHelper) Generate(parent *configv1alpha1.OIDCProvider) (*corev1.Secret, error) {
|
|
key := make([]byte, symmetricKeySize)
|
|
if _, err := s.rand.Read(key); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: fmt.Sprintf("%s%s", s.namePrefix, parent.UID),
|
|
Namespace: parent.Namespace,
|
|
Labels: s.labels,
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
*metav1.NewControllerRef(parent, schema.GroupVersionKind{
|
|
Group: configv1alpha1.SchemeGroupVersion.Group,
|
|
Version: configv1alpha1.SchemeGroupVersion.Version,
|
|
Kind: "OIDCProvider",
|
|
}),
|
|
},
|
|
},
|
|
Type: symmetricSecretType,
|
|
Data: map[string][]byte{
|
|
symmetricSecretDataKey: key,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// IsValid implements SecretHelper.IsValid().
|
|
func (s *symmetricSecretHelper) IsValid(parent *configv1alpha1.OIDCProvider, secret *corev1.Secret) bool {
|
|
if !metav1.IsControlledBy(secret, parent) {
|
|
return false
|
|
}
|
|
|
|
if secret.Type != symmetricSecretType {
|
|
return false
|
|
}
|
|
|
|
key, ok := secret.Data[symmetricSecretDataKey]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if len(key) != symmetricKeySize {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ObserveActiveSecretAndUpdateParentOIDCProvider implements SecretHelper.ObserveActiveSecretAndUpdateParentOIDCProvider().
|
|
func (s *symmetricSecretHelper) ObserveActiveSecretAndUpdateParentOIDCProvider(
|
|
op *configv1alpha1.OIDCProvider,
|
|
secret *corev1.Secret,
|
|
) *configv1alpha1.OIDCProvider {
|
|
var cacheKey string
|
|
if op != nil {
|
|
cacheKey = op.Spec.Issuer
|
|
}
|
|
|
|
s.updateCacheFunc(cacheKey, secret.Data[symmetricSecretDataKey])
|
|
|
|
switch s.secretUsage {
|
|
case SecretUsageTokenSigningKey:
|
|
op.Status.Secrets.TokenSigningKey.Name = secret.Name
|
|
case SecretUsageStateSigningKey:
|
|
op.Status.Secrets.StateSigningKey.Name = secret.Name
|
|
case SecretUsageStateEncryptionKey:
|
|
op.Status.Secrets.StateEncryptionKey.Name = secret.Name
|
|
default:
|
|
plog.Warning("unknown secret usage enum value: %d", s.secretUsage)
|
|
}
|
|
|
|
return op
|
|
}
|