Merge pull request #301 from vmware-tanzu/typed-secrets
Put a Type on all of the Secrets that we create in the supervisor
This commit is contained in:
commit
6c210b67d4
@ -24,6 +24,10 @@ import (
|
|||||||
"go.pinniped.dev/internal/plog"
|
"go.pinniped.dev/internal/plog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
federationDomainKind = "FederationDomain"
|
||||||
|
)
|
||||||
|
|
||||||
type federationDomainSecretsController struct {
|
type federationDomainSecretsController struct {
|
||||||
secretHelper SecretHelper
|
secretHelper SecretHelper
|
||||||
secretRefFunc func(domain *configv1alpha1.FederationDomain) *corev1.LocalObjectReference
|
secretRefFunc func(domain *configv1alpha1.FederationDomain) *corev1.LocalObjectReference
|
||||||
@ -236,3 +240,11 @@ func (c *federationDomainSecretsController) updateFederationDomain(
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package generator
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
federationDomainKind = "FederationDomain"
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateSymmetricKey() ([]byte, error) {
|
|
||||||
b := make([]byte, symmetricKeySize)
|
|
||||||
if _, err := rand.Read(b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValid(secret *corev1.Secret, labels map[string]string) bool {
|
|
||||||
if secret.Type != symmetricSecretType {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
data, ok := secret.Data[symmetricSecretDataKey]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(data) != symmetricKeySize {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range labels {
|
|
||||||
if secret.Labels[key] != value {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func secretDataFunc() (map[string][]byte, error) {
|
|
||||||
symmetricKey, err := generateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[string][]byte{
|
|
||||||
symmetricSecretDataKey: symmetricKey,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateSecret(namespace, name string, labels map[string]string, secretDataFunc func() (map[string][]byte, error), owner metav1.Object) (*corev1.Secret, error) {
|
|
||||||
secretData, err := secretDataFunc()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
deploymentGVK := schema.GroupVersionKind{
|
|
||||||
Group: appsv1.SchemeGroupVersion.Group,
|
|
||||||
Version: appsv1.SchemeGroupVersion.Version,
|
|
||||||
Kind: "Deployment",
|
|
||||||
}
|
|
||||||
|
|
||||||
blockOwnerDeletion := true
|
|
||||||
isController := false
|
|
||||||
|
|
||||||
return &corev1.Secret{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: name,
|
|
||||||
Namespace: namespace,
|
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
|
||||||
{
|
|
||||||
APIVersion: deploymentGVK.GroupVersion().String(),
|
|
||||||
Kind: deploymentGVK.Kind,
|
|
||||||
Name: owner.GetName(),
|
|
||||||
UID: owner.GetUID(),
|
|
||||||
BlockOwnerDeletion: &blockOwnerDeletion,
|
|
||||||
Controller: &isController,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Labels: labels,
|
|
||||||
},
|
|
||||||
Type: symmetricSecretType,
|
|
||||||
Data: secretData,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
configv1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/config/v1alpha1"
|
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
|
// SecretHelper describes an object that can Generate() a Secret and determine whether a Secret
|
||||||
@ -27,8 +26,18 @@ type SecretHelper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// symmetricSecretType is corev1.Secret.Type of all corev1.Secret's generated by this helper.
|
// SupervisorCSRFSigningKeySecretType for the Secret storing the CSRF signing key.
|
||||||
symmetricSecretType = "secrets.pinniped.dev/symmetric"
|
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"
|
||||||
|
|
||||||
// symmetricSecretDataKey is the corev1.Secret.Data key for the symmetric key value generated by this helper.
|
// symmetricSecretDataKey is the corev1.Secret.Data key for the symmetric key value generated by this helper.
|
||||||
symmetricSecretDataKey = "key"
|
symmetricSecretDataKey = "key"
|
||||||
|
|
||||||
@ -96,7 +105,7 @@ func (s *symmetricSecretHelper) Generate(parent *configv1alpha1.FederationDomain
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: symmetricSecretType,
|
Type: s.secretType(),
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
symmetricSecretDataKey: key,
|
symmetricSecretDataKey: key,
|
||||||
},
|
},
|
||||||
@ -109,7 +118,7 @@ func (s *symmetricSecretHelper) IsValid(parent *configv1alpha1.FederationDomain,
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if secret.Type != symmetricSecretType {
|
if secret.Type != s.secretType() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,12 +138,7 @@ func (s *symmetricSecretHelper) ObserveActiveSecretAndUpdateParentFederationDoma
|
|||||||
federationDomain *configv1alpha1.FederationDomain,
|
federationDomain *configv1alpha1.FederationDomain,
|
||||||
secret *corev1.Secret,
|
secret *corev1.Secret,
|
||||||
) *configv1alpha1.FederationDomain {
|
) *configv1alpha1.FederationDomain {
|
||||||
var cacheKey string
|
s.updateCacheFunc(federationDomain.Spec.Issuer, secret.Data[symmetricSecretDataKey])
|
||||||
if federationDomain != nil {
|
|
||||||
cacheKey = federationDomain.Spec.Issuer
|
|
||||||
}
|
|
||||||
|
|
||||||
s.updateCacheFunc(cacheKey, secret.Data[symmetricSecretDataKey])
|
|
||||||
|
|
||||||
switch s.secretUsage {
|
switch s.secretUsage {
|
||||||
case SecretUsageTokenSigningKey:
|
case SecretUsageTokenSigningKey:
|
||||||
@ -144,8 +148,21 @@ func (s *symmetricSecretHelper) ObserveActiveSecretAndUpdateParentFederationDoma
|
|||||||
case SecretUsageStateEncryptionKey:
|
case SecretUsageStateEncryptionKey:
|
||||||
federationDomain.Status.Secrets.StateEncryptionKey.Name = secret.Name
|
federationDomain.Status.Secrets.StateEncryptionKey.Name = secret.Name
|
||||||
default:
|
default:
|
||||||
plog.Warning("unknown secret usage enum value: %d", s.secretUsage)
|
panic(fmt.Sprintf("unknown secret usage enum value: %d", s.secretUsage))
|
||||||
}
|
}
|
||||||
|
|
||||||
return federationDomain
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,25 +23,29 @@ func TestSymmetricSecretHelper(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
secretUsage SecretUsage
|
secretUsage SecretUsage
|
||||||
|
wantSecretType corev1.SecretType
|
||||||
wantSetFederationDomainField func(*configv1alpha1.FederationDomain) string
|
wantSetFederationDomainField func(*configv1alpha1.FederationDomain) string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "token signing key",
|
name: "token signing key",
|
||||||
secretUsage: SecretUsageTokenSigningKey,
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
|
wantSecretType: "secrets.pinniped.dev/federation-domain-token-signing-key",
|
||||||
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.TokenSigningKey.Name
|
return federationDomain.Status.Secrets.TokenSigningKey.Name
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "state signing key",
|
name: "state signing key",
|
||||||
secretUsage: SecretUsageStateSigningKey,
|
secretUsage: SecretUsageStateSigningKey,
|
||||||
|
wantSecretType: "secrets.pinniped.dev/federation-domain-state-signing-key",
|
||||||
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.StateSigningKey.Name
|
return federationDomain.Status.Secrets.StateSigningKey.Name
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "state encryption key",
|
name: "state encryption key",
|
||||||
secretUsage: SecretUsageStateEncryptionKey,
|
secretUsage: SecretUsageStateEncryptionKey,
|
||||||
|
wantSecretType: "secrets.pinniped.dev/federation-domain-state-encryption-key",
|
||||||
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
wantSetFederationDomainField: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.StateEncryptionKey.Name
|
return federationDomain.Status.Secrets.StateEncryptionKey.Name
|
||||||
},
|
},
|
||||||
@ -92,7 +96,7 @@ func TestSymmetricSecretHelper(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: "secrets.pinniped.dev/symmetric",
|
Type: test.wantSecretType,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"key": []byte(keyWith32Bytes),
|
"key": []byte(keyWith32Bytes),
|
||||||
},
|
},
|
||||||
@ -110,55 +114,69 @@ func TestSymmetricSecretHelper(t *testing.T) {
|
|||||||
|
|
||||||
func TestSymmetricSecretHelperIsValid(t *testing.T) {
|
func TestSymmetricSecretHelperIsValid(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
child func(*corev1.Secret)
|
secretUsage SecretUsage
|
||||||
parent func(*configv1alpha1.FederationDomain)
|
child func(*corev1.Secret)
|
||||||
want bool
|
parent func(*configv1alpha1.FederationDomain)
|
||||||
|
want bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "wrong type",
|
name: "wrong type",
|
||||||
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
child: func(s *corev1.Secret) {
|
child: func(s *corev1.Secret) {
|
||||||
s.Type = "wrong"
|
s.Type = "wrong"
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty type",
|
name: "empty type",
|
||||||
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
child: func(s *corev1.Secret) {
|
child: func(s *corev1.Secret) {
|
||||||
s.Type = ""
|
s.Type = ""
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "data key is too short",
|
name: "data key is too short",
|
||||||
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
child: func(s *corev1.Secret) {
|
child: func(s *corev1.Secret) {
|
||||||
|
s.Type = FederationDomainTokenSigningKeyType
|
||||||
s.Data["key"] = []byte("short")
|
s.Data["key"] = []byte("short")
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "data key does not exist",
|
name: "data key does not exist",
|
||||||
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
child: func(s *corev1.Secret) {
|
child: func(s *corev1.Secret) {
|
||||||
|
s.Type = FederationDomainTokenSigningKeyType
|
||||||
delete(s.Data, "key")
|
delete(s.Data, "key")
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "child not owned by parent",
|
name: "child not owned by parent",
|
||||||
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
|
child: func(s *corev1.Secret) {
|
||||||
|
s.Type = FederationDomainTokenSigningKeyType
|
||||||
|
},
|
||||||
parent: func(federationDomain *configv1alpha1.FederationDomain) {
|
parent: func(federationDomain *configv1alpha1.FederationDomain) {
|
||||||
federationDomain.UID = "wrong"
|
federationDomain.UID = "wrong"
|
||||||
},
|
},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy path",
|
name: "happy path",
|
||||||
want: true,
|
secretUsage: SecretUsageTokenSigningKey,
|
||||||
|
child: func(s *corev1.Secret) {
|
||||||
|
s.Type = FederationDomainTokenSigningKeyType
|
||||||
|
}, want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
h := NewSymmetricSecretHelper("none of these args matter", nil, nil, SecretUsageTokenSigningKey, nil)
|
h := NewSymmetricSecretHelper("none of these args matter", nil, nil, test.secretUsage, nil)
|
||||||
|
|
||||||
parent := &configv1alpha1.FederationDomain{
|
parent := &configv1alpha1.FederationDomain{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -179,7 +197,7 @@ func TestSymmetricSecretHelperIsValid(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: "secrets.pinniped.dev/symmetric",
|
Type: "invalid default",
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"key": []byte(keyWith32Bytes),
|
"key": []byte(keyWith32Bytes),
|
||||||
},
|
},
|
||||||
|
@ -6,12 +6,14 @@ package generator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/util/retry"
|
"k8s.io/client-go/util/retry"
|
||||||
@ -143,3 +145,80 @@ func (c *supervisorSecretsController) updateSecret(ctx context.Context, newSecre
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateSymmetricKey() ([]byte, error) {
|
||||||
|
b := make([]byte, symmetricKeySize)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValid(secret *corev1.Secret, labels map[string]string) bool {
|
||||||
|
if secret.Type != SupervisorCSRFSigningKeySecretType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := secret.Data[symmetricSecretDataKey]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(data) != symmetricKeySize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range labels {
|
||||||
|
if secret.Labels[key] != value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretDataFunc() (map[string][]byte, error) {
|
||||||
|
symmetricKey, err := generateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string][]byte{
|
||||||
|
symmetricSecretDataKey: symmetricKey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSecret(namespace, name string, labels map[string]string, secretDataFunc func() (map[string][]byte, error), owner metav1.Object) (*corev1.Secret, error) {
|
||||||
|
secretData, err := secretDataFunc()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deploymentGVK := schema.GroupVersionKind{
|
||||||
|
Group: appsv1.SchemeGroupVersion.Group,
|
||||||
|
Version: appsv1.SchemeGroupVersion.Version,
|
||||||
|
Kind: "Deployment",
|
||||||
|
}
|
||||||
|
|
||||||
|
blockOwnerDeletion := true
|
||||||
|
isController := false
|
||||||
|
|
||||||
|
return &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Namespace: namespace,
|
||||||
|
OwnerReferences: []metav1.OwnerReference{
|
||||||
|
{
|
||||||
|
APIVersion: deploymentGVK.GroupVersion().String(),
|
||||||
|
Kind: deploymentGVK.Kind,
|
||||||
|
Name: owner.GetName(),
|
||||||
|
UID: owner.GetUID(),
|
||||||
|
BlockOwnerDeletion: &blockOwnerDeletion,
|
||||||
|
Controller: &isController,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Type: SupervisorCSRFSigningKeySecretType,
|
||||||
|
Data: secretData,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -258,7 +258,7 @@ func TestSupervisorSecretsControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Type: "secrets.pinniped.dev/symmetric",
|
Type: "secrets.pinniped.dev/supervisor-csrf-signing-key",
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"key": generatedSymmetricKey,
|
"key": generatedSymmetricKey,
|
||||||
},
|
},
|
||||||
@ -280,7 +280,7 @@ func TestSupervisorSecretsControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
Type: "secrets.pinniped.dev/symmetric",
|
Type: "secrets.pinniped.dev/supervisor-csrf-signing-key",
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"key": otherGeneratedSymmetricKey,
|
"key": otherGeneratedSymmetricKey,
|
||||||
},
|
},
|
||||||
|
@ -40,6 +40,8 @@ const (
|
|||||||
//
|
//
|
||||||
// Note! The value for this key will contain only public key material!
|
// Note! The value for this key will contain only public key material!
|
||||||
jwksKey = "jwks"
|
jwksKey = "jwks"
|
||||||
|
|
||||||
|
jwksSecretTypeValue corev1.SecretType = "secrets.pinniped.dev/federation-domain-jwks"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -251,6 +253,7 @@ func (c *jwksWriterController) generateSecret(federationDomain *configv1alpha1.F
|
|||||||
activeJWKKey: jwkData,
|
activeJWKKey: jwkData,
|
||||||
jwksKey: jwksData,
|
jwksKey: jwksData,
|
||||||
},
|
},
|
||||||
|
Type: jwksSecretTypeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s, nil
|
return &s, nil
|
||||||
@ -285,6 +288,7 @@ func (c *jwksWriterController) createOrUpdateSecret(
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldSecret.Data = newSecret.Data
|
oldSecret.Data = newSecret.Data
|
||||||
|
oldSecret.Type = jwksSecretTypeValue
|
||||||
_, err = secretClient.Update(ctx, oldSecret, metav1.UpdateOptions{})
|
_, err = secretClient.Update(ctx, oldSecret, metav1.UpdateOptions{})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@ -322,6 +326,11 @@ func isFederationDomainControllee(obj metav1.Object) bool {
|
|||||||
|
|
||||||
// isValid returns whether the provided secret contains a valid active JWK and verification JWKS.
|
// isValid returns whether the provided secret contains a valid active JWK and verification JWKS.
|
||||||
func isValid(secret *corev1.Secret) bool {
|
func isValid(secret *corev1.Secret) bool {
|
||||||
|
if secret.Type != jwksSecretTypeValue {
|
||||||
|
plog.Debug("secret does not have the expected type", "expectedType", jwksSecretTypeValue, "actualType", secret.Type)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
jwkData, ok := secret.Data[activeJWKKey]
|
jwkData, ok := secret.Data[activeJWKKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
plog.Debug("secret does not contain active jwk")
|
plog.Debug("secret does not contain active jwk")
|
||||||
|
@ -281,6 +281,7 @@ func TestJWKSWriterControllerSync(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Type: "secrets.pinniped.dev/federation-domain-jwks",
|
||||||
}
|
}
|
||||||
s.Data = make(map[string][]byte)
|
s.Data = make(map[string][]byte)
|
||||||
if activeJWKPath != "" {
|
if activeJWKPath != "" {
|
||||||
@ -294,6 +295,9 @@ func TestJWKSWriterControllerSync(t *testing.T) {
|
|||||||
|
|
||||||
goodSecret := newSecret("testdata/good-jwk.json", "testdata/good-jwks.json")
|
goodSecret := newSecret("testdata/good-jwk.json", "testdata/good-jwks.json")
|
||||||
|
|
||||||
|
secretWithWrongType := newSecret("testdata/good-jwk.json", "testdata/good-jwks.json")
|
||||||
|
secretWithWrongType.Type = "not-the-right-type"
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key controllerlib.Key
|
key controllerlib.Key
|
||||||
@ -407,6 +411,24 @@ func TestJWKSWriterControllerSync(t *testing.T) {
|
|||||||
kubetesting.NewGetAction(federationDomainGVR, namespace, goodFederationDomain.Name),
|
kubetesting.NewGetAction(federationDomainGVR, namespace, goodFederationDomain.Name),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "wrong type in secret",
|
||||||
|
key: controllerlib.Key{Namespace: goodFederationDomain.Namespace, Name: goodFederationDomain.Name},
|
||||||
|
federationDomains: []*configv1alpha1.FederationDomain{
|
||||||
|
goodFederationDomainWithStatus,
|
||||||
|
},
|
||||||
|
secrets: []*corev1.Secret{
|
||||||
|
secretWithWrongType,
|
||||||
|
},
|
||||||
|
wantGenerateKeyCount: 1,
|
||||||
|
wantSecretActions: []kubetesting.Action{
|
||||||
|
kubetesting.NewGetAction(secretGVR, namespace, goodSecret.Name),
|
||||||
|
kubetesting.NewUpdateAction(secretGVR, namespace, goodSecret),
|
||||||
|
},
|
||||||
|
wantFederationDomainActions: []kubetesting.Action{
|
||||||
|
kubetesting.NewGetAction(federationDomainGVR, namespace, goodFederationDomain.Name),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invalid jwk JSON in secret",
|
name: "invalid jwk JSON in secret",
|
||||||
key: controllerlib.Key{Namespace: goodFederationDomain.Namespace, Name: goodFederationDomain.Name},
|
key: controllerlib.Key{Namespace: goodFederationDomain.Namespace, Name: goodFederationDomain.Name},
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/coreos/go-oidc"
|
"github.com/coreos/go-oidc"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
@ -39,9 +40,10 @@ const (
|
|||||||
controllerName = "upstream-observer"
|
controllerName = "upstream-observer"
|
||||||
|
|
||||||
// Constants related to the client credentials Secret.
|
// Constants related to the client credentials Secret.
|
||||||
oidcClientSecretType = "secrets.pinniped.dev/oidc-client"
|
oidcClientSecretType corev1.SecretType = "secrets.pinniped.dev/oidc-client"
|
||||||
clientIDDataKey = "clientID"
|
|
||||||
clientSecretDataKey = "clientSecret"
|
clientIDDataKey = "clientID"
|
||||||
|
clientSecretDataKey = "clientSecret"
|
||||||
|
|
||||||
// Constants related to the OIDC provider discovery cache. These do not affect the cache of JWKS.
|
// Constants related to the OIDC provider discovery cache. These do not affect the cache of JWKS.
|
||||||
validatorCacheTTL = 15 * time.Minute
|
validatorCacheTTL = 15 * time.Minute
|
||||||
|
@ -40,7 +40,7 @@ func TestSupervisorSecrets(t *testing.T) {
|
|||||||
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return env.SupervisorAppName + "-key"
|
return env.SupervisorAppName + "-key"
|
||||||
},
|
},
|
||||||
ensureValid: ensureValidSymmetricKey,
|
ensureValid: ensureValidSymmetricSecretOfTypeFunc("secrets.pinniped.dev/supervisor-csrf-signing-key"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jwks",
|
name: "jwks",
|
||||||
@ -54,21 +54,21 @@ func TestSupervisorSecrets(t *testing.T) {
|
|||||||
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.TokenSigningKey.Name
|
return federationDomain.Status.Secrets.TokenSigningKey.Name
|
||||||
},
|
},
|
||||||
ensureValid: ensureValidSymmetricKey,
|
ensureValid: ensureValidSymmetricSecretOfTypeFunc("secrets.pinniped.dev/federation-domain-token-signing-key"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "state signature secret",
|
name: "state signature secret",
|
||||||
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.StateSigningKey.Name
|
return federationDomain.Status.Secrets.StateSigningKey.Name
|
||||||
},
|
},
|
||||||
ensureValid: ensureValidSymmetricKey,
|
ensureValid: ensureValidSymmetricSecretOfTypeFunc("secrets.pinniped.dev/federation-domain-state-signing-key"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "state encryption secret",
|
name: "state encryption secret",
|
||||||
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
secretName: func(federationDomain *configv1alpha1.FederationDomain) string {
|
||||||
return federationDomain.Status.Secrets.StateEncryptionKey.Name
|
return federationDomain.Status.Secrets.StateEncryptionKey.Name
|
||||||
},
|
},
|
||||||
ensureValid: ensureValidSymmetricKey,
|
ensureValid: ensureValidSymmetricSecretOfTypeFunc("secrets.pinniped.dev/federation-domain-state-encryption-key"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -129,6 +129,9 @@ func TestSupervisorSecrets(t *testing.T) {
|
|||||||
func ensureValidJWKS(t *testing.T, secret *corev1.Secret) {
|
func ensureValidJWKS(t *testing.T, secret *corev1.Secret) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
// Ensure the secret has the right type.
|
||||||
|
require.Equal(t, corev1.SecretType("secrets.pinniped.dev/federation-domain-jwks"), secret.Type)
|
||||||
|
|
||||||
// Ensure the secret has an active key.
|
// Ensure the secret has an active key.
|
||||||
jwkData, ok := secret.Data["activeJWK"]
|
jwkData, ok := secret.Data["activeJWK"]
|
||||||
require.True(t, ok, "secret is missing active jwk")
|
require.True(t, ok, "secret is missing active jwk")
|
||||||
@ -157,10 +160,12 @@ func ensureValidJWKS(t *testing.T, secret *corev1.Secret) {
|
|||||||
require.True(t, foundActiveJWK, "could not find active JWK in JWKS: %s", jwks)
|
require.True(t, foundActiveJWK, "could not find active JWK in JWKS: %s", jwks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureValidSymmetricKey(t *testing.T, secret *corev1.Secret) {
|
func ensureValidSymmetricSecretOfTypeFunc(secretTypeValue string) func(*testing.T, *corev1.Secret) {
|
||||||
t.Helper()
|
return func(t *testing.T, secret *corev1.Secret) {
|
||||||
require.Equal(t, corev1.SecretType("secrets.pinniped.dev/symmetric"), secret.Type)
|
t.Helper()
|
||||||
key, ok := secret.Data["key"]
|
require.Equal(t, corev1.SecretType(secretTypeValue), secret.Type)
|
||||||
require.Truef(t, ok, "secret data does not contain 'key': %s", secret.Data)
|
key, ok := secret.Data["key"]
|
||||||
require.Equal(t, 32, len(key))
|
require.Truef(t, ok, "secret data does not contain 'key': %s", secret.Data)
|
||||||
|
require.Equal(t, 32, len(key))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user