2020-12-14 15:36:45 +00:00
|
|
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2020-12-15 01:38:01 +00:00
|
|
|
package generator
|
2020-12-14 15:36:45 +00:00
|
|
|
|
|
|
|
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"
|
2020-12-15 14:13:01 +00:00
|
|
|
"go.pinniped.dev/internal/plog"
|
2020-12-14 15:36:45 +00:00
|
|
|
)
|
|
|
|
|
2020-12-15 01:38:01 +00:00
|
|
|
// 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.
|
|
|
|
//
|
2020-12-15 02:36:56 +00:00
|
|
|
// A SecretHelper has a NamePrefix() that can be used to identify it from other SecretHelper instances.
|
2020-12-15 01:38:01 +00:00
|
|
|
type SecretHelper interface {
|
2020-12-15 02:36:56 +00:00
|
|
|
NamePrefix() string
|
2020-12-16 22:27:09 +00:00
|
|
|
Generate(*configv1alpha1.FederationDomain) (*corev1.Secret, error)
|
|
|
|
IsValid(*configv1alpha1.FederationDomain, *corev1.Secret) bool
|
|
|
|
ObserveActiveSecretAndUpdateParentFederationDomain(*configv1alpha1.FederationDomain, *corev1.Secret) *configv1alpha1.FederationDomain
|
2020-12-15 01:38:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-14 15:36:45 +00:00
|
|
|
const (
|
2020-12-15 14:13:01 +00:00
|
|
|
// 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"
|
2020-12-14 15:36:45 +00:00
|
|
|
|
2020-12-15 00:23:17 +00:00
|
|
|
// symmetricKeySize is the default length, in bytes, of generated keys. It is set to 32 since this
|
2020-12-14 15:36:45 +00:00
|
|
|
// 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).
|
2020-12-15 00:23:17 +00:00
|
|
|
symmetricKeySize = 32
|
2020-12-14 15:36:45 +00:00
|
|
|
)
|
|
|
|
|
2020-12-15 14:13:01 +00:00
|
|
|
// SecretUsage describes how a cryptographic secret is going to be used. It is currently used to
|
2020-12-16 22:27:09 +00:00
|
|
|
// indicate to a SecretHelper which status field to set on the parent FederationDomain for a Secret.
|
2020-12-15 14:13:01 +00:00
|
|
|
type SecretUsage int
|
|
|
|
|
|
|
|
const (
|
|
|
|
SecretUsageTokenSigningKey SecretUsage = iota
|
|
|
|
SecretUsageStateSigningKey
|
|
|
|
SecretUsageStateEncryptionKey
|
|
|
|
)
|
|
|
|
|
2020-12-14 15:36:45 +00:00
|
|
|
// New returns a SecretHelper that has been parameterized with common symmetric secret generation
|
|
|
|
// knobs.
|
2020-12-15 01:38:01 +00:00
|
|
|
func NewSymmetricSecretHelper(
|
2020-12-14 15:36:45 +00:00
|
|
|
namePrefix string,
|
|
|
|
labels map[string]string,
|
|
|
|
rand io.Reader,
|
2020-12-15 14:13:01 +00:00
|
|
|
secretUsage SecretUsage,
|
2020-12-15 03:35:45 +00:00
|
|
|
updateCacheFunc func(cacheKey string, cacheValue []byte),
|
2020-12-15 01:38:01 +00:00
|
|
|
) SecretHelper {
|
|
|
|
return &symmetricSecretHelper{
|
2020-12-15 03:35:45 +00:00
|
|
|
namePrefix: namePrefix,
|
|
|
|
labels: labels,
|
|
|
|
rand: rand,
|
2020-12-15 14:13:01 +00:00
|
|
|
secretUsage: secretUsage,
|
2020-12-15 03:35:45 +00:00
|
|
|
updateCacheFunc: updateCacheFunc,
|
2020-12-14 15:36:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-15 01:38:01 +00:00
|
|
|
type symmetricSecretHelper struct {
|
2020-12-15 03:35:45 +00:00
|
|
|
namePrefix string
|
|
|
|
labels map[string]string
|
|
|
|
rand io.Reader
|
2020-12-15 14:13:01 +00:00
|
|
|
secretUsage SecretUsage
|
2020-12-15 03:35:45 +00:00
|
|
|
updateCacheFunc func(cacheKey string, cacheValue []byte)
|
2020-12-15 01:38:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 02:36:56 +00:00
|
|
|
func (s *symmetricSecretHelper) NamePrefix() string { return s.namePrefix }
|
2020-12-14 15:36:45 +00:00
|
|
|
|
|
|
|
// Generate implements SecretHelper.Generate().
|
2020-12-16 22:27:09 +00:00
|
|
|
func (s *symmetricSecretHelper) Generate(parent *configv1alpha1.FederationDomain) (*corev1.Secret, error) {
|
2020-12-15 00:23:17 +00:00
|
|
|
key := make([]byte, symmetricKeySize)
|
2020-12-14 15:36:45 +00:00
|
|
|
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,
|
2020-12-16 22:27:09 +00:00
|
|
|
Kind: "FederationDomain",
|
2020-12-14 15:36:45 +00:00
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
2020-12-15 14:13:01 +00:00
|
|
|
Type: symmetricSecretType,
|
2020-12-14 15:36:45 +00:00
|
|
|
Data: map[string][]byte{
|
2020-12-15 14:13:01 +00:00
|
|
|
symmetricSecretDataKey: key,
|
2020-12-14 15:36:45 +00:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsValid implements SecretHelper.IsValid().
|
2020-12-16 22:27:09 +00:00
|
|
|
func (s *symmetricSecretHelper) IsValid(parent *configv1alpha1.FederationDomain, secret *corev1.Secret) bool {
|
2020-12-15 03:35:45 +00:00
|
|
|
if !metav1.IsControlledBy(secret, parent) {
|
2020-12-14 15:36:45 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:13:01 +00:00
|
|
|
if secret.Type != symmetricSecretType {
|
2020-12-14 15:36:45 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:13:01 +00:00
|
|
|
key, ok := secret.Data[symmetricSecretDataKey]
|
2020-12-14 15:36:45 +00:00
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2020-12-15 00:23:17 +00:00
|
|
|
if len(key) != symmetricKeySize {
|
2020-12-14 15:36:45 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-12-16 22:27:09 +00:00
|
|
|
// ObserveActiveSecretAndUpdateParentFederationDomain implements SecretHelper.ObserveActiveSecretAndUpdateParentFederationDomain().
|
|
|
|
func (s *symmetricSecretHelper) ObserveActiveSecretAndUpdateParentFederationDomain(
|
|
|
|
op *configv1alpha1.FederationDomain,
|
2020-12-15 14:13:01 +00:00
|
|
|
secret *corev1.Secret,
|
2020-12-16 22:27:09 +00:00
|
|
|
) *configv1alpha1.FederationDomain {
|
2020-12-15 03:35:45 +00:00
|
|
|
var cacheKey string
|
|
|
|
if op != nil {
|
|
|
|
cacheKey = op.Spec.Issuer
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:13:01 +00:00
|
|
|
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
|
2020-12-14 15:36:45 +00:00
|
|
|
}
|