In this demo we will learn how we can debug issues related to SCCs in our cluster.
We have a workload that requires binding to port 80. We create the deployment but pods keep failing.
-
Create a new namespace for running our tests:
NAMESPACE=debug-sccs oc create ns ${NAMESPACE}
-
Create the following deployment
cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: reversewords-app name: reversewords-app spec: replicas: 1 selector: matchLabels: app: reversewords-app strategy: {} template: metadata: creationTimestamp: null labels: app: reversewords-app spec: serviceAccountName: default containers: - image: quay.io/mavazque/reversewords:ubi8 name: reversewords resources: {} env: - name: APP_PORT value: "80" status: {} EOF
-
Debug why pod is failing and fix the issue using an SCC from the ones included in OpenShift out of the box.
Solution
Click here to see solution
-
Check the pod logs
oc -n ${NAMESPACE} logs -l app=reversewords-app
2023/10/16 17:45:13 Starting Reverse Api v0.0.21 Release: NotSet 2023/10/16 17:45:13 Listening on port 80 2023/10/16 17:45:13 listen tcp :80: bind: permission denied
-
From the logs we can see that the pod doesn't have permissions to bind to port 80.
-
We could bind to that port if we were running as UID 0.
-
Create a ServiceAccount for running our deployment workloads:
oc -n ${NAMESPACE} create serviceaccount reversewordsapp
-
Assign the SCC
anyuid
to the SA:oc -n ${NAMESPACE} adm policy add-scc-to-user anyuid -z reversewordsapp
-
Patch the deployment, so it uses the new SA we created:
oc -n ${NAMESPACE} patch deployment reversewords-app -p '{"spec":{"template":{"spec":{"serviceAccountName":"reversewordsapp"}}}}' --type merge
-
Patch the deployment so container
reversewords
runs with UID 0:oc -n ${NAMESPACE} patch deployment reversewords-app -p '{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"reversewords"}],"containers":[{"name":"reversewords","securityContext":{"runAsUser":0}}]}}}}'
-
Check pod logs:
oc -n ${NAMESPACE} logs -l app=reversewords-app
2023/10/16 17:45:28 Starting Reverse Api v0.0.21 Release: NotSet 2023/10/16 17:45:28 Listening on port 80
We have a workload that requires mounting a hostpath. We create the deployment but pods are not being created.
-
Set the namespace for running our tests:
NAMESPACE=debug-sccs
-
Create the following deployment
cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: reversewords-app-hostpath name: reversewords-app-hostpath spec: replicas: 1 selector: matchLabels: app: reversewords-app-hostpath strategy: {} template: metadata: creationTimestamp: null labels: app: reversewords-app-hostpath spec: serviceAccountName: default containers: - image: quay.io/mavazque/reversewords:ubi8 name: reversewords resources: {} volumeMounts: - mountPath: /test-mount/test.txt name: hostpath-volume volumes: - name: hostpath-volume hostPath: path: /tmp/test.txt type: FileOrCreate status: {} EOF
-
Debug why pod is not being created and fix the issue using a custom SCC.
Solution
Click here to see solution
-
Check deployment status
oc -n ${NAMESPACE} get deployment reversewords-app-hostpath -o yaml | grep -A100 ^status:
status: conditions: <OUTPUT_OMITTED> - lastTransitionTime: "2023-10-16T17:45:41Z" lastUpdateTime: "2023-10-16T17:45:41Z" message: 'pods "reversewords-app-hostpath-78f98b8466-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, spec.volumes[0]: Invalid value: "hostPath": hostPath volumes are not allowed to be used, provider "restricted": Forbidden: not usable by user or serviceaccount, provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "node-exporter": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]' reason: FailedCreate status: "True" type: ReplicaFailure observedGeneration: 1 unavailableReplicas: 1
-
From the status we can see that the pod cannot be validated against any SCC which allow hostPath mounts.
-
We could mount that path if we had access to an SCC which allows this kind of mounts.
-
Create a ServiceAccount for running our deployment workloads:
oc -n ${NAMESPACE} create serviceaccount reversewordsapp-hostpath
-
Create an SCC based on the restricted one but with permissions to mount
hostPath
volumes:NOTE: We added
hostPath
to the list of allowed volumes. And setallowHostDirVolumePlugin
totrue
.cat <<EOF | oc create -f - kind: SecurityContextConstraints metadata: name: restricted-hostpathmount priority: null readOnlyRootFilesystem: false requiredDropCapabilities: - ALL runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs supplementalGroups: type: RunAsAny users: [] volumes: - configMap - downwardAPI - emptyDir - persistentVolumeClaim - projected - secret - hostPath allowHostDirVolumePlugin: true allowHostIPC: false allowHostNetwork: false allowHostPID: false allowHostPorts: false allowPrivilegeEscalation: true allowPrivilegedContainer: false allowedCapabilities: null apiVersion: security.openshift.io/v1 defaultAddCapabilities: null fsGroup: type: MustRunAs groups: [] EOF
-
Assign the SCC
restricted-hostpathmount
to the SA:oc -n ${NAMESPACE} adm policy add-scc-to-user restricted-hostpathmount -z reversewordsapp-hostpath
-
Patch the deployment so it uses the new SA we created:
oc -n ${NAMESPACE} patch deployment reversewords-app-hostpath -p '{"spec":{"template":{"spec":{"serviceAccountName":"reversewordsapp-hostpath"}}}}' --type merge
-
Scale the deployment to 0 and back to 1 so it gets the latest configuration:
oc -n ${NAMESPACE} scale deployment reversewords-app-hostpath --replicas=0 oc -n ${NAMESPACE} scale deployment reversewords-app-hostpath --replicas=1
-
Check pod logs:
oc -n ${NAMESPACE} logs -l app=reversewords-app-hostpath
2023/10/16 17:47:53 Starting Reverse Api v0.0.21 Release: NotSet 2023/10/16 17:47:53 Listening on port 8080
-
List our hostPath volume:
oc -n ${NAMESPACE} exec deploy/reversewords-app-hostpath -- ls -l /test-mount/test.txt
-rw-r--r--. 1 root root 0 Feb 8 11:34 /test-mount/test.txt
We have a workload that requires running with a given UID (1024). We created the deployment, but pods are not being created.
-
Set the namespace for running our tests:
NAMESPACE=debug-sccs
-
Create the following deployment
cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: reversewords-app-uid name: reversewords-app-uid spec: replicas: 1 selector: matchLabels: app: reversewords-app-uid strategy: {} template: metadata: creationTimestamp: null labels: app: reversewords-app-uid spec: serviceAccountName: default containers: - image: quay.io/mavazque/reversewords:ubi8 name: reversewords resources: {} securityContext: runAsUser: 1024 status: {} EOF
-
Debug why pod is not being created and fix the issue using an SCC from the ones included in OpenShift out of the box.
Solution
Click here to see solution
-
Check deployment status
oc -n ${NAMESPACE} get deployment reversewords-app-uid -o yaml | grep -A100 ^status:
status: conditions: <OUTPUT_OMITTED> - lastTransitionTime: "2023-10-16T17:48:37Z" lastUpdateTime: "2023-10-16T17:48:37Z" message: 'pods "reversewords-app-uid-85b4f5f9c5-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, spec.containers[0].securityContext.runAsUser: Invalid value: 1024: must be in the ranges: [1000820000, 1000829999], provider "restricted": Forbidden: not usable by user or serviceaccount, provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "restricted-hostpathmount": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "node-exporter": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]' reason: FailedCreate status: "True" type: ReplicaFailure observedGeneration: 1 unavailableReplicas: 1
-
From the status we can see that the pod cannot be validated against any SCC which allow running with an arbitrary UID.
-
Create a ServiceAccount for running our deployment workloads:
oc -n ${NAMESPACE} create serviceaccount reversewordsapp-uid
-
We have a couple SCCs that can make this work
anyuid
andnonroot-v2
. Since we don't need to run with UID 0,nonroot-v2
is a better choice. -
Assign the SCC
nonroot-v2
to the SA:oc -n ${NAMESPACE} adm policy add-scc-to-user nonroot-v2 -z reversewordsapp-uid
-
Patch the deployment, so it uses the new SA we created:
oc -n ${NAMESPACE} patch deployment reversewords-app-uid -p '{"spec":{"template":{"spec":{"serviceAccountName":"reversewordsapp-uid"}}}}' --type merge
-
Check pod logs:
oc -n ${NAMESPACE} logs -l app=reversewords-app-uid
2023/10/16 17:49:46 Starting Reverse Api v0.0.21 Release: NotSet 2023/10/16 17:49:46 Listening on port 8080
-
Check UID assigned to the container:
oc -n ${NAMESPACE} exec deploy/reversewords-app-uid -- whoami
1024
We have created a custom SCC based on the restricted-v2
one, we want all pods in the namespace debug-sccs
using the custom-scc
SA to run with the restricted-custom
SCC but for some reason, pods are getting a different SCC assigned.
Debug what happens and change the required configurations so we get the custom-scc
assigned.
-
Set the namespace for running our tests:
NAMESPACE=debug-sccs
-
Create the
restricted-custom
SCC:cat <<EOF | oc create -f - apiVersion: security.openshift.io/v1 kind: SecurityContextConstraints metadata: name: restricted-v2-custom allowHostDirVolumePlugin: false allowHostIPC: false allowHostNetwork: false allowHostPID: false allowHostPorts: false allowPrivilegeEscalation: false allowPrivilegedContainer: false allowedCapabilities: - NET_BIND_SERVICE defaultAddCapabilities: null fsGroup: type: MustRunAs groups: [] priority: null readOnlyRootFilesystem: false requiredDropCapabilities: - ALL runAsUser: type: MustRunAsRange seLinuxContext: type: MustRunAs seccompProfiles: - runtime/default supplementalGroups: type: RunAsAny users: [] volumes: - configMap - downwardAPI - emptyDir - persistentVolumeClaim - projected - secret EOF
-
Create the
custom-scc
SA :oc -n ${NAMESPACE} create sa custom-scc
-
Give access to
custom-scc
to therestricted-custom
SCC:oc -n ${NAMESPACE} adm policy add-scc-to-user restricted-v2-custom system:serviceaccount:${NAMESPACE}:custom-scc
-
Create the following deployment:
cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: reversewords-app-customscc name: reversewords-app-customscc spec: replicas: 1 selector: matchLabels: app: reversewords-app-customscc strategy: {} template: metadata: creationTimestamp: null labels: app: reversewords-app-customscc spec: serviceAccountName: custom-scc containers: - image: quay.io/mavazque/reversewords:ubi8 name: reversewords resources: {} status: {} EOF
-
Check the SCC assigned to the pod:
oc -n ${NAMESPACE} get pod -l app=reversewords-app-customscc -o 'custom-columns=NAME:metadata.name,APPLIED SCC:metadata.annotations.openshift\.io/scc'
NOTE:
restricted-v2
SCC was applied instead ofrestricted-custom
.NAME APPLIED SCC reversewords-app-customscc-b697b8c5d-w52hn restricted-v2
-
Debug why pod is not being assigned the desired SCC and fix the issue by modifying the
custom-scc
as needed.
Solution
Click here to see solution
-
Remember that all authenticated users have access to the
restricted-v2
SCC and that the SCCs are ordered as follows:- If the SCCs have different priorities, higher priority first.
- If priority is the same, the most restrictive first.
- If priority and restriction level are the same, by alphabetical order first.
-
Our SCCs have the same priority
null
with equals to 0. -
Our SCCs have the same restriction level.
-
Our custom SCC when ordered alphabetically will be after the
restricted
one. -
We need to increase the priority of the
restricted-v2-custom
SCC, so it has more priority and gets 1st on the list, since this is a Pod we cannotmake use of theopenshift.io/required-scc
annotation:oc patch scc restricted-v2-custom -p '{"priority":1}' --type merge
-
Force the app pod to be recreated:
oc -n ${NAMESPACE} delete pod -l app=reversewords-app-customscc
-
Check the SCC assigned to the new pod:
oc -n ${NAMESPACE} get pod -l app=reversewords-app-customscc -o 'custom-columns=NAME:metadata.name,APPLIED SCC:metadata.annotations.openshift\.io/scc'
NOTE: This time the
restricted-v2-custom
SCC got prioritized.NAME APPLIED SCC reversewords-app-customscc-b697b8c5d-p2ltv restricted-v2-custom
We have a NFS server which is providing shared storage. The NFS share is configured, so it can be accessed by the group 5000
. We need our application to connect to this share and read/write the content inside the share. For some reason we are not able to read/write the content.
Debug the issue and fix the required configurations so our application can read/write the content from the share.
-
Create a new namespace for running our tests:
NAMESPACE=debug-scc-shared-storage oc create ns ${NAMESPACE}
-
Create the
nfs-server
deployment:cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: v1 kind: ServiceAccount metadata: name: nfs-server --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: nfs-server-anyuid-scc roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:openshift:scc:anyuid subjects: - kind: ServiceAccount name: nfs-server namespace: ${NAMESPACE} --- apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: nfs-server name: nfs-server spec: replicas: 1 selector: matchLabels: app: nfs-server strategy: {} template: metadata: creationTimestamp: null labels: app: nfs-server spec: serviceAccountName: nfs-server containers: - image: quay.io/mavazque/nfs-server:latest name: nfs-server securityContext: runAsUser: 0 ports: - containerPort: 2049 resources: {} status: {} --- apiVersion: v1 kind: Service metadata: creationTimestamp: null labels: app: nfs-server name: nfs-server spec: ports: - name: 2049-2049 port: 2049 protocol: TCP targetPort: 2049 selector: app: nfs-server type: ClusterIP status: loadBalancer: {} EOF
-
Create a PV and a PVC to be used by the app for attaching to the NFS Server
# Get ClusterIP Service IP NFS_SERVER=$(oc -n ${NAMESPACE} get svc nfs-server -o jsonpath='{.spec.clusterIP}') # Objects creation cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: v1 kind: PersistentVolume metadata: name: nfs-shared-test-volume-debug spec: capacity: storage: 500Mi accessModes: - ReadWriteMany nfs: server: ${NFS_SERVER} path: "/nfs-share" mountOptions: - port=2049 - mountport=2049 - nfsvers=3 - tcp --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-shared-test-volume-debug-claim spec: accessModes: - ReadWriteMany storageClassName: "" volumeName: nfs-shared-test-volume-debug resources: requests: storage: 500Mi EOF
-
Create the app deployment
cat <<EOF | oc -n ${NAMESPACE} create -f - apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: reversewords-app-shared-storage name: reversewords-app-shared-storage spec: replicas: 1 selector: matchLabels: app: reversewords-app-shared-storage strategy: {} template: metadata: creationTimestamp: null labels: app: reversewords-app-shared-storage spec: volumes: - name: test-volume persistentVolumeClaim: claimName: nfs-shared-test-volume-debug-claim containers: - image: quay.io/mavazque/reversewords:ubi8 name: reversewords resources: {} volumeMounts: - name: test-volume mountPath: "/mnt" status: {} EOF
-
Try to read/write to the NFS Share
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- cat /mnt/testfile.txt
cat: /mnt/testfile.txt: Permission denied command terminated with exit code 1
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- touch /mnt/shared-storage-1
touch: cannot touch '/mnt/shared-storage-1': Permission denied command terminated with exit code 1
Solution
Click here to see solution
-
As said in the lab description, the NFS Share uses the group
5000
as the collaborative group for the NFS Share. That means that we need to assign this group to the user running our container. -
Let's review the NFS Share config from the app pod:
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- ls -ld /mnt
drwxrwsr-x. 1 5000 5000 26 Feb 16 2021 /mnt
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- ls -lrt /mnt/
total 4 -rw-rw----. 1 5000 5000 38 Feb 16 2021 testfile.txt
-
As we can see, the folder is owned by UID,GID 5000 and so is the file.
-
Let's check which user is running our container:
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- id
uid=1000700000(1000700000) gid=0(root) groups=0(root),1000700000
-
As you can see the user is not part of the GID 5000, so we need to fix that:
oc -n ${NAMESPACE} patch deployment reversewords-app-shared-storage -p '{"spec":{"template":{"spec":{"securityContext":{"supplementalGroups":[5000]}}}}}'
-
If we check the user running the container again:
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- id
uid=1000700000(1000700000) gid=0(root) groups=0(root),5000,1000700000
-
Since now, we are part of the group we will be able to read and write to the shared storage:
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- cat /mnt/testfile.txt
This is a testfile owned by user 5000
oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- touch /mnt/shared-storage-1 oc -n ${NAMESPACE} exec -ti deployment/reversewords-app-shared-storage -- ls -l /mnt/shared-storage-1
-rw-r--r--. 1 1000760000 5000 0 Oct 16 18:01 /mnt/shared-storage-1