Managing SELinux profiles
To control what namespaced workloads can access on RHCOS nodes, use the Security Profiles Operator to create SELinux profiles, bind them to pods, and record policies from running applications.
The Security Profiles Operator supports only Red Hat Enterprise Linux CoreOS (RHCOS) worker nodes. Red Hat Enterprise Linux (RHEL) nodes are not supported.
Creating SELinux profiles
Use the SelinuxProfile object to create SELinux profiles.
The SelinuxProfile object has several features that allow for better security hardening and readability:
- Restricts the profiles to inherit from to the current namespace or a system-wide profile. Because there are typically many profiles installed on the system, but only a subset should be used by cluster workloads, the inheritable system profiles are listed in the
spodinstance inspec.selinuxOptions.allowedSystemProfiles. - Performs basic validation of the permissions, classes and labels.
- Adds a new keyword
@selfthat describes the process using the policy. This allows reusing a policy between workloads and namespaces easily, as the usage of the policy is based on the name and namespace. - Adds features for better security hardening and readability compared to writing a profile directly in the SELinux CIL language.
Procedure
- Create a project by running the following command:terminal
$ oc new-project nginx-deploy - Create a policy that can be used with a non-privileged workload by creating the following
SelinuxProfileobject:yamlapiVersion: security-profiles-operator.x-k8s.io/v1alpha2 kind: SelinuxProfile metadata: name: nginx-secure spec: allow: '@self': tcp_socket: - listen http_cache_port_t: tcp_socket: - name_bind node_t: tcp_socket: - node_bind inherit: - kind: System name: container - Wait for
selinuxdto install the policy by running the following command:terminal$ oc wait --for=condition=ready selinuxprofile nginx-secureExample outputselinuxprofile.security-profiles-operator.x-k8s.io/nginx-secure condition metThe policies are placed into an
emptyDirin the container owned by the Security Profiles Operator. The policies are saved in Common Intermediate Language (CIL) format in/etc/selinux.d/<name>_<namespace>.cil. - Access the pod by running the following command:terminal
$ oc -n openshift-security-profiles rsh -c selinuxd ds/spod
Verification
- View the file contents with
catby running the following command:terminal$ cat /etc/selinux.d/nginx-secure.cilExample output(block nginx-secure (blockinherit container) (allow process nginx-secure.process ( tcp_socket ( listen ))) (allow process http_cache_port_t ( tcp_socket ( name_bind ))) (allow process node_t ( tcp_socket ( node_bind ))) ) - Verify that a policy has been installed by running the following command:terminal
$ semodule -l | grep nginx-secureExample outputnginx-secure
Apply SELinux profiles to a pod
To enforce a recorded or custom SELinux profile on a workload, create a pod that references the profile in its security context.
For SELinux profiles, the namespace must be labeled to allow privileged workloads.
Procedure
- Apply the
scc.podSecurityLabelSync=falselabel to thenginx-deploynamespace by running the following command:terminal$ oc label ns nginx-deploy security.openshift.io/scc.podSecurityLabelSync=false - Apply the
privilegedlabel to thenginx-deploynamespace by running the following command:terminal$ oc label ns nginx-deploy --overwrite=true pod-security.kubernetes.io/enforce=privileged - Obtain the SELinux profile usage string by running the following command:terminal
$ oc get selinuxprofile.security-profiles-operator.x-k8s.io/nginx-secure -ojsonpath='{.status.usage}'Example outputnginx-secure.process - Apply the output string in the workload manifest in the
.spec.containers[].securityContext.seLinuxOptionsattribute:yamlapiVersion: v1 kind: Pod metadata: name: nginx-secure namespace: nginx-deploy spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - image: nginxinc/nginx-unprivileged:1.21 name: nginx securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] seLinuxOptions: # NOTE: This uses an appropriate SELinux type type: nginx-secure.processImportantThe SELinux
typemust exist before creating the workload.
Apply SELinux log policies
To log policy violations or AVC denials, set the SElinuxProfile profile to permissive.
This procedure defines logging policies. It does not set enforcement policies.
Procedure
- Add
permissive: trueto anSElinuxProfile:yamlapiVersion: security-profiles-operator.x-k8s.io/v1alpha2 kind: SelinuxProfile metadata: name: nginx-secure spec: permissive: true
Binding workloads to profiles with ProfileBindings
You can use the ProfileBinding resource to bind a security profile to the SecurityContext of a container.
Procedure
- To bind a pod that uses a
quay.io/security-profiles-operator/test-nginx-unprivileged:1.21image to the exampleSelinuxProfileprofile, create aProfileBindingobject in the same namespace with the pod and theSelinuxProfileobjects:yamlapiVersion: security-profiles-operator.x-k8s.io/v1alpha1 kind: ProfileBinding metadata: namespace: my-namespace name: nginx-binding spec: profileRef: kind: SelinuxProfile name: profile image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21where:
spec.profileRef.kindSpecifies the kind of the profile.
spec.profileRef.nameSpecifies the name of the profile.
spec.imageAllows you to enable a default security profile by using a wildcard in the image attribute:
image: "*"
ImportantUsing the
image: "*"wildcard attribute binds all new pods with a default security profile in a given namespace. - Label the namespace with
enable-binding=trueby running the following command:terminal$ oc label ns my-namespace spo.x-k8s.io/enable-binding=true - Define a pod named
test-pod.yaml:yamlapiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21 - Create the pod:terminal
$ oc create -f test-pod.yamlNoteIf the pod already exists, you must re-create the pod for the binding to work properly.
Verification
- Confirm the pod inherits the
ProfileBindingby running the following command:terminal$ oc get pod test-pod -o jsonpath='{.spec.containers[*].securityContext.seLinuxOptions.type}'Example outputprofile.process
Replicate controllers and SecurityContextConstraints
Deploy SELinux policies for replicating controllers such as deployments or daemon sets so the pods those controllers create can use custom SELinux policies.
Pods that the controllers create do not run with the identity of the user who creates the workload. Unless you select a ServiceAccount, the pods might use a restricted SecurityContextConstraints (SCC) object that does not allow custom security policies.
Procedure
- Create a project by running the following command:terminal
$ oc new-project nginx-secure - Create the following
RoleBindingobject to allow SELinux policies to be used in thenginx-securenamespace:yamlkind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spo-nginx namespace: nginx-secure subjects: - kind: ServiceAccount name: spo-deploy-test roleRef: kind: Role name: spo-nginx apiGroup: rbac.authorization.k8s.io - Create the
Roleobject:yamlapiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null name: spo-nginx namespace: nginx-secure rules: - apiGroups: - security.openshift.io resources: - securitycontextconstraints resourceNames: - privileged verbs: - use - Create the
ServiceAccountobject:yamlapiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: null name: spo-deploy-test namespace: nginx-secure - Create the
Deploymentobject:yamlapiVersion: apps/v1 kind: Deployment metadata: name: selinux-test namespace: nginx-secure metadata: labels: app: selinux-test spec: replicas: 3 selector: matchLabels: app: selinux-test template: metadata: labels: app: selinux-test spec: serviceAccountName: spo-deploy-test securityContext: seLinuxOptions: type: nginx-secure.process containers: - name: nginx-unpriv image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21 ports: - containerPort: 8080The
spec.template.spec.securityContext.seLinuxOptions.typemust exist before the Deployment is created.NoteThe SELinux type is not specified in the workload and is handled by the SCC. When the pods are created by the deployment and the
ReplicaSet, the pods will run with the appropriate profile.Ensure that your SCC is usable by only the correct service account. Refer to Additional resources for more information.
Record profiles from workloads
The Security Profiles Operator can record system calls with ProfileRecording objects to create baseline profiles for applications.
When using the log enricher for recording SELinux profiles, verify the log enricher feature is enabled. See Additional resources for more information.
A container with privileged: true security context restraints prevents log-based recording. Privileged containers are not subject to SELinux policies, and log-based recording makes use of a special SELinux profile to record events.
Procedure
- Create a project by running the following command:terminal
$ oc new-project my-namespace - Label the namespace with
enable-recording=trueby running the following command:terminal$ oc label ns my-namespace spo.x-k8s.io/enable-recording=true - Create a
ProfileRecordingobject containing arecorder: logsvariable:yamlapiVersion: security-profiles-operator.x-k8s.io/v1alpha1 kind: ProfileRecording metadata: namespace: my-namespace name: test-recording spec: kind: SelinuxProfile recorder: logs podSelector: matchLabels: app: my-app - Create a workload to record:yaml
apiVersion: v1 kind: Pod metadata: namespace: my-namespace name: my-pod labels: app: my-app spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: nginx image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21 ports: - containerPort: 8080 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] - name: redis image: quay.io/security-profiles-operator/redis:6.2.1 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] - Confirm the pod is in a
Runningstate by entering the following command:terminal$ oc -n my-namespace get podsExample outputNAME READY STATUS RESTARTS AGE my-pod 2/2 Running 0 18s - Confirm the enricher indicates that it receives audit logs for those containers:terminal
$ oc -n openshift-security-profiles logs --since=1m --selector name=spod -c log-enricherExample outputI0517 13:55:36.383187 348295 enricher.go:376] log-enricher "msg"="audit" "container"="redis" "namespace"="my-namespace" "node"="ip-10-0-189-53.us-east-2.compute.internal" "perm"="name_bind" "pod"="my-pod" "profile"="test-recording_redis_6kmrb_1684331729" "scontext"="system_u:system_r:selinuxrecording.process:s0:c4,c27" "tclass"="tcp_socket" "tcontext"="system_u:object_r:redis_port_t:s0" "timestamp"="1684331735.105:273965" "type"="selinux"
Verification
- Remove the pod:terminal
$ oc -n my-namespace delete pod my-pod - Confirm the Security Profiles Operator reconciles the two SELinux profiles:terminal
$ oc get selinuxprofiles -lspo.x-k8s.io/recording-id=test-recordingExample output for SELinux profileNAME USAGE STATE test-recording-nginx test-recording-nginx.process Installed test-recording-redis test-recording-redis.process Installed
Merge per-container profile instances
To reuse one recorded profile when deploying applications with a ReplicaSet or Deployment, configure the Security Profiles Operator to merge per-container profile instances into a single profile instead of keeping a separate profile for each container.
Procedure
- Edit a
ProfileRecordingobject to include amergeStrategy: containersvariable:yamlapiVersion: security-profiles-operator.x-k8s.io/v1alpha1 kind: ProfileRecording metadata: # The name of the Recording is the same as the resulting SelinuxProfile CRD # after reconciliation. name: test-recording namespace: my-namespace spec: kind: SelinuxProfile recorder: logs mergeStrategy: containers podSelector: matchLabels: app: sp-record - Label the namespace by running the following command:terminal
$ oc label ns my-namespace security.openshift.io/scc.podSecurityLabelSync=false pod-security.kubernetes.io/enforce=privileged pod-security.kubernetes.io/audit=privileged pod-security.kubernetes.io/warn=privileged --overwrite=true - Create the workload with the following YAML:yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: my-namespace spec: replicas: 3 selector: matchLabels: app: sp-record template: metadata: labels: app: sp-record spec: serviceAccountName: spo-record-sa containers: - name: nginx-record image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21 ports: - containerPort: 8080 - To record the individual profiles, delete the deployment by running the following command:terminal
$ oc delete deployment nginx-deploy -n my-namespace - To merge the profiles, delete the profile recording by running the following command:terminal
$ oc delete profilerecording test-recording -n my-namespace - To start the merge operation and generate the results profile, run the following command:terminal
$ oc get selinuxprofiles -lspo.x-k8s.io/recording-id=test-recording -n my-namespaceExample output for SELinux profileNAME USAGE STATE test-recording-nginx-record test-recording-nginx-record.process Installed - To view the permissions used by any of the containers, run the following command:terminal
$ oc get selinuxprofiles test-recording-nginx-record -o yaml
About seLinuxContext: RunAsAny
To record SELinux policies, a webhook injects a permissive SELinux type so the pod logs AVC denials while recording.
The SELinux type makes the pod run in permissive mode, logging all the AVC denials into audit.log. By default, a workload is not allowed to run with a custom SELinux policy, but uses an automatically generated type.
To record a workload, the workload must use a service account that has permissions to use an SCC that allows the webhook to inject the permissive SELinux type. The privileged SCC contains seLinuxContext: RunAsAny.
In addition, the namespace must be labeled with pod-security.kubernetes.io/enforce: privileged if your cluster enables Pod Security Admission, because only the privileged Pod Security Standard allows using a custom SELinux policy.