diff --git a/pkg/tlsutil/tls.go b/pkg/tlsutil/tls.go index 5480cf1db64..f613fa4f5e0 100644 --- a/pkg/tlsutil/tls.go +++ b/pkg/tlsutil/tls.go @@ -157,7 +157,8 @@ func (scg *SDKCertGenerator) GenerateCert(cr runtime.Object, service *v1.Service if err != nil { return nil, nil, nil, err } - caSecret, caConfigMap, err := getCASecretAndConfigMapInCluster(scg.KubeClient, ToCASecretAndConfigMapName(k, n), ns) + caSecretAndConfigMapName := ToCASecretAndConfigMapName(k, n) + caSecret, caConfigMap, err := getCASecretAndConfigMapInCluster(scg.KubeClient, caSecretAndConfigMapName, ns) if err != nil { return nil, nil, nil, err } @@ -186,15 +187,44 @@ func (scg *SDKCertGenerator) GenerateCert(cr runtime.Object, service *v1.Service if err != nil { return nil, nil, nil, err } - appSecret, err := scg.KubeClient.CoreV1().Secrets(ns).Create(toTLSSecret(key, cert, appSecretName, ns)) + appSecret, err := scg.KubeClient.CoreV1().Secrets(ns).Create(toTLSSecret(key, cert, appSecretName)) if err != nil { return nil, nil, nil, err } return appSecret, caConfigMap, caSecret, nil } else { - // TODO: handle the case where both CA and Application TLS assets don't exist. + // case: both CA and Application TLS assets don't exist. + caKey, err := newPrivateKey() + if err != nil { + return nil, nil, nil, err + } + caCert, err := newSelfSignedCACertificate(caKey) + if err != nil { + return nil, nil, nil, err + } + caSecret, caConfigMap := toCASecretAndConfigmap(caKey, caCert, caSecretAndConfigMapName) + caSecret, err = scg.KubeClient.CoreV1().Secrets(ns).Create(caSecret) + if err != nil { + return nil, nil, nil, err + } + caConfigMap, err = scg.KubeClient.CoreV1().ConfigMaps(ns).Create(caConfigMap) + if err != nil { + return nil, nil, nil, err + } + key, err := newPrivateKey() + if err != nil { + return nil, nil, nil, err + } + cert, err := newSignedCertificate(config, service, key, caCert, caKey) + if err != nil { + return nil, nil, nil, err + } + appSecret, err := scg.KubeClient.CoreV1().Secrets(ns).Create(toTLSSecret(key, cert, appSecretName)) + if err != nil { + return nil, nil, nil, err + } + return appSecret, caConfigMap, caSecret, nil } - return nil, nil, nil, nil } func verifyConfig(config *CertConfig) error { @@ -276,15 +306,10 @@ func toKindNameNamespace(cr runtime.Object) (string, string, string, error) { // toTLSSecret returns a client/server "kubernetes.io/tls" secret. // TODO: add owner ref. -func toTLSSecret(key *rsa.PrivateKey, cert *x509.Certificate, name, namespace string) *v1.Secret { +func toTLSSecret(key *rsa.PrivateKey, cert *x509.Certificate, name string) *v1.Secret { return &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, }, Data: map[string][]byte{ v1.TLSPrivateKeyKey: encodePrivateKeyPEM(key), @@ -295,30 +320,20 @@ func toTLSSecret(key *rsa.PrivateKey, cert *x509.Certificate, name, namespace st } // TODO: add owner ref. -func toCASecretAndConfigmap(key *rsa.PrivateKey, cert *x509.Certificate, name, namespace string) (*v1.ConfigMap, *v1.Secret) { - return &v1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, +func toCASecretAndConfigmap(key *rsa.PrivateKey, cert *x509.Certificate, name string) (*v1.Secret, *v1.ConfigMap) { + return &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, }, - Data: map[string]string{ - TLSCACertKey: string(encodeCertificatePEM(cert)), - }, - }, &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", + Data: map[string][]byte{ + TLSPrivateCAKeyKey: encodePrivateKeyPEM(key), }, + }, &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, }, - Data: map[string][]byte{ - TLSPrivateCAKeyKey: encodePrivateKeyPEM(key), + Data: map[string]string{ + TLSCACertKey: string(encodeCertificatePEM(cert)), }, } } diff --git a/test/e2e/tls_util_test.go b/test/e2e/tls_util_test.go index 4490e258dcf..9e132675674 100644 --- a/test/e2e/tls_util_test.go +++ b/test/e2e/tls_util_test.go @@ -24,6 +24,7 @@ import ( "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) var ( @@ -104,17 +105,7 @@ func TestBothAppAndCATLSAssetsExist(t *testing.T) { } cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - // Use Pod as a dummy runtime object for the CR input of GenerateCert(). - mCR := &v1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: crKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: crName, - Namespace: namespace, - }, - } - actualAppSecret, actualCaConfigMap, actualCaSecret, err := cg.GenerateCert(mCR, nil, ccfg) + actualAppSecret, actualCaConfigMap, actualCaSecret, err := cg.GenerateCert(newDummyCR(namespace), nil, ccfg) if err != nil { t.Fatal(err) } @@ -146,17 +137,7 @@ func TestOnlyAppSecretExist(t *testing.T) { } cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - // Use Pod as a dummy runtime object for the CR input of GenerateCert(). - mCR := &v1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: crKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: crName, - Namespace: namespace, - }, - } - _, _, _, err = cg.GenerateCert(mCR, nil, ccfg) + _, _, _, err = cg.GenerateCert(newDummyCR(namespace), nil, ccfg) if err == nil { t.Fatal("expect error, but got none") } @@ -186,27 +167,83 @@ func TestOnlyCAExist(t *testing.T) { } cg := tlsutil.NewSDKCertGenerator(f.KubeClient) - // Use Pod as a dummy runtime object for the CR input of GenerateCert(). - mCR := &v1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: crKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: crName, - Namespace: namespace, - }, + appSecret, _, _, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg) + if err != nil { + t.Fatal(err) } - appSvc := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app-service", - Namespace: namespace, - }, + + verifyAppSecret(t, appSecret, namespace) +} + +// TestNoneOfCaAndAppSecretExist ensures that when none of the CA and Application TLS assets +// exist, GenerateCert() creates both and put them into the k8s cluster. +func TestNoneOfCaAndAppSecretExist(t *testing.T) { + f := framework.Global + ctx := f.NewTestCtx(t) + defer ctx.Cleanup(t) + namespace, err := ctx.GetNamespace() + if err != nil { + t.Fatal(err) } - appSecret, _, _, err := cg.GenerateCert(mCR, appSvc, ccfg) + + cg := tlsutil.NewSDKCertGenerator(f.KubeClient) + appSecret, caConfigMap, caSecret, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg) if err != nil { t.Fatal(err) } + verifyAppSecret(t, appSecret, namespace) + verifyCaConfigMap(t, caConfigMap, namespace) + verifyCASecret(t, caSecret, namespace) +} + +func verifyCASecret(t *testing.T, caSecret *v1.Secret, namespace string) { + // check if caConfigMap has the correct fields. + if caConfigMapAndSecretName != caSecret.Name { + t.Fatalf("expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name) + } + if namespace != caSecret.Namespace { + t.Fatalf("expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace) + } + if _, ok := caSecret.Data[tlsutil.TLSPrivateCAKeyKey]; !ok { + t.Fatalf("expect the ca config to have the data field %v, but got none", tlsutil.TLSPrivateCAKeyKey) + } + + // check if caConfigMap exists in k8s cluster. + caSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + // check if caSecret returned from GenerateCert is the same as the one that exists in the k8s. + if !reflect.DeepEqual(caSecret, caSecretFromCluster) { + t.Fatalf("expect %+v, but got %+v", caSecret, caSecretFromCluster) + } +} + +func verifyCaConfigMap(t *testing.T, caConfigMap *v1.ConfigMap, namespace string) { + // check if caConfigMap has the correct fields. + if caConfigMapAndSecretName != caConfigMap.Name { + t.Fatalf("expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name) + } + if namespace != caConfigMap.Namespace { + t.Fatalf("expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace) + } + if _, ok := caConfigMap.Data[tlsutil.TLSCACertKey]; !ok { + t.Fatalf("expect the ca config to have the data field %v, but got none", tlsutil.TLSCACertKey) + } + + // check if caConfigMap exists in k8s cluster. + caConfigMapFromCluster, err := framework.Global.KubeClient.CoreV1().ConfigMaps(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + // check if caConfigMap returned from GenerateCert is the same as the one that exists in the k8s. + if !reflect.DeepEqual(caConfigMap, caConfigMapFromCluster) { + t.Fatalf("expect %+v, but got %+v", caConfigMap, caConfigMapFromCluster) + } +} + +func verifyAppSecret(t *testing.T, appSecret *v1.Secret, namespace string) { // check if appSecret has the correct fields. if appSecretName != appSecret.Name { t.Fatalf("expect the secret name %v, but got %v", appSecretName, appSecret.Name) @@ -225,7 +262,7 @@ func TestOnlyCAExist(t *testing.T) { } // check if appSecret exists in k8s cluster. - appSecretFromCluster, err := f.KubeClient.CoreV1().Secrets(namespace).Get(appSecretName, metav1.GetOptions{}) + appSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(appSecretName, metav1.GetOptions{}) if err != nil { t.Fatal(err) } @@ -234,3 +271,25 @@ func TestOnlyCAExist(t *testing.T) { t.Fatalf("expect %+v, but got %+v", appSecret, appSecretFromCluster) } } + +// newDummyCR returns a dummy runtime object for the CR input of GenerateCert(). +func newDummyCR(namespace string) runtime.Object { + return &v1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: crKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: crName, + Namespace: namespace, + }, + } +} + +func newAppSvc(namespace string) *v1.Service { + return &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app-service", + Namespace: namespace, + }, + } +}