Configure name of the supervisor default TLS cert secret via ConfigMap

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
This commit is contained in:
Ryan Richard 2020-10-28 11:56:50 -07:00 committed by Andrew Keesler
parent 978ecda758
commit 29e0ce5662
8 changed files with 71 additions and 16 deletions

View File

@ -113,6 +113,7 @@ func startControllers(
WithController( WithController(
supervisorconfig.NewTLSCertObserverController( supervisorconfig.NewTLSCertObserverController(
dynamicTLSCertProvider, dynamicTLSCertProvider,
cfg.NamesConfig.DefaultTLSCertificateSecret,
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(),
controllerlib.WithInformer, controllerlib.WithInformer,

View File

@ -30,6 +30,8 @@ metadata:
data: data:
#@yaml/text-templated-strings #@yaml/text-templated-strings
pinniped.yaml: | pinniped.yaml: |
names:
defaultTLSCertificateSecret: (@= defaultResourceNameWithSuffix("default-tls-certificate") @)
labels: (@= json.encode(labels()).rstrip() @) labels: (@= json.encode(labels()).rstrip() @)
--- ---
#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": #@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "":

View File

@ -8,8 +8,11 @@ package supervisor
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"go.pinniped.dev/internal/constable"
) )
// FromPath loads an Config from a provided local file path, inserts any // FromPath loads an Config from a provided local file path, inserts any
@ -30,5 +33,20 @@ func FromPath(path string) (*Config, error) {
config.Labels = make(map[string]string) config.Labels = make(map[string]string)
} }
if err := validateNames(&config.NamesConfig); err != nil {
return nil, fmt.Errorf("validate names: %w", err)
}
return &config, nil return &config, nil
} }
func validateNames(names *NamesConfigSpec) error {
missingNames := []string{}
if names.DefaultTLSCertificateSecret == "" {
missingNames = append(missingNames, "defaultTLSCertificateSecret")
}
if len(missingNames) > 0 {
return constable.Error("missing required names: " + strings.Join(missingNames, ", "))
}
return nil
}

View File

@ -18,6 +18,7 @@ func TestFromPath(t *testing.T) {
name string name string
yaml string yaml string
wantConfig *Config wantConfig *Config
wantError string
}{ }{
{ {
name: "Happy", name: "Happy",
@ -26,23 +27,40 @@ func TestFromPath(t *testing.T) {
labels: labels:
myLabelKey1: myLabelValue1 myLabelKey1: myLabelValue1
myLabelKey2: myLabelValue2 myLabelKey2: myLabelValue2
names:
defaultTLSCertificateSecret: my-secret-name
`), `),
wantConfig: &Config{ wantConfig: &Config{
Labels: map[string]string{ Labels: map[string]string{
"myLabelKey1": "myLabelValue1", "myLabelKey1": "myLabelValue1",
"myLabelKey2": "myLabelValue2", "myLabelKey2": "myLabelValue2",
}, },
NamesConfig: NamesConfigSpec{
DefaultTLSCertificateSecret: "my-secret-name",
},
}, },
}, },
{ {
name: "When only the required fields are present, causes other fields to be defaulted", name: "When only the required fields are present, causes other fields to be defaulted",
yaml: here.Doc(` yaml: here.Doc(`
--- ---
names:
defaultTLSCertificateSecret: my-secret-name
`), `),
wantConfig: &Config{ wantConfig: &Config{
Labels: map[string]string{}, Labels: map[string]string{},
NamesConfig: NamesConfigSpec{
DefaultTLSCertificateSecret: "my-secret-name",
},
}, },
}, },
{
name: "Missing defaultTLSCertificateSecret name",
yaml: here.Doc(`
---
`),
wantError: "validate names: missing required names: defaultTLSCertificateSecret",
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
@ -62,8 +80,12 @@ func TestFromPath(t *testing.T) {
// Test FromPath() // Test FromPath()
config, err := FromPath(f.Name()) config, err := FromPath(f.Name())
require.NoError(t, err) if test.wantError != "" {
require.Equal(t, test.wantConfig, config) require.EqualError(t, err, test.wantError)
} else {
require.NoError(t, err)
require.Equal(t, test.wantConfig, config)
}
}) })
} }
} }

