@@ -63,6 +63,113 @@ type containerParameters struct {
6363 SecretName string
6464}
6565
66+ // getDefaultPodSecurityContext returns the default pod-level security context for MarkLogic StatefulSets
67+ func getDefaultPodSecurityContext () * corev1.PodSecurityContext {
68+ fsGroup := int64 (2 )
69+ fsGroupChangePolicy := corev1 .FSGroupChangeOnRootMismatch
70+ return & corev1.PodSecurityContext {
71+ FSGroup : & fsGroup ,
72+ FSGroupChangePolicy : & fsGroupChangePolicy ,
73+ }
74+ }
75+
76+ // getDefaultContainerSecurityContext returns the default container-level security context for MarkLogic containers
77+ // This enforces:
78+ // - runAsUser: 1000 (non-root user)
79+ // - runAsNonRoot: true (prevents running as root)
80+ // - allowPrivilegeEscalation: false (prevents privilege escalation)
81+ // - readOnlyRootFilesystem: true (makes root filesystem read-only)
82+ // - capabilities drop ALL (removes all Linux capabilities)
83+ func getDefaultContainerSecurityContext () * corev1.SecurityContext {
84+ runAsUser := int64 (1000 )
85+ runAsNonRoot := true
86+ allowPrivilegeEscalation := false
87+ readOnlyRootFilesystem := true
88+ return & corev1.SecurityContext {
89+ RunAsUser : & runAsUser ,
90+ RunAsNonRoot : & runAsNonRoot ,
91+ AllowPrivilegeEscalation : & allowPrivilegeEscalation ,
92+ ReadOnlyRootFilesystem : & readOnlyRootFilesystem ,
93+ Capabilities : & corev1.Capabilities {
94+ Drop : []corev1.Capability {"ALL" },
95+ },
96+ }
97+ }
98+
99+ // mergeSecurityContext merges user-provided SecurityContext with defaults
100+ // User-provided values take precedence over defaults
101+ func mergeSecurityContext (userContext , defaultContext * corev1.SecurityContext ) * corev1.SecurityContext {
102+ if userContext == nil {
103+ return defaultContext
104+ }
105+
106+ merged := defaultContext .DeepCopy ()
107+
108+ if userContext .RunAsUser != nil {
109+ merged .RunAsUser = userContext .RunAsUser
110+ }
111+ if userContext .RunAsNonRoot != nil {
112+ merged .RunAsNonRoot = userContext .RunAsNonRoot
113+ }
114+ if userContext .AllowPrivilegeEscalation != nil {
115+ merged .AllowPrivilegeEscalation = userContext .AllowPrivilegeEscalation
116+ }
117+ if userContext .ReadOnlyRootFilesystem != nil {
118+ merged .ReadOnlyRootFilesystem = userContext .ReadOnlyRootFilesystem
119+ }
120+ if userContext .Capabilities != nil {
121+ merged .Capabilities = userContext .Capabilities
122+ }
123+ if userContext .Privileged != nil {
124+ merged .Privileged = userContext .Privileged
125+ }
126+ if userContext .SELinuxOptions != nil {
127+ merged .SELinuxOptions = userContext .SELinuxOptions
128+ }
129+ if userContext .SeccompProfile != nil {
130+ merged .SeccompProfile = userContext .SeccompProfile
131+ }
132+
133+ return merged
134+ }
135+
136+ // mergePodSecurityContext merges user-provided PodSecurityContext with defaults
137+ // User-provided values take precedence over defaults
138+ func mergePodSecurityContext (userContext , defaultContext * corev1.PodSecurityContext ) * corev1.PodSecurityContext {
139+ if userContext == nil {
140+ return defaultContext
141+ }
142+
143+ merged := defaultContext .DeepCopy ()
144+
145+ if userContext .FSGroup != nil {
146+ merged .FSGroup = userContext .FSGroup
147+ }
148+ if userContext .FSGroupChangePolicy != nil {
149+ merged .FSGroupChangePolicy = userContext .FSGroupChangePolicy
150+ }
151+ if userContext .RunAsUser != nil {
152+ merged .RunAsUser = userContext .RunAsUser
153+ }
154+ if userContext .RunAsNonRoot != nil {
155+ merged .RunAsNonRoot = userContext .RunAsNonRoot
156+ }
157+ if userContext .SELinuxOptions != nil {
158+ merged .SELinuxOptions = userContext .SELinuxOptions
159+ }
160+ if userContext .SeccompProfile != nil {
161+ merged .SeccompProfile = userContext .SeccompProfile
162+ }
163+ if userContext .SupplementalGroups != nil {
164+ merged .SupplementalGroups = userContext .SupplementalGroups
165+ }
166+ if userContext .Sysctls != nil {
167+ merged .Sysctls = userContext .Sysctls
168+ }
169+
170+ return merged
171+ }
172+
66173func (oc * OperatorContext ) ReconcileStatefulset () (reconcile.Result , error ) {
67174 cr := oc .GetMarkLogicServer ()
68175 logger := oc .ReqLogger
@@ -202,6 +309,10 @@ func (oc *OperatorContext) createStatefulSet(statefulset *appsv1.StatefulSet, cr
202309}
203310
204311func generateStatefulSetsDef (stsMeta metav1.ObjectMeta , params statefulSetParameters , ownerDef metav1.OwnerReference , containerParams containerParameters ) * appsv1.StatefulSet {
312+ // Enforce default security contexts, merging with user-provided values
313+ // User values take precedence, but defaults ensure minimum security standards
314+ podSecurityContext := mergePodSecurityContext (containerParams .PodSecurityContext , getDefaultPodSecurityContext ())
315+
205316 statefulSet := & appsv1.StatefulSet {
206317 TypeMeta : generateTypeMeta ("StatefulSet" , "apps/v1" ),
207318 ObjectMeta : stsMeta ,
@@ -219,7 +330,7 @@ func generateStatefulSetsDef(stsMeta metav1.ObjectMeta, params statefulSetParame
219330 Spec : corev1.PodSpec {
220331 Containers : generateContainerDef ("marklogic-server" , containerParams ),
221332 TerminationGracePeriodSeconds : params .TerminationGracePeriodSeconds ,
222- SecurityContext : containerParams . PodSecurityContext ,
333+ SecurityContext : podSecurityContext ,
223334 Volumes : generateVolumes (stsMeta .Name , containerParams ),
224335 NodeSelector : params .NodeSelector ,
225336 Affinity : params .Affinity ,
@@ -319,14 +430,18 @@ func GetPodsForStatefulSet(namespace, name string) ([]corev1.Pod, error) {
319430}
320431
321432func generateContainerDef (name string , containerParams containerParameters ) []corev1.Container {
433+ // Enforce default container security context, merging with user-provided values
434+ // This ensures minimum security standards are always applied
435+ securityContext := mergeSecurityContext (containerParams .SecurityContext , getDefaultContainerSecurityContext ())
436+
322437 containerDef := []corev1.Container {
323438 {
324439 Name : name ,
325440 Image : containerParams .Image ,
326441 ImagePullPolicy : containerParams .ImagePullPolicy ,
327442 Env : getEnvironmentVariables (containerParams ),
328443 Lifecycle : getLifeCycle (),
329- SecurityContext : containerParams . SecurityContext ,
444+ SecurityContext : securityContext ,
330445 VolumeMounts : getVolumeMount (containerParams ),
331446 },
332447 }
0 commit comments