Skip to content

Commit

Permalink
[CN-867] Make TLS secretName a required property
Browse files Browse the repository at this point in the history
  • Loading branch information
dzeromski-hazelcast committed Jun 7, 2023
1 parent 8fa2bb4 commit f36e01f
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 55 deletions.
6 changes: 4 additions & 2 deletions api/v1alpha1/hazelcast_types.go
Expand Up @@ -164,7 +164,7 @@ type HazelcastSpec struct {

// Hazelcast TLS configuration
// +optional
TLS TLS `json:"tls,omitempty"`
TLS *TLS `json:"tls,omitempty"`

// Hazelcast serialization configuration
// +optional
Expand Down Expand Up @@ -1051,7 +1051,9 @@ const (

type TLS struct {
// Name of the secret with TLS certificate and key.
SecretName string `json:"secretName,omitempty"`
// +kubebuilder:validation:Required
// +required
SecretName string `json:"secretName"`

// Mutual authentication configuration. It’s None by default which
// means the client side of connection is not authenticated.
Expand Down
35 changes: 22 additions & 13 deletions api/v1alpha1/hazelcast_validation.go
Expand Up @@ -149,20 +149,29 @@ func validateLicense(h *Hazelcast) *field.Error {
}

func validateTLS(h *Hazelcast) *field.Error {
// make sure secret exists
if h.Spec.TLS.SecretName != "" {
secretName := types.NamespacedName{
Name: h.Spec.TLS.SecretName,
Namespace: h.Namespace,
}
// skip validation if TLS is not set
if h.Spec.TLS == nil {
return nil
}

var secret corev1.Secret
err := kubeclient.Get(context.Background(), secretName, &secret)
if kerrors.IsNotFound(err) {
// we care only about not found error
return field.NotFound(field.NewPath("spec").Child("tls"),
"Hazelcast Enterprise TLS Secret is not found")
}
p := field.NewPath("spec").Child("tls").Child("secretName")

// if user skipped validation secretName can be empty
if h.Spec.TLS.SecretName == "" {
return field.NotFound(p, "Hazelcast Enterprise TLS Secret name is empty")
}

// check if secret exists
secretName := types.NamespacedName{
Name: h.Spec.TLS.SecretName,
Namespace: h.Namespace,
}

var secret corev1.Secret
err := kubeclient.Get(context.Background(), secretName, &secret)
if kerrors.IsNotFound(err) {
// we care only about not found error
return field.NotFound(p, "Hazelcast Enterprise TLS Secret not found")
}

return nil
Expand Down
58 changes: 45 additions & 13 deletions api/v1alpha1/management_center_validation.go
@@ -1,29 +1,39 @@
package v1alpha1

import (
"context"
"encoding/json"
"fmt"
"reflect"

corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"

"github.com/hazelcast/hazelcast-platform-operator/internal/kubeclient"
n "github.com/hazelcast/hazelcast-platform-operator/internal/naming"
"github.com/hazelcast/hazelcast-platform-operator/internal/platform"
)

func ValidateManagementCenterSpec(mc *ManagementCenter) error {
currentErrs := ValidateManagementCenterSpecCurrent(mc)
updateErrs := ValidateManagementCenterSpecUpdate(mc)
allErrs := append(currentErrs, updateErrs...)
if len(allErrs) == 0 {
var errors field.ErrorList
errors = append(errors, ValidateManagementCenterSpecCurrent(mc)...)
errors = append(errors, ValidateManagementCenterSpecUpdate(mc)...)
for i := range mc.Spec.HazelcastClusters {
err := validateClusterConfigTLS(&mc.Spec.HazelcastClusters[i], mc.Namespace)
if err != nil {
errors = append(errors, err)
}
}
if len(errors) == 0 {
return nil
}
return kerrors.NewInvalid(schema.GroupKind{Group: "hazelcast.com", Kind: "ManagementCenter"}, mc.Name, allErrs)
return kerrors.NewInvalid(schema.GroupKind{Group: "hazelcast.com", Kind: "ManagementCenter"}, mc.Name, errors)
}

func ValidateManagementCenterSpecCurrent(mc *ManagementCenter) []*field.Error {
func ValidateManagementCenterSpecCurrent(mc *ManagementCenter) field.ErrorList {
var allErrs field.ErrorList

if mc.Spec.ExternalConnectivity.Route.IsEnabled() {
Expand All @@ -32,13 +42,10 @@ func ValidateManagementCenterSpecCurrent(mc *ManagementCenter) []*field.Error {
"Route can only be enabled in OpenShift environments."))
}
}
if len(allErrs) == 0 {
return nil
}
return allErrs
}

func ValidateManagementCenterSpecUpdate(mc *ManagementCenter) []*field.Error {
func ValidateManagementCenterSpecUpdate(mc *ManagementCenter) field.ErrorList {
last, ok := mc.ObjectMeta.Annotations[n.LastSuccessfulSpecAnnotation]
if !ok {
return nil
Expand All @@ -52,7 +59,7 @@ func ValidateManagementCenterSpecUpdate(mc *ManagementCenter) []*field.Error {
return ValidateNotUpdatableMcPersistenceFields(mc.Spec.Persistence, parsed.Persistence)
}

func ValidateNotUpdatableMcPersistenceFields(current, last MCPersistenceConfiguration) []*field.Error {
func ValidateNotUpdatableMcPersistenceFields(current, last MCPersistenceConfiguration) field.ErrorList {
var allErrs field.ErrorList

if !reflect.DeepEqual(current.Enabled, last.Enabled) {
Expand All @@ -71,9 +78,34 @@ func ValidateNotUpdatableMcPersistenceFields(current, last MCPersistenceConfigur
allErrs = append(allErrs,
field.Forbidden(field.NewPath("spec").Child("persistence").Child("size"), "field cannot be updated"))
}
return allErrs
}

if len(allErrs) == 0 {
func validateClusterConfigTLS(config *HazelcastClusterConfig, namespace string) *field.Error {
// skip validation if TLS is not set
if config.TLS == nil {
return nil
}
return allErrs

p := field.NewPath("spec").Child("hazelcastClusters").Child("tls").Child("secretName")

// if user skipped validation secretName can be empty
if config.TLS.SecretName == "" {
return field.NotFound(p, "Management Center Cluster config TLS Secret name is empty")
}

// check if secret exists
secretName := types.NamespacedName{
Name: config.TLS.SecretName,
Namespace: namespace,
}

var secret corev1.Secret
err := kubeclient.Get(context.Background(), secretName, &secret)
if kerrors.IsNotFound(err) {
// we care only about not found error
return field.NotFound(p, "Management Center Cluster config TLS Secret not found")
}

return nil
}
2 changes: 1 addition & 1 deletion api/v1alpha1/managementcenter_types.go
Expand Up @@ -83,7 +83,7 @@ type HazelcastClusterConfig struct {
// TLS client configuration.
// +kubebuilder:default:={}
// +optional
TLS TLS `json:"tls,omitempty"`
TLS *TLS `json:"tls,omitempty"`
}

// ExternalConnectivityConfiguration defines how to expose Management Center pod.
Expand Down
5 changes: 0 additions & 5 deletions api/v1alpha1/managementcenter_webhook.go
Expand Up @@ -16,9 +16,6 @@ func (r *ManagementCenter) SetupWebhookWithManager(mgr ctrl.Manager) error {
Complete()
}

// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+kubebuilder:webhook:path=/validate-hazelcast-com-v1alpha1-managementcenter,mutating=false,failurePolicy=ignore,sideEffects=None,groups=hazelcast.com,resources=managementcenters,verbs=create;update,versions=v1alpha1,name=vmanagementcenter.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &ManagementCenter{}
Expand All @@ -44,7 +41,5 @@ func (r *ManagementCenter) ValidateUpdate(old runtime.Object) error {
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *ManagementCenter) ValidateDelete() error {
managementcenterlog.Info("validate delete", "name", r.Name)

// TODO(user): fill in your validation logic upon object deletion.
return nil
}
16 changes: 13 additions & 3 deletions api/v1alpha1/zz_generated.deepcopy.go

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

2 changes: 2 additions & 0 deletions config/crd/bases/hazelcast.com_hazelcasts.yaml
Expand Up @@ -1831,6 +1831,8 @@ spec:
secretName:
description: Name of the secret with TLS certificate and key.
type: string
required:
- secretName
type: object
userCodeDeployment:
description: User Codes to Download into CLASSPATH
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/hazelcast.com_managementcenters.yaml
Expand Up @@ -130,6 +130,8 @@ spec:
description: Name of the secret with TLS certificate and
key.
type: string
required:
- secretName
type: object
required:
- address
Expand Down
4 changes: 2 additions & 2 deletions controllers/hazelcast/hazelcast_resources.go
Expand Up @@ -859,7 +859,7 @@ func hazelcastBasicConfig(h *hazelcastv1alpha1.Hazelcast) config.Hazelcast {
DataAccessEnabled: h.Spec.ManagementCenterConfig.DataAccessEnabled,
}

if h.Spec.TLS.SecretName != "" {
if h.Spec.TLS != nil && h.Spec.TLS.SecretName != "" {
var (
jksPath = path.Join(n.HazelcastMountPath, "hazelcast.jks")
password = "hazelcast"
Expand Down Expand Up @@ -922,7 +922,7 @@ func hazelcastKeystore(ctx context.Context, c client.Client, h *hazelcastv1alpha
store = keystore.New()
password = []byte("hazelcast")
)
if h.Spec.TLS.SecretName != "" {
if h.Spec.TLS != nil && h.Spec.TLS.SecretName != "" {
cert, key, err := loadTLSKeyPair(ctx, c, h)
if err != nil {
return nil, err
Expand Down
13 changes: 7 additions & 6 deletions controllers/managementcenter/managementcenter_resources.go
Expand Up @@ -325,12 +325,13 @@ func (r *ManagementCenterReconciler) reconcileSecret(ctx context.Context, mc *ha
opResult, err := util.CreateOrUpdateForce(ctx, r.Client, secret, func() error {
files := make(map[string][]byte)
for _, cluster := range mc.Spec.HazelcastClusters {
keystore, err := hazelcastKeystore(ctx, r.Client, mc, cluster.TLS.SecretName)
if err != nil {
return err
if cluster.TLS != nil {
keystore, err := hazelcastKeystore(ctx, r.Client, mc, cluster.TLS.SecretName)
if err != nil {
return err
}
files[cluster.Name+".jks"] = keystore
}
files[cluster.Name+".jks"] = keystore

clientConfig, err := hazelcastClientConfig(ctx, r.Client, &cluster)
if err != nil {
return err
Expand Down Expand Up @@ -575,7 +576,7 @@ func hazelcastClientConfig(ctx context.Context, c client.Client, config *hazelca
},
}

if config.TLS.SecretName != "" {
if config.TLS != nil && config.TLS.SecretName != "" {
clientConfig.Network.SSL = SSL{
Enabled: "true",
FactoryClassName: "com.hazelcast.nio.ssl.BasicSSLContextFactory",
Expand Down
Expand Up @@ -2089,6 +2089,8 @@ spec:
secretName:
description: Name of the secret with TLS certificate and key.
type: string
required:
- secretName
type: object
userCodeDeployment:
description: User Codes to Download into CLASSPATH
Expand Down Expand Up @@ -2629,6 +2631,8 @@ spec:
description: Name of the secret with TLS certificate and
key.
type: string
required:
- secretName
type: object
required:
- address
Expand Down
2 changes: 1 addition & 1 deletion internal/hazelcast-client/client_registry.go
Expand Up @@ -42,7 +42,7 @@ func (cr *HazelcastClientRegistry) GetOrCreate(ctx context.Context, nn types.Nam
}
var pool *x509.CertPool
var certificate *tls.Certificate
if h.Spec.TLS.SecretName != "" {
if h.Spec.TLS != nil && h.Spec.TLS.SecretName != "" {
var s v1.Secret
err = cr.K8sClient.Get(ctx, types.NamespacedName{Name: h.Spec.TLS.SecretName, Namespace: h.Namespace}, &s)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/phonehome/phonehome.go
Expand Up @@ -245,7 +245,7 @@ func (phm *PhoneHomeData) fillHazelcastMetrics(cl client.Client, hzClientRegistr
phm.UserCodeDeployment.addUsageMetrics(&hz.Spec.UserCodeDeployment)
phm.JVMConfigUsage.addUsageMetrics(hz.Spec.JVM)
phm.JetEngine.addUsageMetrics(hz.Spec.JetEngineConfiguration)
phm.TLS.addUsageMetrics(&hz.Spec.TLS)
phm.TLS.addUsageMetrics(hz.Spec.TLS)
createdMemberCount += int(*hz.Spec.ClusterSize)
executorServiceCount += len(hz.Spec.ExecutorServices) + len(hz.Spec.DurableExecutorServices) + len(hz.Spec.ScheduledExecutorServices)
highAvailabilityModes = append(highAvailabilityModes, string(hz.Spec.HighAvailabilityMode))
Expand Down Expand Up @@ -379,7 +379,7 @@ func (je *JetEngine) addUsageMetrics(jec hazelcastv1alpha1.JetEngineConfiguratio
}

func (t *TLS) addUsageMetrics(tls *hazelcastv1alpha1.TLS) {
if tls.SecretName == "" {
if tls == nil {
return
}

Expand Down
4 changes: 2 additions & 2 deletions test/e2e/config/hazelcast/config.go
Expand Up @@ -423,7 +423,7 @@ var (
Repository: repo(ee),
Version: *hazelcastVersion,
LicenseKeySecretName: licenseKey(ee),
TLS: hazelcastv1alpha1.TLS{
TLS: &hazelcastv1alpha1.TLS{
SecretName: lk.Name + "-tls",
},
},
Expand All @@ -442,7 +442,7 @@ var (
Repository: repo(ee),
Version: *hazelcastVersion,
LicenseKeySecretName: licenseKey(ee),
TLS: hazelcastv1alpha1.TLS{
TLS: &hazelcastv1alpha1.TLS{
SecretName: lk.Name + "-mtls",
MutualAuthentication: hazelcastv1alpha1.MutualAuthenticationRequired,
},
Expand Down

0 comments on commit f36e01f

Please sign in to comment.