Skip to content

Commit

Permalink
🌱 make additional secret data always sensitive
Browse files Browse the repository at this point in the history
Signed-off-by: Yang Le <yangle@redhat.com>
  • Loading branch information
elgnay committed Jun 17, 2024
1 parent 1227b71 commit 2b85559
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 38 deletions.
15 changes: 6 additions & 9 deletions pkg/registration/clientcert/cert_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,9 @@ type ClientCertOption struct {
// SecretName is the name of the secret containing client certificate. The secret will be created if
// it does not exist.
SecretName string
// AdditonalSecretData contains data that will be added into client certificate secret besides tls.key/tls.crt
// AdditionalSecretData contains data that will be added into client certificate secret besides tls.key/tls.crt
// Once AdditionalSecretData changes, the client cert will be recreated.
AdditionalSecretData map[string][]byte
// AdditonalSecretDataSensitive is true indicates the client cert is sensitive to the AdditonalSecretData.
// That means once AdditonalSecretData changes, the client cert will be recreated.
AdditionalSecretDataSensitive bool
}

type StatusUpdateFunc func(ctx context.Context, cond metav1.Condition) error
Expand Down Expand Up @@ -293,7 +291,6 @@ func (c *clientCertificateController) sync(ctx context.Context, syncCtx factory.
secret,
syncCtx.Recorder(),
c.Subject,
c.AdditionalSecretDataSensitive,
c.AdditionalSecretData)
if err != nil {
return err
Expand Down Expand Up @@ -375,14 +372,14 @@ func shouldCreateCSR(
secret *corev1.Secret,
recorder events.Recorder,
subject *pkix.Name,
additionalSecretDataSensitive bool,
additionalSecretData map[string][]byte) (bool, error) {
switch {
case !hasValidClientCertificate(logger, subject, secret):
recorder.Eventf("NoValidCertificateFound",
"No valid client certificate for %s is found. Bootstrap is required", controllerName)
case additionalSecretDataSensitive && !hasAdditionalSecretData(additionalSecretData, secret):
recorder.Eventf("AdditonalSecretDataChanged",
// return true to create a CSR for a new client cert once the additional secret data changes.
case !hasAdditionalSecretData(additionalSecretData, secret):
recorder.Eventf("AdditionalSecretDataChanged",
"The additional secret data is changed. Re-create the client certificate for %s", controllerName)
default:
notBefore, notAfter, err := getCertValidityPeriod(secret)
Expand All @@ -408,7 +405,7 @@ func shouldCreateCSR(
return true, nil
}

// hasAdditonalSecretData checks if the secret includes the expected additional secret data.
// hasAdditionalSecretData checks if the secret includes the expected additional secret data.
func hasAdditionalSecretData(additionalSecretData map[string][]byte, secret *corev1.Secret) bool {
for k, v := range additionalSecretData {
value, ok := secret.Data[k]
Expand Down
23 changes: 10 additions & 13 deletions pkg/registration/clientcert/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ func TestSync(t *testing.T) {
}

cases := []struct {
name string
queueKey string
secrets []runtime.Object
approvedCSRCert *testinghelpers.TestCert
keyDataExpected bool
csrNameExpected bool
additonalSecretDataSensitive bool
expectedCondition *metav1.Condition
validateActions func(t *testing.T, hubActions, agentActions []clienttesting.Action)
name string
queueKey string
secrets []runtime.Object
approvedCSRCert *testinghelpers.TestCert
keyDataExpected bool
csrNameExpected bool
expectedCondition *metav1.Condition
validateActions func(t *testing.T, hubActions, agentActions []clienttesting.Action)
}{
{
name: "agent bootstrap",
Expand Down Expand Up @@ -139,9 +138,8 @@ func TestSync(t *testing.T) {
AgentNameFile: []byte("invalid-name"),
}),
},
keyDataExpected: true,
csrNameExpected: true,
additonalSecretDataSensitive: true,
keyDataExpected: true,
csrNameExpected: true,
validateActions: func(t *testing.T, hubActions, agentActions []clienttesting.Action) {
testingcommon.AssertActions(t, hubActions, "create")
actual := hubActions[0].(clienttesting.CreateActionImpl).Object
Expand Down Expand Up @@ -183,7 +181,6 @@ func TestSync(t *testing.T) {
ClusterNameFile: []byte(testinghelpers.TestManagedClusterName),
AgentNameFile: []byte(testAgentName),
},
AdditionalSecretDataSensitive: c.additonalSecretDataSensitive,
}
csrOption := CSROption{
ObjectMeta: metav1.ObjectMeta{
Expand Down
11 changes: 5 additions & 6 deletions pkg/registration/spoke/addon/registration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,16 @@ func (c *addOnRegistrationController) startRegistration(ctx context.Context, con
kubeInformerFactory := informers.NewSharedInformerFactoryWithOptions(
kubeClient, 10*time.Minute, informers.WithNamespace(config.InstallationNamespace))

additonalSecretData := map[string][]byte{}
additionalSecretData := map[string][]byte{}
if config.registration.SignerName == certificatesv1.KubeAPIServerClientSignerName {
additonalSecretData[clientcert.KubeconfigFile] = c.kubeconfigData
additionalSecretData[clientcert.KubeconfigFile] = c.kubeconfigData
}

// build and start a client cert controller
clientCertOption := clientcert.ClientCertOption{
SecretNamespace: config.InstallationNamespace,
SecretName: config.secretName,
AdditionalSecretData: additonalSecretData,
AdditionalSecretDataSensitive: true,
SecretNamespace: config.InstallationNamespace,
SecretName: config.secretName,
AdditionalSecretData: additionalSecretData,
}

csrOption := clientcert.CSROption{
Expand Down
7 changes: 0 additions & 7 deletions pkg/registration/spoke/spokeagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/openshift/library-go/pkg/controller/controllercmd"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/wait"
Expand Down Expand Up @@ -244,12 +243,6 @@ func (o *SpokeAgentConfig) RunSpokeAgentWithSpokeInformers(ctx context.Context,
// in scenario #2 and #3, which results in an error message in log: 'Observed a panic: timeout waiting for
// informer cache'
if !ok {
// delete the hub kubeconfig secret if exits
if err := managementKubeClient.CoreV1().Secrets(o.agentOptions.ComponentNamespace).Delete(ctx,
o.registrationOption.HubKubeconfigSecret, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
return err
}

// create a ClientCertForHubController for spoke agent bootstrap
// the bootstrap informers are supposed to be terminated after completing the bootstrap process.
bootstrapInformerFactory := informers.NewSharedInformerFactory(bootstrapKubeClient, 10*time.Minute)
Expand Down
24 changes: 21 additions & 3 deletions test/integration/registration/spokeagent_rebootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
certutil "k8s.io/client-go/util/cert"
"sigs.k8s.io/controller-runtime/pkg/envtest"

addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
Expand Down Expand Up @@ -518,10 +519,27 @@ var _ = ginkgo.Describe("Rebootstrap", func() {
ginkgo.By("stop the hub")
stopHub()

ginkgo.By("the hub kubeconfig secret should be deleted once client cert expired")
ginkgo.By("wait until the client cert expires")
gomega.Eventually(func() bool {
_, err := kubeClient.CoreV1().Secrets(testNamespace).Get(context.Background(), hubKubeconfigSecret, metav1.GetOptions{})
return apierrors.IsNotFound(err)
secret, err := kubeClient.CoreV1().Secrets(testNamespace).Get(context.Background(), hubKubeconfigSecret, metav1.GetOptions{})
if err != nil {
return false
}
data, ok := secret.Data[clientcert.TLSCertFile]
if !ok {
return false
}
certs, err := certutil.ParseCertsPEM(data)
if err != nil {
return false
}
now := time.Now()
for _, cert := range certs {
if now.After(cert.NotAfter) {
return true
}
}
return false
}, eventuallyTimeout, eventuallyInterval).Should(gomega.BeTrue())

ginkgo.By("start the hub again")
Expand Down

0 comments on commit 2b85559

Please sign in to comment.