Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Commit

Permalink
Use random UID and GID when running on OpenShift
Browse files Browse the repository at this point in the history
When running on OpenShift, allow OpenShift to assign a random UID and
GID for the Gatekeeper containers. When it's not OpenShift, fallback to
running as a non-privileged user and group.

Additionally, for backwards compatibility with OpenShift 4.10, seccomp
profile is left unset. See the following for this recommendation:
https://connect.redhat.com/en/blog/important-openshift-changes-pod-security-standards

Signed-off-by: mprahl <[email protected]>
  • Loading branch information
mprahl committed Sep 18, 2023
1 parent 89a85bf commit a7d2f6a
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 53 deletions.
21 changes: 0 additions & 21 deletions config/gatekeeper/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,9 @@ resources:
- v1_secret_gatekeeper-webhook-server-cert.yaml
- v1_serviceaccount_gatekeeper-admin.yaml
- v1_service_gatekeeper-webhook-service.yaml
# Add annotations for backwards compatibility
# Add OpenShift specific RBAC
# Remove --disable-cert-rotation
# Set a CPU limit
patches:
- path: patches/apps_v1_deployment_gatekeeper-audit.yaml
- path: patches/apps_v1_deployment_gatekeeper-controller-manager.yaml
- patch: |-
- op: add
path: /rules/-
value:
apiGroups:
- security.openshift.io
resourceNames:
- anyuid
resources:
- securitycontextconstraints
verbs:
- use
target:
group: rbac.authorization.k8s.io
kind: Role
name: gatekeeper-manager-role
version: v1
- patch: |-
- op: remove
path: /spec/template/spec/containers/0/args/5
Expand Down
10 changes: 0 additions & 10 deletions config/gatekeeper/patches/apps_v1_deployment_gatekeeper-audit.yaml

This file was deleted.

This file was deleted.

40 changes: 40 additions & 0 deletions controllers/gatekeeper_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ func crOverrides(gatekeeper *operatorv1alpha1.Gatekeeper, asset string, obj *uns
if err := removeAnnotations(obj); err != nil {
return err
}

if err := openShiftDeploymentOverrides(obj); err != nil {
return err
}
}
// webhook overrides
case WebhookFile:
Expand All @@ -512,6 +516,10 @@ func crOverrides(gatekeeper *operatorv1alpha1.Gatekeeper, asset string, obj *uns
if err := removeAnnotations(obj); err != nil {
return err
}

if err := openShiftDeploymentOverrides(obj); err != nil {
return err
}
}
// ValidatingWebhookConfiguration overrides
case ValidatingWebhookConfiguration:
Expand Down Expand Up @@ -742,6 +750,38 @@ func removeAnnotations(obj *unstructured.Unstructured) error {
return nil
}

// openShiftDeploymentOverrides will remove runAsUser, runAsGroup, and seccompProfile on every container in the
// Deployment manifest. The seccompProfile is removed for backwards compatibility with OpenShift <= v4.10. Setting
// seccompProfile=runtime/default in such versions explicitly disqualified the workload from the restricted SCC.
// In OpenShift v4.11+, any workload running in a namespace prefixed with "openshift-*" must use the "restricted"
// profile unless there is a ClusterServiceVersion present, which is not the case for the Gatekeeper operand namespace.
func openShiftDeploymentOverrides(obj *unstructured.Unstructured) error {
containers, _, err := unstructured.NestedSlice(obj.Object, "spec", "template", "spec", "containers")
if err != nil {
return errors.Wrapf(err, "Failed to parse the deployment's containers")
}

for i := range containers {
container, ok := containers[i].(map[string]interface{})
if !ok {
continue
}

unstructured.RemoveNestedField(container, "securityContext", "runAsUser")
unstructured.RemoveNestedField(container, "securityContext", "runAsGroup")
unstructured.RemoveNestedField(container, "securityContext", "seccompProfile")

containers[i] = container
}

err = unstructured.SetNestedField(obj.Object, containers, "spec", "template", "spec", "containers")
if err != nil {
return errors.Wrapf(err, "Failed to set the OpenShift overrides")
}

return nil
}

func setLogLevel(obj *unstructured.Unstructured, logLevel *operatorv1alpha1.LogLevelMode) error {
if logLevel != nil {
return setContainerArg(obj, managerContainer, LogLevelArg, string(*logLevel), false)
Expand Down
58 changes: 58 additions & 0 deletions controllers/gatekeeper_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,64 @@ func assertAffinity(g *WithT, expected *corev1.Affinity, current interface{}) {
g.Expect(util.ToMap(expected)).To(BeEquivalentTo(util.ToMap(current)))
}

func TestOpenShiftOverrides(t *testing.T) {
g := NewWithT(t)
gatekeeper := &operatorv1alpha1.Gatekeeper{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
}

auditObj, err := util.GetManifestObject(AuditFile)
g.Expect(err).ToNot(HaveOccurred())

webhookObj, err := util.GetManifestObject(WebhookFile)
g.Expect(err).ToNot(HaveOccurred())

// Test that no OpenShift overrides take place when it's not OpenShift
err = crOverrides(gatekeeper, AuditFile, auditObj, namespace, false, false)
g.Expect(err).ToNot(HaveOccurred())
assertOverrides(g, auditObj, true)

err = crOverrides(gatekeeper, WebhookFile, webhookObj, namespace, false, false)
g.Expect(err).ToNot(HaveOccurred())
assertOverrides(g, webhookObj, true)

// Test that OpenShift overrides take place
err = crOverrides(gatekeeper, AuditFile, auditObj, namespace, true, false)
g.Expect(err).ToNot(HaveOccurred())
assertOverrides(g, auditObj, false)

err = crOverrides(gatekeeper, WebhookFile, webhookObj, namespace, true, false)
g.Expect(err).ToNot(HaveOccurred())
assertOverrides(g, webhookObj, false)
}

func assertOverrides(g *WithT, current *unstructured.Unstructured, isSet bool) {
containers, _, err := unstructured.NestedSlice(current.Object, "spec", "template", "spec", "containers")
g.ExpectWithOffset(1, err).ToNot(HaveOccurred())
g.ExpectWithOffset(1, containers).ToNot(BeEmpty())

for i := range containers {
container, ok := containers[i].(map[string]interface{})
if !ok {
continue
}

_, runAsUserFound, err := unstructured.NestedInt64(container, "securityContext", "runAsUser")
g.ExpectWithOffset(1, err).ToNot(HaveOccurred())
g.ExpectWithOffset(1, runAsUserFound).To(Equal(isSet))

_, runAsGroupFound, err := unstructured.NestedInt64(container, "securityContext", "runAsGroup")
g.ExpectWithOffset(1, err).ToNot(HaveOccurred())
g.ExpectWithOffset(1, runAsGroupFound).To(Equal(isSet))

_, seccompProfileFound, err := unstructured.NestedMap(container, "securityContext", "seccompProfile")
g.ExpectWithOffset(1, err).ToNot(HaveOccurred())
g.ExpectWithOffset(1, seccompProfileFound).To(Equal(isSet))
}
}

func TestNodeSelector(t *testing.T) {
g := NewWithT(t)
nodeSelector := map[string]string{
Expand Down
12 changes: 0 additions & 12 deletions pkg/bindata/bindata.go

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

0 comments on commit a7d2f6a

Please sign in to comment.