From 025362558ef8b63bf460e83f6f5e848a7483cf32 Mon Sep 17 00:00:00 2001 From: Romain Winieski Date: Thu, 18 Sep 2025 11:43:53 +0200 Subject: [PATCH] enforce automountServiceAccountToken=false --- api/v1/marklogiccluster_types.go | 2 ++ api/v1/marklogicgroup_types.go | 16 +++++++++------- api/v1/zz_generated.deepcopy.go | 10 ++++++++++ ...marklogic.progress.com_marklogicclusters.yaml | 3 +++ .../marklogic.progress.com_marklogicgroups.yaml | 3 +++ config/samples/complete.yaml | 2 +- config/samples/minimal-production.yaml | 2 +- config/samples/quick-start.yaml | 11 ++--------- pkg/k8sutil/marklogicServer.go | 6 ++++++ pkg/k8sutil/statefulset.go | 8 ++++++++ test/e2e/2_marklogic_cluster_test.go | 2 +- 11 files changed, 46 insertions(+), 19 deletions(-) diff --git a/api/v1/marklogiccluster_types.go b/api/v1/marklogiccluster_types.go index 5453f7b..121feb5 100644 --- a/api/v1/marklogiccluster_types.go +++ b/api/v1/marklogiccluster_types.go @@ -46,6 +46,8 @@ type MarklogicClusterSpec struct { // +kubebuilder:validation:XValidation:rule="self == oldSelf", message="ServiceAccountName can not be changed" // The name of the service account to assigned to the MarkLogic pods ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +kubebuilder:default:=false + AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"` // +kubebuilder:default:={enabled: true, size: "10Gi"} Persistence *Persistence `json:"persistence,omitempty"` Resources *corev1.ResourceRequirements `json:"resources,omitempty"` diff --git a/api/v1/marklogicgroup_types.go b/api/v1/marklogicgroup_types.go index 3ed957a..8eb432b 100644 --- a/api/v1/marklogicgroup_types.go +++ b/api/v1/marklogicgroup_types.go @@ -37,13 +37,15 @@ type MarklogicGroupSpec struct { // +kubebuilder:default:="progressofficial/marklogic-db:11.3.0-ubi-rootless" Image string `json:"image"` // +kubebuilder:default:="IfNotPresent" - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` - ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - Auth *AdminAuth `json:"auth,omitempty"` - ServiceAccountName string `json:"serviceAccountName,omitempty"` - Persistence *Persistence `json:"persistence,omitempty"` - Resources *corev1.ResourceRequirements `json:"resources,omitempty"` - TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + Auth *AdminAuth `json:"auth,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + // +kubebuilder:default:=false + AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"` + Persistence *Persistence `json:"persistence,omitempty"` + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` // +kubebuilder:validation:Enum=OnDelete;RollingUpdate // +kubebuilder:default:="OnDelete" UpdateStrategy appsv1.StatefulSetUpdateStrategyType `json:"updateStrategy,omitempty"` diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 8ab1994..e32b864 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -350,6 +350,11 @@ func (in *MarklogicClusterSpec) DeepCopyInto(out *MarklogicClusterSpec) { *out = new(AdminAuth) (*in).DeepCopyInto(*out) } + if in.AutomountServiceAccountToken != nil { + in, out := &in.AutomountServiceAccountToken, &out.AutomountServiceAccountToken + *out = new(bool) + **out = **in + } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence *out = new(Persistence) @@ -589,6 +594,11 @@ func (in *MarklogicGroupSpec) DeepCopyInto(out *MarklogicGroupSpec) { *out = new(AdminAuth) (*in).DeepCopyInto(*out) } + if in.AutomountServiceAccountToken != nil { + in, out := &in.AutomountServiceAccountToken, &out.AutomountServiceAccountToken + *out = new(bool) + **out = **in + } if in.Persistence != nil { in, out := &in.Persistence, &out.Persistence *out = new(Persistence) diff --git a/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml b/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml index 9d27b2f..1f2ff9e 100644 --- a/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml +++ b/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml @@ -3260,6 +3260,9 @@ spec: walletPassword: type: string type: object + automountServiceAccountToken: + default: false + type: boolean clusterDomain: default: cluster.local type: string diff --git a/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml b/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml index 7ec9c3b..9e95819 100644 --- a/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml +++ b/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml @@ -3265,6 +3265,9 @@ spec: walletPassword: type: string type: object + automountServiceAccountToken: + default: false + type: boolean bootstrapHost: type: string clusterDomain: diff --git a/config/samples/complete.yaml b/config/samples/complete.yaml index 8c5d325..35015ee 100644 --- a/config/samples/complete.yaml +++ b/config/samples/complete.yaml @@ -5,7 +5,7 @@ metadata: namespace: prod annotations: {} spec: - image: "progressofficial/marklogic-db:11.3.1-ubi-rootless-2.1.1" + image: "progressofficial/marklogic-db:11.3.1-ubi-rootless-2.2.1" imagePullPolicy: IfNotPresent imagePullSecrets: - name: regcred diff --git a/config/samples/minimal-production.yaml b/config/samples/minimal-production.yaml index 95fa4bb..64cdd38 100644 --- a/config/samples/minimal-production.yaml +++ b/config/samples/minimal-production.yaml @@ -5,7 +5,7 @@ metadata: name: ml-cluster namespace: prod spec: - image: "progressofficial/marklogic-db:11.3.1-ubi-rootless-2.1.1" + image: "progressofficial/marklogic-db:11.3.1-ubi-rootless-2.2.1" ## It is recommended to use Kubernetes secrets to store the admin credentials ## To create a secret, run the following command in the same namespace as the CR: ## kubectl create secret generic admincreds --from-literal=username=admin --from-literal=password=admin diff --git a/config/samples/quick-start.yaml b/config/samples/quick-start.yaml index 9bf2882..b509203 100644 --- a/config/samples/quick-start.yaml +++ b/config/samples/quick-start.yaml @@ -4,24 +4,17 @@ metadata: name: single-node annotations: {} spec: - image: "progressofficial/marklogic-db:11.3.0-ubi-rootless" + image: "progressofficial/marklogic-db:11.3.1-ubi-rootless-2.2.1" ## It is recommended to use Kubernetes secrets to store the admin credentials ## To create a secret, run the following command in the same namespace as the CR: ## kubectl create secret generic admincreds --from-literal=username=admin --from-literal=password=admin ## If you do not provide the admin credentials, the operator will generate a secret for you containing admin credentials persistence: - enabled: true + enabled: false size: 10Gi markLogicGroups: - replicas: 1 name: node groupConfig: name: node - resources: - requests: - memory: "4Gi" - cpu: 2000m - limits: - memory: "4Gi" - cpu: 2000m isBootstrap: true \ No newline at end of file diff --git a/pkg/k8sutil/marklogicServer.go b/pkg/k8sutil/marklogicServer.go index 5e2997e..838fec5 100644 --- a/pkg/k8sutil/marklogicServer.go +++ b/pkg/k8sutil/marklogicServer.go @@ -19,6 +19,7 @@ type MarkLogicGroupParameters struct { Replicas *int32 Name string ServiceAccountName string + AutomountServiceAccountToken *bool Labels map[string]string Annotations map[string]string GroupConfig *marklogicv1.GroupConfig @@ -124,6 +125,7 @@ func (cc *ClusterContext) GenerateMarkLogicGroupDef(cr *marklogicv1.MarklogicClu GroupConfig: params.GroupConfig, Auth: params.Auth, ServiceAccountName: params.ServiceAccountName, + AutomountServiceAccountToken: params.AutomountServiceAccountToken, Image: params.Image, Labels: params.Labels, Annotations: params.Annotations, @@ -261,6 +263,9 @@ func generateMarkLogicClusterParams(cr *marklogicv1.MarklogicCluster) *MarkLogic } func generateMarkLogicGroupParams(cr *marklogicv1.MarklogicCluster, index int, clusterParams *MarkLogicClusterParameters) *MarkLogicGroupParameters { + // Always enforce automountServiceAccountToken to false for security + falseValue := false + markLogicGroupParameters := &MarkLogicGroupParameters{ Replicas: cr.Spec.MarkLogicGroups[index].Replicas, Name: cr.Spec.MarkLogicGroups[index].Name, @@ -273,6 +278,7 @@ func generateMarkLogicGroupParams(cr *marklogicv1.MarklogicCluster, index int, c ImagePullSecrets: clusterParams.ImagePullSecrets, Auth: clusterParams.Auth, ServiceAccountName: clusterParams.ServiceAccountName, + AutomountServiceAccountToken: &falseValue, // Always false for security License: clusterParams.License, Persistence: clusterParams.Persistence, TerminationGracePeriodSeconds: clusterParams.TerminationGracePeriodSeconds, diff --git a/pkg/k8sutil/statefulset.go b/pkg/k8sutil/statefulset.go index 080b805..509bcef 100644 --- a/pkg/k8sutil/statefulset.go +++ b/pkg/k8sutil/statefulset.go @@ -32,6 +32,7 @@ type statefulSetParameters struct { ImagePullSecrets []corev1.LocalObjectReference AdditionalVolumeClaimTemplates *[]corev1.PersistentVolumeClaim ServiceAccountName string + AutomountServiceAccountToken *bool } type containerParameters struct { @@ -238,6 +239,9 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame if params.ServiceAccountName != "" { statefulSet.Spec.Template.Spec.ServiceAccountName = params.ServiceAccountName } + if params.AutomountServiceAccountToken != nil { + statefulSet.Spec.Template.Spec.AutomountServiceAccountToken = params.AutomountServiceAccountToken + } if containerParams.Tls != nil && containerParams.Tls.EnableOnDefaultAppServers { copyCertsVM := []corev1.VolumeMount{ { @@ -349,10 +353,14 @@ func generateContainerDef(name string, containerParams containerParameters) []co } func generateStatefulSetsParams(cr *marklogicv1.MarklogicGroup) statefulSetParameters { + // Always enforce automountServiceAccountToken to false for security + falseValue := false + params := statefulSetParameters{ Replicas: cr.Spec.Replicas, Name: cr.Spec.Name, ServiceAccountName: cr.Spec.ServiceAccountName, + AutomountServiceAccountToken: &falseValue, // Always false for security TerminationGracePeriodSeconds: cr.Spec.TerminationGracePeriodSeconds, UpdateStrategy: cr.Spec.UpdateStrategy, NodeSelector: cr.Spec.NodeSelector, diff --git a/test/e2e/2_marklogic_cluster_test.go b/test/e2e/2_marklogic_cluster_test.go index f05c448..f021866 100644 --- a/test/e2e/2_marklogic_cluster_test.go +++ b/test/e2e/2_marklogic_cluster_test.go @@ -319,7 +319,7 @@ func TestMarklogicCluster(t *testing.T) { } // Exponential backoff: 1s, 2s, 4s, 8s, 16s time.Sleep(time.Duration(1<<(attempt-1)) * time.Second) - } + } t.Logf("Query datasource response: %s", output) // Verify MarkLogic logs in Grafana using Loki and Fluent Bit if strings.Contains(string(output), "Starting MarkLogic Server") {