Skip to content

Commit

Permalink
[CN-807] Remove separate secret for mTLS
Browse files Browse the repository at this point in the history
  • Loading branch information
dzeromski-hazelcast committed Apr 6, 2023
1 parent 07d1600 commit f4ef196
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Expand Up @@ -82,7 +82,7 @@ jobs:
runs-on: ubuntu-latest
permissions: {}
if: ( !cancelled() && github.event_name == 'pull_request' )
timeout-minutes: 10
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
21 changes: 6 additions & 15 deletions controllers/hazelcast/fakes_test.go
Expand Up @@ -2,7 +2,6 @@ package hazelcast

import (
"context"
"errors"
"fmt"
"net"
"net/http"
Expand All @@ -22,7 +21,6 @@ import (

hazelcastv1alpha1 "github.com/hazelcast/hazelcast-platform-operator/api/v1alpha1"
hzclient "github.com/hazelcast/hazelcast-platform-operator/internal/hazelcast-client"
n "github.com/hazelcast/hazelcast-platform-operator/internal/naming"
codecTypes "github.com/hazelcast/hazelcast-platform-operator/internal/protocol/types"
)

Expand Down Expand Up @@ -80,26 +78,19 @@ type fakeHttpClientRegistry struct {
clients sync.Map
}

func (hr *fakeHttpClientRegistry) Create(_ context.Context, _ client.Client, ns string) (*http.Client, error) {
if v, ok := hr.clients.Load(types.NamespacedName{Name: n.MTLSCertSecretName, Namespace: ns}); ok {
return v.(*http.Client), nil
}
return nil, errors.New("no client found")
}

func (hr *fakeHttpClientRegistry) Get(ns string) (*http.Client, bool) {
if v, ok := hr.clients.Load(types.NamespacedName{Name: n.MTLSCertSecretName, Namespace: ns}); ok {
func (hr *fakeHttpClientRegistry) Get(ctx context.Context, name types.NamespacedName) (*http.Client, bool) {
if v, ok := hr.clients.Load(name); ok {
return v.(*http.Client), ok
}
return nil, false
}

func (hr *fakeHttpClientRegistry) Delete(ns string) {
hr.clients.Delete(types.NamespacedName{Name: n.MTLSCertSecretName, Namespace: ns})
func (hr *fakeHttpClientRegistry) Delete(name types.NamespacedName) {
hr.clients.Delete(name)
}

func (hr *fakeHttpClientRegistry) Set(ns string, cl *http.Client) {
hr.clients.Store(types.NamespacedName{Name: n.MTLSCertSecretName, Namespace: ns}, cl)
func (hr *fakeHttpClientRegistry) Set(name types.NamespacedName, cl *http.Client) {
hr.clients.Store(name, cl)
}

type fakeHzClient struct {
Expand Down
5 changes: 0 additions & 5 deletions controllers/hazelcast/hazelcast_controller.go
Expand Up @@ -189,11 +189,6 @@ func (r *HazelcastReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return r.update(ctx, h, recoptions.Error(err), withHzFailedPhase(err.Error()))
}

err = r.reconcileMTLSSecret(ctx, h)
if err != nil {
return r.update(ctx, h, recoptions.Error(err), withHzFailedPhase(err.Error()))
}

if err = r.reconcileStatefulset(ctx, h, logger); err != nil {
// Conflicts are expected and will be handled on the next reconcile loop, no need to error out here
if errors.IsConflict(err) {
Expand Down
52 changes: 13 additions & 39 deletions controllers/hazelcast/hazelcast_resources.go
Expand Up @@ -35,6 +35,7 @@ import (
hazelcastv1alpha1 "github.com/hazelcast/hazelcast-platform-operator/api/v1alpha1"
"github.com/hazelcast/hazelcast-platform-operator/internal/config"
hzclient "github.com/hazelcast/hazelcast-platform-operator/internal/hazelcast-client"
"github.com/hazelcast/hazelcast-platform-operator/internal/mtls"
n "github.com/hazelcast/hazelcast-platform-operator/internal/naming"
"github.com/hazelcast/hazelcast-platform-operator/internal/platform"
"github.com/hazelcast/hazelcast-platform-operator/internal/protocol/codec"
Expand Down Expand Up @@ -592,9 +593,16 @@ func (r *HazelcastReconciler) reconcileSecret(ctx context.Context, h *hazelcastv
if err != nil {
return err
}
mtlsCert, mtlsKey, err := mtls.NewCertificateAuthority()
if err != nil {
return err
}
cm.Data = map[string][]byte{
"hazelcast.yaml": config,
"hazelcast.jks": keystore,
"ca.crt": mtlsCert,
"tls.crt": mtlsCert,
"tls.key": mtlsKey,
}
return nil
})
Expand Down Expand Up @@ -1324,27 +1332,6 @@ func createWanReplicationConfig(publisherId string, wr hazelcastv1alpha1.WanRepl
return cfg
}

func (r *HazelcastReconciler) reconcileMTLSSecret(ctx context.Context, h *hazelcastv1alpha1.Hazelcast) error {
_, err := r.mtlsClientRegistry.Create(ctx, r.Client, h.Namespace)
if err != nil {
return err
}
secret := &v1.Secret{}
secretName := types.NamespacedName{Name: n.MTLSCertSecretName, Namespace: h.Namespace}
err = r.Client.Get(ctx, secretName, secret)
if err != nil {
return err
}
err = controllerutil.SetControllerReference(h, secret, r.Scheme)
if err != nil {
return err
}
_, err = util.CreateOrUpdate(ctx, r.Client, secret, func() error {
return nil
})
return err
}

func (r *HazelcastReconciler) reconcileStatefulset(ctx context.Context, h *hazelcastv1alpha1.Hazelcast, logger logr.Logger) error {
ls := labels(h)
sts := &appsv1.StatefulSet{
Expand Down Expand Up @@ -1509,21 +1496,21 @@ func sidecarContainer(h *hazelcastv1alpha1.Hazelcast) v1.Container {
Env: []v1.EnvVar{
{
Name: "BACKUP_CA",
Value: path.Join(n.MTLSCertPath, "ca.crt"),
Value: path.Join(n.HazelcastMountPath, "ca.crt"),
},
{
Name: "BACKUP_CERT",
Value: path.Join(n.MTLSCertPath, "tls.crt"),
Value: path.Join(n.HazelcastMountPath, "tls.crt"),
},
{
Name: "BACKUP_KEY",
Value: path.Join(n.MTLSCertPath, "tls.key"),
Value: path.Join(n.HazelcastMountPath, "tls.key"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: n.MTLSCertSecretName,
MountPath: n.MTLSCertPath,
Name: n.HazelcastStorageName,
MountPath: n.HazelcastMountPath,
},
},
SecurityContext: containerSecurityContext(),
Expand Down Expand Up @@ -1765,7 +1752,6 @@ func volumes(h *hazelcastv1alpha1.Hazelcast) []v1.Volume {
},
},
userCodeAgentVolume(h),
tlsVolume(h),
}

if h.Spec.UserCodeDeployment.IsConfigMapEnabled() {
Expand Down Expand Up @@ -1801,18 +1787,6 @@ func tmpDirVolume() v1.Volume {
}
}

func tlsVolume(_ *hazelcastv1alpha1.Hazelcast) v1.Volume {
return v1.Volume{
Name: n.MTLSCertSecretName,
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: n.MTLSCertSecretName,
DefaultMode: &[]int32{420}[0],
},
},
}
}

func userCodeConfigMapVolumes(h *hazelcastv1alpha1.Hazelcast) []corev1.Volume {
var vols []corev1.Volume
for _, cm := range h.Spec.UserCodeDeployment.ConfigMaps {
Expand Down
2 changes: 1 addition & 1 deletion controllers/hazelcast/hot_backup_controller.go
Expand Up @@ -281,7 +281,7 @@ func (r *HotBackupReconciler) startBackup(ctx context.Context, backupName types.
backupUUIDs := make([]string, len(b.Members()))
// for each member monitor and upload backup if needed
g, groupCtx = errgroup.WithContext(ctx)
mtlsClient, ok := r.mtlsClientRegistry.Get(hazelcastName.Namespace)
mtlsClient, ok := r.mtlsClientRegistry.Get(ctx, hazelcastName)
if !ok {
returnErr := errors.New("failed to get MTLS client")
return r.updateStatus(ctx, backupName, recoptions.Error(returnErr),
Expand Down
10 changes: 5 additions & 5 deletions controllers/hazelcast/hot_backup_controller_test.go
Expand Up @@ -42,7 +42,7 @@ func TestHotBackupReconciler_shouldBeSuccessful(t *testing.T) {
cr := &fakeHzClientRegistry{}
sr := &fakeHzStatusServiceRegistry{}
hr := &fakeHttpClientRegistry{}
hr.Set(nn.Namespace, &http.Client{})
hr.Set(nn, &http.Client{})
cr.Set(nn, &fakeHzClient)
sr.Set(nn, &fakeHzStatusService)

Expand Down Expand Up @@ -73,7 +73,7 @@ func TestHotBackupReconciler_shouldSetStatusToFailedWhenHbCallFails(t *testing.T
sr := &fakeHzStatusServiceRegistry{}
cr := &fakeHzClientRegistry{}
hr := &fakeHttpClientRegistry{}
hr.Set(nn.Namespace, &http.Client{})
hr.Set(nn, &http.Client{})
sr.Set(nn, &fakeHzStatusService)
cr.Set(nn, &fakeHzClient)

Expand All @@ -99,7 +99,7 @@ func TestHotBackupReconciler_shouldSetStatusToFailedWhenTimedMemberStateFails(t
sr := &fakeHzStatusServiceRegistry{}
cr := &fakeHzClientRegistry{}
hr := &fakeHttpClientRegistry{}
hr.Set(nn.Namespace, &http.Client{})
hr.Set(nn, &http.Client{})
sr.Set(nn, &fakeHzStatusService)
cr.Set(nn, &fakeHzClient)

Expand Down Expand Up @@ -135,7 +135,7 @@ func TestHotBackupReconciler_shouldNotTriggerHotBackupTwice(t *testing.T) {
sr := &fakeHzStatusServiceRegistry{}
cr := &fakeHzClientRegistry{}
hr := &fakeHttpClientRegistry{}
hr.Set(nn.Namespace, &http.Client{})
hr.Set(nn, &http.Client{})
sr.Set(nn, &fakeHzStatusService)
cr.Set(nn, &fakeHzClient)

Expand Down Expand Up @@ -173,7 +173,7 @@ func TestHotBackupReconciler_shouldCancelContextIfHotbackupCRIsDeleted(t *testin
cr := &fakeHzClientRegistry{}
sr := &fakeHzStatusServiceRegistry{}
hr := &fakeHttpClientRegistry{}
hr.Set(nn.Namespace, &http.Client{})
hr.Set(nn, &http.Client{})
cr.Set(nn, &fakeHzClient)
sr.Set(nn, &fakeHzStatusService)

Expand Down
7 changes: 4 additions & 3 deletions controllers/hazelcast/wanreplication_controller.go
Expand Up @@ -326,10 +326,11 @@ func (r *WanReplicationReconciler) checkConnectivity(ctx context.Context, req ct
hzResourceName = m.Spec.HazelcastResourceName
}

statusService, ok := r.statusServiceRegistry.Get(types.NamespacedName{
name := types.NamespacedName{
Namespace: req.Namespace,
Name: hzResourceName,
})
}
statusService, ok := r.statusServiceRegistry.Get(name)
if !ok {
return fmt.Errorf("get Hazelcast Status Service failed for Hazelcast CR %s", hzResourceName)
}
Expand All @@ -340,7 +341,7 @@ func (r *WanReplicationReconciler) checkConnectivity(ctx context.Context, req ct
memberAddresses = append(memberAddresses, v.Address)
}

mtlsClient, ok := r.mtlsClientRegistry.Get(req.Namespace)
mtlsClient, ok := r.mtlsClientRegistry.Get(ctx, name)
if !ok {
return errors.New("failed to get MTLS client")
}
Expand Down
49 changes: 7 additions & 42 deletions internal/mtls/client.go
Expand Up @@ -16,8 +16,6 @@ import (
"time"

v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand All @@ -32,23 +30,10 @@ var (
)

func NewClient(ctx context.Context, kubeClient client.Client, secretName types.NamespacedName) (*http.Client, error) {
if secretName.Namespace == "" {
// if not specified we always use default namespace
// as a fallback for running operator locally
secretName.Namespace = "default"
}
secret := &v1.Secret{}
err := kubeClient.Get(ctx, secretName, secret)
if err != nil {
// exit for errors other than not found
if !kerrors.IsNotFound(err) {
return nil, err
}
// generate new secret with tls cert
secret, err = createSecret(ctx, kubeClient, secretName)
if err != nil {
return nil, err
}
return nil, err
}

// parse secret data
Expand Down Expand Up @@ -90,23 +75,7 @@ func NewClient(ctx context.Context, kubeClient client.Client, secretName types.N
return c, nil
}

func createSecret(ctx context.Context, kubeClient client.Client, secretName types.NamespacedName) (*v1.Secret, error) {
ca, err := generateCA()
if err != nil {
return nil, fmt.Errorf("mtls: %w", err)
}
secret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName.Name,
Namespace: secretName.Namespace,
},
Type: v1.SecretTypeTLS,
Data: ca,
}
return secret, kubeClient.Create(ctx, secret)
}

func generateCA() (map[string][]byte, error) {
func NewCertificateAuthority() (cert []byte, key []byte, err error) {
ca := &x509.Certificate{
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: pkix.Name{
Expand All @@ -123,12 +92,12 @@ func generateCA() (map[string][]byte, error) {

privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, err
return nil, nil, err
}

rawCrt, err := x509.CreateCertificate(rand.Reader, ca, ca, &privateKey.PublicKey, privateKey)
if err != nil {
return nil, err
return nil, nil, err
}

var crtPEM bytes.Buffer
Expand All @@ -137,7 +106,7 @@ func generateCA() (map[string][]byte, error) {
Bytes: rawCrt,
})
if err != nil {
return nil, err
return nil, nil, err
}

var keyPEM bytes.Buffer
Expand All @@ -146,12 +115,8 @@ func generateCA() (map[string][]byte, error) {
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
if err != nil {
return nil, err
return nil, nil, err
}

return map[string][]byte{
TLSCAKey: crtPEM.Bytes(),
v1.TLSCertKey: crtPEM.Bytes(),
v1.TLSPrivateKeyKey: keyPEM.Bytes(),
}, nil
return crtPEM.Bytes(), keyPEM.Bytes(), nil
}

0 comments on commit f4ef196

Please sign in to comment.