Skip to content
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
75 changes: 45 additions & 30 deletions pkg/tlsutil/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand All @@ -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)),
},
}
}
135 changes: 97 additions & 38 deletions test/e2e/tls_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

var (
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand All @@ -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,
},
}
}