View File

@ -5,5 +5,11 @@ package supervisor
// Config contains knobs to setup an instance of the Pinniped Supervisor. // Config contains knobs to setup an instance of the Pinniped Supervisor.
type Config struct { type Config struct {
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
NamesConfig NamesConfigSpec `json:"names"`
}
// NamesConfigSpec configures the names of some Kubernetes resources for the Supervisor.
type NamesConfigSpec struct {
DefaultTLSCertificateSecret string `json:"defaultTLSCertificateSecret"`
} }

View File

@ -18,12 +18,11 @@ import (
"go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/controllerlib"
) )
const SecretNameForDefaultTLSCertificate = "default-tls-certificate" //nolint:gosec // this is not a hardcoded credential
type tlsCertObserverController struct { type tlsCertObserverController struct {
issuerTLSCertSetter IssuerTLSCertSetter issuerTLSCertSetter IssuerTLSCertSetter
oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer defaultTLSCertificateSecretName string
secretInformer corev1informers.SecretInformer oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer
secretInformer corev1informers.SecretInformer
} }
type IssuerTLSCertSetter interface { type IssuerTLSCertSetter interface {
@ -33,6 +32,7 @@ type IssuerTLSCertSetter interface {
func NewTLSCertObserverController( func NewTLSCertObserverController(
issuerTLSCertSetter IssuerTLSCertSetter, issuerTLSCertSetter IssuerTLSCertSetter,
defaultTLSCertificateSecretName string,
secretInformer corev1informers.SecretInformer, secretInformer corev1informers.SecretInformer,
oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer, oidcProviderConfigInformer v1alpha1.OIDCProviderConfigInformer,
withInformer pinnipedcontroller.WithInformerOptionFunc, withInformer pinnipedcontroller.WithInformerOptionFunc,
@ -41,9 +41,10 @@ func NewTLSCertObserverController(
controllerlib.Config{ controllerlib.Config{
Name: "tls-certs-observer-controller", Name: "tls-certs-observer-controller",
Syncer: &tlsCertObserverController{ Syncer: &tlsCertObserverController{
issuerTLSCertSetter: issuerTLSCertSetter, issuerTLSCertSetter: issuerTLSCertSetter,
oidcProviderConfigInformer: oidcProviderConfigInformer, defaultTLSCertificateSecretName: defaultTLSCertificateSecretName,
secretInformer: secretInformer, oidcProviderConfigInformer: oidcProviderConfigInformer,
secretInformer: secretInformer,
}, },
}, },
withInformer( withInformer(
@ -88,7 +89,7 @@ func (c *tlsCertObserverController) Sync(ctx controllerlib.Context) error {
klog.InfoS("tlsCertObserverController Sync updated the TLS cert cache", "issuerHostCount", len(issuerHostToTLSCertMap)) klog.InfoS("tlsCertObserverController Sync updated the TLS cert cache", "issuerHostCount", len(issuerHostToTLSCertMap))
c.issuerTLSCertSetter.SetIssuerHostToTLSCertMap(issuerHostToTLSCertMap) c.issuerTLSCertSetter.SetIssuerHostToTLSCertMap(issuerHostToTLSCertMap)
defaultCert, err := c.certFromSecret(ns, SecretNameForDefaultTLSCertificate) defaultCert, err := c.certFromSecret(ns, c.defaultTLSCertificateSecretName)
if err != nil { if err != nil {
c.issuerTLSCertSetter.SetDefaultTLSCert(nil) c.issuerTLSCertSetter.SetDefaultTLSCert(nil)
} else { } else {

View File

@ -42,6 +42,7 @@ func TestTLSCertObserverControllerInformerFilters(t *testing.T) {
oidcProviderConfigInformer := pinnipedinformers.NewSharedInformerFactory(nil, 0).Config().V1alpha1().OIDCProviderConfigs() oidcProviderConfigInformer := pinnipedinformers.NewSharedInformerFactory(nil, 0).Config().V1alpha1().OIDCProviderConfigs()
_ = NewTLSCertObserverController( _ = NewTLSCertObserverController(
nil, nil,
"", // don't care about the secret name for this test
secretsInformer, secretsInformer,
oidcProviderConfigInformer, oidcProviderConfigInformer,
observableWithInformerOption.WithInformer, // make it possible to observe the behavior of the Filters observableWithInformerOption.WithInformer, // make it possible to observe the behavior of the Filters
@ -115,7 +116,10 @@ func (f *fakeIssuerTLSCertSetter) SetDefaultTLSCert(certificate *tls.Certificate
func TestTLSCertObserverControllerSync(t *testing.T) { func TestTLSCertObserverControllerSync(t *testing.T) {
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) { spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
const installedInNamespace = "some-namespace" const (
installedInNamespace = "some-namespace"
defaultTLSSecretName = "some-default-secret-name"
)
var ( var (
r *require.Assertions r *require.Assertions
@ -136,6 +140,7 @@ func TestTLSCertObserverControllerSync(t *testing.T) {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewTLSCertObserverController( subject = NewTLSCertObserverController(
issuerTLSCertSetter, issuerTLSCertSetter,
defaultTLSSecretName,
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(), pinnipedInformers.Config().V1alpha1().OIDCProviderConfigs(),
controllerlib.WithInformer, controllerlib.WithInformer,
@ -324,7 +329,7 @@ func TestTLSCertObserverControllerSync(t *testing.T) {
r.Equal(expectedCertificate2, *actualCertificate2) r.Equal(expectedCertificate2, *actualCertificate2)
}) })
when("there is also a default TLS cert secret called default-tls-certificate", func() { when("there is also a default TLS cert secret with the configured default TLS cert secret name", func() {
var ( var (
expectedDefaultCertificate tls.Certificate expectedDefaultCertificate tls.Certificate
) )
@ -338,7 +343,7 @@ func TestTLSCertObserverControllerSync(t *testing.T) {
expectedDefaultCertificate, err = tls.X509KeyPair(testCrt, testKey) expectedDefaultCertificate, err = tls.X509KeyPair(testCrt, testKey)
r.NoError(err) r.NoError(err)
defaultTLSCertSecret := &corev1.Secret{ defaultTLSCertSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "default-tls-certificate", Namespace: installedInNamespace}, ObjectMeta: metav1.ObjectMeta{Name: defaultTLSSecretName, Namespace: installedInNamespace},
Data: map[string][]byte{"tls.crt": testCrt, "tls.key": testKey}, Data: map[string][]byte{"tls.crt": testCrt, "tls.key": testKey},
} }
r.NoError(kubeInformerClient.Tracker().Add(defaultTLSCertSecret)) r.NoError(kubeInformerClient.Tracker().Add(defaultTLSCertSecret))

View File

@ -135,7 +135,7 @@ func TestSupervisorTLSTerminationWithDefaultCerts(t *testing.T) {
requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t, issuerUsingIPAddress) requireEndpointHasTLSErrorBecauseCertificatesAreNotReady(t, issuerUsingIPAddress)
// Create a Secret at the special name which represents the default TLS cert. // Create a Secret at the special name which represents the default TLS cert.
specialNameForDefaultTLSCertSecret := "default-tls-certificate" //nolint:gosec // this is not a hardcoded credential specialNameForDefaultTLSCertSecret := "pinniped-supervisor-default-tls-certificate" //nolint:gosec // this is not a hardcoded credential
defaultCA := createTLSCertificateSecret(ctx, t, ns, "cert-hostname-doesnt-matter", []net.IP{ip}, specialNameForDefaultTLSCertSecret, kubeClient) defaultCA := createTLSCertificateSecret(ctx, t, ns, "cert-hostname-doesnt-matter", []net.IP{ip}, specialNameForDefaultTLSCertSecret, kubeClient)
// Now that the Secret exists, we should be able to access the endpoints by IP address using the CA. // Now that the Secret exists, we should be able to access the endpoints by IP address using the CA.