// 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" ) // 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 Notify(*configv1alpha1.OIDCProvider, *corev1.Secret) } 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 ) // 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, updateCacheFunc func(cacheKey string, cacheValue []byte), ) SecretHelper { return &symmetricSecretHelper{ namePrefix: namePrefix, labels: labels, rand: rand, updateCacheFunc: updateCacheFunc, } } type symmetricSecretHelper struct { namePrefix string labels map[string]string rand io.Reader 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 } // Notify implements SecretHelper.Notify(). func (s *symmetricSecretHelper) Notify(op *configv1alpha1.OIDCProvider, secret *corev1.Secret) { var cacheKey string if op != nil { cacheKey = op.Spec.Issuer } s.updateCacheFunc(cacheKey, secret.Data[SymmetricSecretDataKey]) }