6565265bee
Signed-off-by: Matt Moyer <moyerm@vmware.com>
195 lines
6.5 KiB
Go
195 lines
6.5 KiB
Go
// Copyright 2020-2021 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/latest/apis/supervisor/config/v1alpha1"
|
|
)
|
|
|
|
// 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.FederationDomain) (*corev1.Secret, error)
|
|
IsValid(*configv1alpha1.FederationDomain, *corev1.Secret) bool
|
|
ObserveActiveSecretAndUpdateParentFederationDomain(*configv1alpha1.FederationDomain, *corev1.Secret) *configv1alpha1.FederationDomain
|
|
Handles(metav1.Object) bool
|
|
}
|
|
|
|
const (
|
|
// SupervisorCSRFSigningKeySecretType for the Secret storing the CSRF signing key.
|
|
SupervisorCSRFSigningKeySecretType corev1.SecretType = "secrets.pinniped.dev/supervisor-csrf-signing-key"
|
|
|
|
// FederationDomainTokenSigningKeyType for the Secret storing the FederationDomain token signing key.
|
|
FederationDomainTokenSigningKeyType corev1.SecretType = "secrets.pinniped.dev/federation-domain-token-signing-key"
|
|
|
|
// FederationDomainStateSigningKeyType for the Secret storing the FederationDomain state signing key.
|
|
FederationDomainStateSigningKeyType corev1.SecretType = "secrets.pinniped.dev/federation-domain-state-signing-key"
|
|
|
|
// FederationDomainStateEncryptionKeyType for the Secret storing the FederationDomain state encryption key.
|
|
FederationDomainStateEncryptionKeyType corev1.SecretType = "secrets.pinniped.dev/federation-domain-state-encryption-key"
|
|
|
|
federationDomainKind = "FederationDomain"
|
|
|
|
// 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 FederationDomain 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.FederationDomain) (*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: federationDomainKind,
|
|
}),
|
|
},
|
|
},
|
|
Type: s.secretType(),
|
|
Data: map[string][]byte{
|
|
symmetricSecretDataKey: key,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// IsValid implements SecretHelper.IsValid().
|
|
func (s *symmetricSecretHelper) IsValid(parent *configv1alpha1.FederationDomain, secret *corev1.Secret) bool {
|
|
if !metav1.IsControlledBy(secret, parent) {
|
|
return false
|
|
}
|
|
|
|
if secret.Type != s.secretType() {
|
|
return false
|
|
}
|
|
|
|
key, ok := secret.Data[symmetricSecretDataKey]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if len(key) != symmetricKeySize {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ObserveActiveSecretAndUpdateParentFederationDomain implements SecretHelper.ObserveActiveSecretAndUpdateParentFederationDomain().
|
|
func (s *symmetricSecretHelper) ObserveActiveSecretAndUpdateParentFederationDomain(
|
|
federationDomain *configv1alpha1.FederationDomain,
|
|
secret *corev1.Secret,
|
|
) *configv1alpha1.FederationDomain {
|
|
s.updateCacheFunc(federationDomain.Spec.Issuer, secret.Data[symmetricSecretDataKey])
|
|
|
|
switch s.secretUsage {
|
|
case SecretUsageTokenSigningKey:
|
|
federationDomain.Status.Secrets.TokenSigningKey.Name = secret.Name
|
|
case SecretUsageStateSigningKey:
|
|
federationDomain.Status.Secrets.StateSigningKey.Name = secret.Name
|
|
case SecretUsageStateEncryptionKey:
|
|
federationDomain.Status.Secrets.StateEncryptionKey.Name = secret.Name
|
|
default:
|
|
panic(fmt.Sprintf("unknown secret usage enum value: %d", s.secretUsage))
|
|
}
|
|
|
|
return federationDomain
|
|
}
|
|
|
|
func (s *symmetricSecretHelper) secretType() corev1.SecretType {
|
|
switch s.secretUsage {
|
|
case SecretUsageTokenSigningKey:
|
|
return FederationDomainTokenSigningKeyType
|
|
case SecretUsageStateSigningKey:
|
|
return FederationDomainStateSigningKeyType
|
|
case SecretUsageStateEncryptionKey:
|
|
return FederationDomainStateEncryptionKeyType
|
|
default:
|
|
panic(fmt.Sprintf("unknown secret usage enum value: %d", s.secretUsage))
|
|
}
|
|
}
|
|
|
|
func (s *symmetricSecretHelper) Handles(obj metav1.Object) bool {
|
|
return IsFederationDomainSecretOfType(obj, s.secretType())
|
|
}
|
|
|
|
func IsFederationDomainSecretOfType(obj metav1.Object, secretType corev1.SecretType) bool {
|
|
secret, ok := obj.(*corev1.Secret)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if secret.Type != secretType {
|
|
return false
|
|
}
|
|
return isFederationDomainControllee(secret)
|
|
}
|
|
|
|
// isFederationDomainControllee returns whether the provided obj is controlled by an FederationDomain.
|
|
func isFederationDomainControllee(obj metav1.Object) bool {
|
|
controller := metav1.GetControllerOf(obj)
|
|
return controller != nil &&
|
|
controller.APIVersion == configv1alpha1.SchemeGroupVersion.String() &&
|
|
controller.Kind == federationDomainKind
|
|
}
|