Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Allow Custom Signer signingCA to Specify Namespace in AddOnTemplate #747

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ require (
k8s.io/kube-aggregator v0.31.3
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6
open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50
open-cluster-management.io/api v0.15.1-0.20241126073717-05ff7c1affe8
open-cluster-management.io/api v0.15.1-0.20241209025232-b62746ae96d4
open-cluster-management.io/sdk-go v0.15.1-0.20241125015855-1536c3970f8f
sigs.k8s.io/cluster-inventory-api v0.0.0-20240730014211-ef0154379848
sigs.k8s.io/controller-runtime v0.19.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@ k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50 h1:TXRd6OdGjArh6cwlCYOqlIcyx21k81oUIYj4rmHlYx0=
open-cluster-management.io/addon-framework v0.11.1-0.20241129080247-57b1d2859f50/go.mod h1:tsBSNs9mGfVQQjXBnjgpiX6r0UM+G3iNfmzQgKhEfw4=
open-cluster-management.io/api v0.15.1-0.20241126073717-05ff7c1affe8 h1:yKI2N8VN3zij+2O8kEOGfXBtZDs3pMey0BFfikgBpJM=
open-cluster-management.io/api v0.15.1-0.20241126073717-05ff7c1affe8/go.mod h1:9erZEWEn4bEqh0nIX2wA7f/s3KCuFycQdBrPrRzi0QM=
open-cluster-management.io/api v0.15.1-0.20241209025232-b62746ae96d4 h1:f6KU3t9s0PA6vXmAjB6A9sd52OqBqOFK2uAhk3UUBKs=
open-cluster-management.io/api v0.15.1-0.20241209025232-b62746ae96d4/go.mod h1:9erZEWEn4bEqh0nIX2wA7f/s3KCuFycQdBrPrRzi0QM=
open-cluster-management.io/sdk-go v0.15.1-0.20241125015855-1536c3970f8f h1:zeC7QrFNarfK2zY6jGtd+mX+yDrQQmnH/J8A7n5Nh38=
open-cluster-management.io/sdk-go v0.15.1-0.20241125015855-1536c3970f8f/go.mod h1:fi5WBsbC5K3txKb8eRLuP0Sim/Oqz/PHX18skAEyjiA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,19 @@ spec:
signingCA:
description: |-
SigningCA represents the reference of the secret on the hub cluster to sign the CSR
the secret must be in the namespace where the addon-manager is located, and the secret
type must be "kubernetes.io/tls"
the secret type must be "kubernetes.io/tls"
Note: The addon manager will not have permission to access the secret by default, so
the user must grant the permission to the addon manager(by creating rolebinding for
the addon-manager serviceaccount "addon-manager-controller-sa").
the user must grant the permission to the addon manager(by creating rolebinding/clusterrolebinding
for the addon-manager serviceaccount "addon-manager-controller-sa").
properties:
name:
description: Name of the signing CA secret
type: string
namespace:
description: Namespace of the signing CA secret, the
namespace of the addon-manager will be used if it
is not set.
type: string
required:
- name
type: object
Expand Down
11 changes: 8 additions & 3 deletions pkg/addon/templateagent/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,18 +285,23 @@ func CustomSignerWithExpiry(
if csr.Spec.SignerName != customSignerConfig.SignerName {
return nil
}
caSecret, err := kubeclient.CoreV1().Secrets(AddonManagerNamespace()).Get(

secretNamespace := AddonManagerNamespace()
if len(customSignerConfig.SigningCA.Namespace) != 0 {
secretNamespace = customSignerConfig.SigningCA.Namespace
}
caSecret, err := kubeclient.CoreV1().Secrets(secretNamespace).Get(
context.TODO(), customSignerConfig.SigningCA.Name, metav1.GetOptions{})
if err != nil {
utilruntime.HandleError(fmt.Errorf("get custome signer ca %s/%s failed, %v",
AddonManagerNamespace(), customSignerConfig.SigningCA.Name, err))
secretNamespace, customSignerConfig.SigningCA.Name, err))
return nil
}

caData, caKey, err := extractCAdata(caSecret.Data[corev1.TLSCertKey], caSecret.Data[corev1.TLSPrivateKeyKey])
if err != nil {
utilruntime.HandleError(fmt.Errorf("get ca %s/%s data failed, %v",
AddonManagerNamespace(), customSignerConfig.SigningCA.Name, err))
secretNamespace, customSignerConfig.SigningCA.Name, err))
return nil
}
return utils.DefaultSignerWithExpiry(caKey, caData, duration)(csr)
Expand Down
62 changes: 62 additions & 0 deletions pkg/addon/templateagent/registration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"net"
"os"
"strings"
"testing"
Expand All @@ -13,12 +14,14 @@ import (
"github.com/stretchr/testify/assert"
certificatesv1 "k8s.io/api/certificates/v1"
certificates "k8s.io/api/certificates/v1beta1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
fakekube "k8s.io/client-go/kubernetes/fake"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog/v2/ktesting"

addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
Expand Down Expand Up @@ -248,11 +251,28 @@ func TestTemplateCSRApproveCheckFunc(t *testing.T) {
}

func TestTemplateCSRSignFunc(t *testing.T) {
ca, key, err := certutil.GenerateSelfSignedCertKey("test", []net.IP{}, []string{})
if err != nil {
t.Errorf("Failed to generate self signed CA config: %v", err)
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test-ns",
},
Data: map[string][]byte{
corev1.TLSCertKey: ca,
corev1.TLSPrivateKeyKey: key,
},
Type: corev1.SecretTypeTLS,
}

cases := []struct {
name string
cluster *clusterv1.ManagedCluster
addon *addonapiv1alpha1.ManagedClusterAddOn
template *addonapiv1alpha1.AddOnTemplate
casecret *corev1.Secret
csr *certificatesv1.CertificateSigningRequest
expectedCert []byte
}{
Expand Down Expand Up @@ -325,11 +345,53 @@ func TestTemplateCSRSignFunc(t *testing.T) {
},
expectedCert: nil,
},
{
name: "customsigner with ca secret",
casecret: secret,
cluster: NewFakeManagedCluster("cluster1"),
template: NewFakeAddonTemplate("template1", []addonapiv1alpha1.RegistrationSpec{
{
Type: addonapiv1alpha1.RegistrationTypeCustomSigner,
CustomSigner: &addonapiv1alpha1.CustomSignerRegistrationConfig{
SignerName: "s1",
Subject: &addonapiv1alpha1.Subject{
User: "u1",
Groups: []string{
"g1",
"g2",
},
OrganizationUnits: []string{},
},
SigningCA: addonapiv1alpha1.SigningCARef{
Name: secret.Name,
Namespace: secret.Namespace,
},
},
},
}),
addon: NewFakeTemplateManagedClusterAddon("addon1", "cluster1", "template1", "fakehash"),
csr: &certificatesv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: "csr1",
Labels: map[string]string{
clusterv1.ClusterNameLabelKey: "cluster1",
},
},
Spec: certificatesv1.CertificateSigningRequestSpec{
SignerName: "s1",
Username: "system:open-cluster-management:cluster1:adcde",
},
},
expectedCert: nil,
},
}
for _, c := range cases {
_, ctx := ktesting.NewTestContext(t)
addonClient := fakeaddon.NewSimpleClientset(c.template, c.addon)
hubKubeClient := fakekube.NewSimpleClientset()
if c.casecret != nil {
hubKubeClient = fakekube.NewSimpleClientset(c.casecret)
}
addonInformerFactory := addoninformers.NewSharedInformerFactory(addonClient, 30*time.Minute)
mcaStore := addonInformerFactory.Addon().V1alpha1().ManagedClusterAddOns().Informer().GetStore()
if err := mcaStore.Add(c.addon); err != nil {
Expand Down
52 changes: 37 additions & 15 deletions test/e2e/addonmanagement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,24 @@ var _ = ginkgo.Describe("Enable addon management feature gate", ginkgo.Ordered,
"addon/signca_secret_rolebinding.yaml",
}

var signerSecretNamespace string

ginkgo.BeforeEach(func() {
signerSecretNamespace = "signer-secret-test-ns" + rand.String(6)

ginkgo.By("create addon custom sign secret namespace")
_, err := hub.KubeClient.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: signerSecretNamespace,
},
}, metav1.CreateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
gomega.Expect(err).ToNot(gomega.HaveOccurred())
}

ginkgo.By("create addon custom sign secret")
err := copySignerSecret(context.TODO(), hub.KubeClient, "open-cluster-management-hub",
"signer-secret", templateagent.AddonManagerNamespace(), customSignerSecretName)
err = copySignerSecret(context.TODO(), hub.KubeClient, "open-cluster-management-hub",
"signer-secret", signerSecretNamespace, customSignerSecretName)
gomega.Expect(err).ToNot(gomega.HaveOccurred())

// the addon manager deployment should be running
Expand All @@ -85,11 +99,12 @@ var _ = ginkgo.Describe("Enable addon management feature gate", ginkgo.Ordered,
ginkgo.By(fmt.Sprintf("create addon template resources for cluster %v", universalClusterName))
err = createResourcesFromYamlFiles(context.Background(), hub.DynamicClient, hub.RestMapper, s,
defaultAddonTemplateReaderManifestsFunc(manifests.AddonManifestFiles, map[string]interface{}{
"Namespace": universalClusterName,
"AddonInstallNamespace": addonInstallNamespace,
"CustomSignerName": customSignerName,
"AddonManagerNamespace": templateagent.AddonManagerNamespace(),
"CustomSignerSecretName": customSignerSecretName,
"Namespace": universalClusterName,
"AddonInstallNamespace": addonInstallNamespace,
"CustomSignerName": customSignerName,
"AddonManagerNamespace": templateagent.AddonManagerNamespace(),
"CustomSignerSecretName": customSignerSecretName,
"CustomSignerSecretNamespace": signerSecretNamespace,
}),
templateResources,
)
Expand Down Expand Up @@ -132,22 +147,29 @@ var _ = ginkgo.Describe("Enable addon management feature gate", ginkgo.Ordered,
ginkgo.By(fmt.Sprintf("delete addon template resources for cluster %v", universalClusterName))
err = deleteResourcesFromYamlFiles(context.Background(), hub.DynamicClient, hub.RestMapper, s,
defaultAddonTemplateReaderManifestsFunc(manifests.AddonManifestFiles, map[string]interface{}{
"Namespace": universalClusterName,
"AddonInstallNamespace": addonInstallNamespace,
"CustomSignerName": customSignerName,
"AddonManagerNamespace": templateagent.AddonManagerNamespace(),
"CustomSignerSecretName": customSignerSecretName,
"Namespace": universalClusterName,
"AddonInstallNamespace": addonInstallNamespace,
"CustomSignerName": customSignerName,
"AddonManagerNamespace": templateagent.AddonManagerNamespace(),
"CustomSignerSecretName": customSignerSecretName,
"CustomSignerSecretNamespace": signerSecretNamespace,
}),
templateResources,
)
gomega.Expect(err).ToNot(gomega.HaveOccurred())

ginkgo.By("delete addon custom sign secret")
err = hub.KubeClient.CoreV1().Secrets(templateagent.AddonManagerNamespace()).Delete(context.TODO(),
err = hub.KubeClient.CoreV1().Secrets(signerSecretNamespace).Delete(context.TODO(),
customSignerSecretName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
ginkgo.Fail(fmt.Sprintf("failed to delete custom signer secret %v/%v: %v",
templateagent.AddonManagerNamespace(), customSignerSecretName, err))
signerSecretNamespace, customSignerSecretName, err))
}

ginkgo.By("delete addon custom sign secret namespace")
err = hub.KubeClient.CoreV1().Namespaces().Delete(context.TODO(), signerSecretNamespace, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
ginkgo.Fail(fmt.Sprintf("failed to delete custom signer secret namespace %v: %v", signerSecretNamespace, err))
}

// delete all CSR created for the addon on the hub cluster, otherwise if it reches the limit number 10, the
Expand Down Expand Up @@ -182,7 +204,7 @@ var _ = ginkgo.Describe("Enable addon management feature gate", ginkgo.Ordered,
return err
}).Should(gomega.Succeed())

ginkgo.By("Check custom signer secret is created")
ginkgo.By("Check custom client cert secret is created")
gomega.Eventually(func() error {
_, err := hub.KubeClient.CoreV1().Secrets(addonInstallNamespace).Get(context.TODO(),
templateagent.CustomSignedSecretName(addOnName, customSignerName), metav1.GetOptions{})
Expand Down
1 change: 1 addition & 0 deletions test/e2e/manifests/addon/addon_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ spec:
signerName: << CustomSignerName >>
signingCA:
name: << CustomSignerSecretName >>
namespace: << CustomSignerSecretNamespace >>
subject:
groups:
- g1
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/manifests/addon/signca_secret_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: get-customer-ca
namespace: << AddonManagerNamespace >>
namespace: << CustomSignerSecretNamespace >>
rules:
- apiGroups:
- ""
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/manifests/addon/signca_secret_rolebinding.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: get-customer-ca
namespace: << AddonManagerNamespace >>
namespace: << CustomSignerSecretNamespace >>
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
Expand Down
2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ open-cluster-management.io/addon-framework/pkg/agent
open-cluster-management.io/addon-framework/pkg/assets
open-cluster-management.io/addon-framework/pkg/index
open-cluster-management.io/addon-framework/pkg/utils
# open-cluster-management.io/api v0.15.1-0.20241126073717-05ff7c1affe8
# open-cluster-management.io/api v0.15.1-0.20241209025232-b62746ae96d4
## explicit; go 1.22.0
open-cluster-management.io/api/addon/v1alpha1
open-cluster-management.io/api/client/addon/clientset/versioned
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading