Skip to content

Commit 3afecb1

Browse files
authored
Fix the default Coherence Pod security context to be compatible with OpenShift and to be configurable. (#770)
1 parent 29668bb commit 3afecb1

19 files changed

+554
-27
lines changed

api/v1/coherenceresourcespec_types.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -833,21 +833,11 @@ func (in *CoherenceResourceSpec) GetImagePullSecrets() []corev1.LocalObjectRefer
833833
// GetSecurityContext returns the Pod security context to use.
834834
func (in *CoherenceResourceSpec) GetSecurityContext() *corev1.PodSecurityContext {
835835
if in == nil || in.SecurityContext == nil {
836-
return DefaultSecurityContext()
836+
return operator.DefaultSecurityContext()
837837
}
838838
return in.SecurityContext
839839
}
840840

841-
func DefaultSecurityContext() *corev1.PodSecurityContext {
842-
return &corev1.PodSecurityContext{
843-
RunAsNonRoot: ptr.To(DefaultRunAsNonRoot),
844-
RunAsUser: ptr.To(DefaultRunAsUser),
845-
RunAsGroup: ptr.To(DefaultRunAsGroup),
846-
FSGroup: ptr.To(DefaultFsGroup),
847-
FSGroupChangePolicy: ptr.To(DefaultFSGroupChangePolicy),
848-
}
849-
}
850-
851841
// GetServiceAccountName returns the service account name for the cluster.
852842
func (in *CoherenceResourceSpec) GetServiceAccountName() string {
853843
if in != nil {

api/v1/common_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ func createMinimalExpectedPodSpec(deployment coh.CoherenceResource) corev1.PodTe
503503
VolumeSource: emptyVolume,
504504
},
505505
},
506-
SecurityContext: coh.DefaultSecurityContext(),
506+
SecurityContext: operator.DefaultSecurityContext(),
507507
TopologySpreadConstraints: spec.EnsureTopologySpreadConstraints(deployment),
508508
Affinity: spec.CreateDefaultPodAffinity(deployment),
509509
ServiceAccountName: spec.GetServiceAccountName(),

config/manager/manager.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ spec:
8787
- mountPath: /tmp/k8s-webhook-server/serving-certs
8888
name: cert
8989
readOnly: true
90+
- mountPath: /coherence-operator/config
91+
name: config
92+
readOnly: true
9093
readinessProbe:
9194
httpGet:
9295
port: health
@@ -114,6 +117,10 @@ spec:
114117
drop:
115118
- "ALL"
116119
volumes:
120+
- name: config
121+
configMap:
122+
name: coherence-operator
123+
optional: true
117124
- name: cert
118125
secret:
119126
defaultMode: 420

docs/about/04_coherence_spec.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,9 @@ m| ipFamilies | IPFamilies is a list of IP families (e.g. IPv4, IPv6) assigned t
10511051
This field may hold a maximum of two entries (dual-stack families, in either order). These families must correspond to the values of the clusterIPs field, if specified. Both clusterIPs and ipFamilies are governed by the ipFamilyPolicy field. m| []https://pkg.go.dev/k8s.io/api/core/v1#IPFamily | false
10521052
m| ipFamilyPolicy | IPFamilyPolicy represents the dual-stack-ness requested or required by this Service, and is gated by the "IPv6DualStack" feature gate. If there is no value provided, then this field will be set to SingleStack. Services can be "SingleStack" (a single IP family), "PreferDualStack" (two IP families on dual-stack configured clusters or a single IP family on single-stack clusters), or "RequireDualStack" (two IP families on dual-stack configured clusters, otherwise fail). The ipFamilies and clusterIPs fields depend on the value of this field. This field will be wiped when updating a service to type ExternalName. m| *https://{k8s-doc-link}/#ipfamilypolicy-v1-core[corev1.IPFamilyPolicy] | false
10531053
m| allocateLoadBalancerNodePorts | allocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster load-balancer does not rely on NodePorts. allocateLoadBalancerNodePorts may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type. This field is alpha-level and is only honored by servers that enable the ServiceLBNodePortControl feature. m| *bool | false
1054+
m| loadBalancerClass | loadBalancerClass is the class of the load balancer implementation this Service belongs to. If specified, the value of this field must be a label-style identifier, with an optional prefix, e.g. "internal-vip" or "example.com/internal-vip". Unprefixed names are reserved for end-users. This field can only be set when the Service type is 'LoadBalancer'. If not set, the default load balancer implementation is used, today this is typically done through the cloud provider integration, but should apply for any default implementation. If set, it is assumed that a load balancer implementation is watching for Services with a matching class. Any default load balancer implementation (e.g. cloud providers) should ignore Services that set this field. This field can only be set when creating or updating a Service to type 'LoadBalancer'. Once set, it can not be changed. This field will be wiped when a service is updated to a non 'LoadBalancer' type. m| *string | false
1055+
m| internalTrafficPolicy | InternalTrafficPolicy describes how nodes distribute service traffic they receive on the ClusterIP. If set to "Local", the proxy will assume that pods only want to talk to endpoints of the service on the same node as the pod, dropping the traffic if there are no local endpoints. The default value, "Cluster", uses the standard behavior of routing to all endpoints evenly (possibly modified by topology and other features). m| *https://{k8s-doc-link}/#serviceinternaltrafficpolicy-v1-core[corev1.ServiceInternalTrafficPolicy] | false
1056+
m| trafficDistribution | TrafficDistribution offers a way to express preferences for how traffic is distributed to Service endpoints. Implementations can use this field as a hint, but are not required to guarantee strict adherence. If the field is not set, the implementation will apply its default routing strategy. If set to "PreferClose", implementations should prioritize endpoints that are in the same zone. m| *string | false
10541057
|===
10551058
10561059
<<Table of Contents,Back to TOC>>

docs/installation/011_install_manifests.adoc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ below in <<manual-crd,Manually Install the CRDs>>.
103103
[#manual-crd]
104104
== Manually Install the CRDs
105105
106-
Although by default the Operator will install its CRDs, they can be manually installed into Kubernetes.
107-
This may be required where the Operator is running with restricted permissions as described above.
108-
109106
The Operator release artifacts include small versions of the two CRDs which can be installed with the following commands:
110107
111108
[source,bash]
@@ -133,3 +130,4 @@ Or on MacOS, where `sed` is slightly different:
133130
----
134131
sed -i '' -e 's/replicas: 3/replicas: 1/g' coherence-operator.yaml
135132
----
133+

docs/other/045_security_context.adoc

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@ Kubernetes allows you to configure a https://kubernetes.io/docs/tasks/configure-
1616
1717
For more details see the Kubernetes https://kubernetes.io/docs/tasks/configure-pod-container/security-context/[Security Context] documentation.
1818
19+
=== The Default Security Context
20+
1921
The Coherence Operator configures a default security context for the Coherence Pods is none is specified in the `Coherence` resource yaml.
2022
The default security context looks like this:
2123
[source,yaml]
2224
----
2325
securityContext:
2426
runAsNonRoot: true
25-
runAsUser: 1000
26-
runAsGroup: 2000
27-
fsGroup: 2000
27+
runAsUser: 1000650000
28+
runAsGroup: 1000650000
29+
fsGroup: 1000650000
2830
fsGroupChangePolicy: "OnRootMismatch"
2931
----
3032
33+
It is possible to change the values used for the default security context by specifying them when the Operator is installed.
34+
See the <<config,Configure The Default Security Context>> section below.
35+
3136
It is possible to override this as described below.
3237
3338
=== Setting the Pod Security Context
@@ -72,3 +77,116 @@ spec:
7277
capabilities:
7378
add: ["NET_ADMIN", "SYS_TIME"]
7479
----
80+
81+
[#config]
82+
=== Configure The Default Security Context
83+
84+
As already mentioned above the default security context created by the operator looks like this:
85+
86+
[source,yaml]
87+
----
88+
securityContext:
89+
runAsNonRoot: true
90+
runAsUser: 1000650000
91+
runAsGroup: 1000650000
92+
fsGroup: 1000650000
93+
fsGroupChangePolicy: "OnRootMismatch"
94+
----
95+
96+
The default values used for `runAsUser`, `runAsGroup` and `fsGroup` can be configured using the Operator's configuration file.
97+
98+
When the Operator is installed using the default installation it will read an optional configuration file from
99+
an optional `ConfigMap`. The `ConfigMap` must be created in the same namespace as the operator is running and
100+
should be named `coherence-operator`. The config map should contain a yaml file named `coherence-operator.yaml`.
101+
102+
[IMPORTANT]
103+
====
104+
The `coherence-operator` config map MUST be created before the Operator is installed, even if the yaml file
105+
that it contains is empty.
106+
107+
The Operator will watch the config file for changes, so if the `ConfigMap` is updated after the Operator is started,
108+
the changes will take effect. If the `ConfigMap` does not exist when the Operator is started then the config file
109+
cannot be mounted for the Operator to watch.
110+
====
111+
112+
113+
==== Disable The Default Security Context
114+
115+
To disable the creation of a default Pod security context for Coherence Pods, create a configuration file
116+
name `coherence-operator.yaml` with the following contents.
117+
118+
[source]
119+
.coherence-operator.yaml
120+
----
121+
coherenceSecurityContext:
122+
enabled: false
123+
----
124+
125+
Create the `ConfigMap` using the configuration file in the same namespace that the operator will be installed into.
126+
For example, if the operator is to be installed into a namespace named `coherence` the `ConfigMap` can be created
127+
using the following command:
128+
129+
[source,bash]
130+
----
131+
kubectl -n coherence create configmap coherence-operator \
132+
--from-file=coherence-operator.yaml
133+
----
134+
135+
With the `coherenceSecurityContext.enabled` field set to false, the Operator will not apply a default security context
136+
to the Coherence Pods. This may be useful in environments such as OpenShift which already apply a default security
137+
configuration to Pods.
138+
139+
==== Change The Default Security Context
140+
141+
In the configuration file, any field under the `coherenceSecurityContext` section will be applied to
142+
the default security context and override the operators default values.
143+
144+
For example, a `ConfigMap` could be created with the following file:
145+
146+
[source]
147+
.coherence-operator.yaml
148+
----
149+
coherenceSecurityContext:
150+
runAsUser: 1000
151+
----
152+
153+
This will override the `runAsUser` field to be set to `1000` resulting in a default security context as shown below:
154+
155+
[source,yaml]
156+
----
157+
securityContext:
158+
runAsNonRoot: true
159+
runAsUser: 1000
160+
runAsGroup: 1000650000
161+
fsGroup: 1000650000
162+
fsGroupChangePolicy: "OnRootMismatch"
163+
----
164+
165+
If the config file contains an empty value, this will result in the corresponding value being unset in the
166+
security context.
167+
This is useful for unsetting fields that the operator has default values for such as `runAsUser`, `runAsGroup`,
168+
`runAsNonRoot`, `fsGroup` and `fsGroupChangePolicy`.
169+
170+
171+
For example, the default `runAsUser` value is `1000650000`.
172+
The configuration file can be created with a `runAsUser` field with no value as shown below
173+
174+
[source]
175+
.coherence-operator.yaml
176+
----
177+
coherenceSecurityContext:
178+
runAsUser:
179+
----
180+
181+
This will result in a security context with the `runAsUser` unset.
182+
183+
[source,yaml]
184+
----
185+
securityContext:
186+
runAsNonRoot: true
187+
runAsUser:
188+
runAsGroup: 1000650000
189+
fsGroup: 1000650000
190+
fsGroupChangePolicy: "OnRootMismatch"
191+
----
192+

helm-charts/coherence-operator/templates/deployment.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ spec:
275275
- mountPath: /tmp/k8s-webhook-server/serving-certs
276276
name: cert
277277
readOnly: true
278+
- mountPath: /coherence-operator/config
279+
name: config
280+
readOnly: true
278281
readinessProbe:
279282
httpGet:
280283
port: health
@@ -378,3 +381,7 @@ spec:
378381
secret:
379382
defaultMode: 420
380383
secretName: {{ .Values.webhookCertSecret }}
384+
- name: config
385+
configMap:
386+
name: coherence-operator
387+
optional: true

helm-charts/coherence-operator/values.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ globalAnnotations:
7575

7676
# ---------------------------------------------------------------------------
7777
# Operator Pod securityContext
78-
# This sets the securityContext configuration for the Pod, for example
78+
# This sets the securityContext configuration for the Operator Pod, for example,
7979
# to run as a non-root user:
8080
#
8181
# podSecurityContext:
@@ -86,7 +86,7 @@ podSecurityContext:
8686

8787
# ---------------------------------------------------------------------------
8888
# Operator container securityContext
89-
# This sets the securityContext configuration for the container, for example
89+
# This sets the securityContext configuration for the container, for example,
9090
# to run as a non-root user:
9191
#
9292
# securityContext:

pkg/operator/operator.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/spf13/viper"
2020
corev1 "k8s.io/api/core/v1"
2121
"k8s.io/apimachinery/pkg/util/version"
22+
"k8s.io/utils/ptr"
2223
"os"
2324
"path/filepath"
2425
ctrl "sigs.k8s.io/controller-runtime"
@@ -46,6 +47,25 @@ const (
4647
CertTypeManual = "manual"
4748
CertManagerIssuerName = "coherence-webhook-server-issuer"
4849

50+
// DefaultRunAsNonRoot is the default value for the runAsNonRoot field in the Pod security context
51+
DefaultRunAsNonRoot = true
52+
// DefaultRunAsUser is the default value for the runAsUser field in the Pod security context
53+
DefaultRunAsUser int64 = 1000650000
54+
// DefaultRunAsGroup is the default value for the runAsGroup field in the Pod security context
55+
DefaultRunAsGroup int64 = 1000650000
56+
// DefaultFsGroup is the default value for the fsGroup field in the Pod security context
57+
DefaultFsGroup int64 = DefaultRunAsGroup
58+
// DefaultFSGroupChangePolicy is the default value for the fsGroup field in the Pod security context
59+
DefaultFSGroupChangePolicy = corev1.FSGroupChangeOnRootMismatch
60+
61+
ConfigKeyCoherenceSecurityContext = "coherenceSecurityContext"
62+
ConfigKeyCoherenceSecurityContextEnabled = "enabled"
63+
ConfigKeyCoherenceSecurityContextRunAsUser = "runAsUser"
64+
ConfigKeyCoherenceSecurityContextRunAsGroup = "runAsGroup"
65+
ConfigKeyCoherenceSecurityContextRunAsNonRoot = "runAsNonRoot"
66+
ConfigKeyCoherenceSecurityContextFsGroup = "fsGroup"
67+
ConfigKeyCoherenceSecurityContextFSGroupChangePolicy = "fSGroupChangePolicy"
68+
4969
DefaultMutatingWebhookName = "coherence-operator-mutating-webhook-configuration"
5070
DefaultValidatingWebhookName = "coherence-operator-validating-webhook-configuration"
5171

@@ -60,6 +80,8 @@ const (
6080
FlagDevMode = "coherence-dev-mode"
6181
FlagCipherDenyList = "cipher-deny-list"
6282
FlagCipherAllowList = "cipher-allow-list"
83+
FlagConfig = "config"
84+
FlagConfigType = "config-type"
6385
FlagDryRun = "dry-run"
6486
FlagEnableWebhook = "enable-webhook"
6587
FlagEnableHttp2 = "enable-http2"
@@ -619,3 +641,46 @@ func RemoveFromUInt16Array(arr []uint16, toRemove uint16) []uint16 {
619641
}
620642
return arr
621643
}
644+
645+
// DefaultSecurityContext returns the default Pod security context that the Operator will apply
646+
// to Coherence pods. The values used can be overridden using command line args.
647+
func DefaultSecurityContext() *corev1.PodSecurityContext {
648+
v := GetViper()
649+
650+
m := v.GetStringMap(ConfigKeyCoherenceSecurityContext)
651+
if m == nil {
652+
m = make(map[string]interface{})
653+
}
654+
655+
enabled, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextEnabled)]
656+
if found && enabled == false {
657+
return nil
658+
}
659+
660+
sc := &corev1.PodSecurityContext{}
661+
662+
if v.IsSet(ConfigKeyCoherenceSecurityContext) {
663+
err := v.UnmarshalKey(ConfigKeyCoherenceSecurityContext, sc)
664+
if err != nil {
665+
setupLog.Error(err, "unable to unmarshal coherenceSecurityContext from Operator config file")
666+
}
667+
}
668+
669+
if _, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextRunAsUser)]; !found {
670+
sc.RunAsUser = ptr.To(DefaultRunAsUser)
671+
}
672+
if _, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextRunAsGroup)]; !found {
673+
sc.RunAsGroup = ptr.To(DefaultRunAsGroup)
674+
}
675+
if _, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextRunAsNonRoot)]; !found {
676+
sc.RunAsNonRoot = ptr.To(DefaultRunAsNonRoot)
677+
}
678+
if _, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextFsGroup)]; !found {
679+
sc.FSGroup = ptr.To(DefaultFsGroup)
680+
}
681+
if _, found := m[strings.ToLower(ConfigKeyCoherenceSecurityContextFSGroupChangePolicy)]; !found {
682+
sc.FSGroupChangePolicy = ptr.To(DefaultFSGroupChangePolicy)
683+
}
684+
685+
return sc
686+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
coherenceSecurityContext:
2+
enabled: false
3+

0 commit comments

Comments
 (0)