---
title: Managing SELinux profiles
---

# Managing SELinux profiles {#spo-selinux}

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.

> [!IMPORTANT]
> 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 {#spo-create-selinux-profile_spo-selinux}

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 `spod` instance in `spec.selinuxOptions.allowedSystemProfiles`.
- Performs basic validation of the permissions, classes and labels.
- Adds a new keyword `@self` that 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**

1. Create a project by running the following command:

   ```terminal
   $ oc new-project nginx-deploy
   ```
2. Create a policy that can be used with a non-privileged workload by creating the following `SelinuxProfile` object:

   ```yaml
   apiVersion: 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
   ```
3. Wait for `selinuxd` to install the policy by running the following command:

   ```terminal
   $ oc wait --for=condition=ready selinuxprofile nginx-secure
   ```

   ```terminal {title="Example output"}
   selinuxprofile.security-profiles-operator.x-k8s.io/nginx-secure condition met
   ```

   The policies are placed into an `emptyDir` in 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`.
4. Access the pod by running the following command:

   ```terminal
   $ oc -n openshift-security-profiles rsh -c selinuxd ds/spod
   ```

**Verification**

1. View the file contents with `cat` by running the following command:

   ```terminal
   $ cat /etc/selinux.d/nginx-secure.cil
   ```

   ```terminal {title="Example 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 )))
   )
   ```
2. Verify that a policy has been installed by running the following command:

   ```terminal
   $ semodule -l | grep nginx-secure
   ```

   ```terminal {title="Example output"}
   nginx-secure
   ```

## Apply SELinux profiles to a pod {#spo-applying-profiles_spo-selinux}

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](https://kubernetes.io/docs/concepts/security/pod-security-standards/) workloads.

**Procedure**

1. Apply the `scc.podSecurityLabelSync=false` label to the `nginx-deploy` namespace by running the following command:

   ```terminal
   $ oc label ns nginx-deploy security.openshift.io/scc.podSecurityLabelSync=false
   ```
2. Apply the `privileged` label to the `nginx-deploy` namespace by running the following command:

   ```terminal
   $ oc label ns nginx-deploy --overwrite=true pod-security.kubernetes.io/enforce=privileged
   ```
3. 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}'
   ```

   ```terminal {title="Example output"}
   nginx-secure.process
   ```
4. Apply the output string in the workload manifest in the `.spec.containers[].securityContext.seLinuxOptions` attribute:

   ```yaml
   apiVersion: 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.process
   ```

   > [!IMPORTANT]
   > The SELinux `type` must exist before creating the workload.

### Apply SELinux log policies {#spo-selinux-permissive_spo-selinux}

To log policy violations or AVC denials, set the `SElinuxProfile` profile to `permissive`.

> [!IMPORTANT]
> This procedure defines logging policies. It does not set enforcement policies.

**Procedure**

- Add `permissive: true` to an `SElinuxProfile`:

  ```yaml
  apiVersion: security-profiles-operator.x-k8s.io/v1alpha2
  kind: SelinuxProfile
  metadata:
    name: nginx-secure
  spec:
    permissive: true
  ```

### Binding workloads to profiles with ProfileBindings {#spo-binding-workloads_spo-selinux}

You can use the `ProfileBinding` resource to bind a security profile to the `SecurityContext` of a container.

**Procedure**

1. To bind a pod that uses a `quay.io/security-profiles-operator/test-nginx-unprivileged:1.21` image to the example `SelinuxProfile` profile, create a `ProfileBinding` object in the same namespace with the pod and the `SelinuxProfile` objects:

   ```yaml
   apiVersion: 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.21
   ```

   where:

   `spec.profileRef.kind`
   :   Specifies the kind of the profile.

   `spec.profileRef.name`
   :   Specifies the name of the profile.

   `spec.image`
   :   Allows you to enable a default security profile by using a wildcard in the image attribute: `image: "*"`

   > [!IMPORTANT]
   > Using the `image: "*"` wildcard attribute binds all new pods with a default security profile in a given namespace.
2. Label the namespace with `enable-binding=true` by running the following command:

   ```terminal
   $ oc label ns my-namespace spo.x-k8s.io/enable-binding=true
   ```
3. Define a pod named `test-pod.yaml`:

   ```yaml
   apiVersion: v1
   kind: Pod
   metadata:
     name: test-pod
   spec:
     containers:
     - name: test-container
       image: quay.io/security-profiles-operator/test-nginx-unprivileged:1.21
   ```
4. Create the pod:

   ```terminal
   $ oc create -f test-pod.yaml
   ```

   > [!NOTE]
   > If the pod already exists, you must re-create the pod for the binding to work properly.

**Verification**

- Confirm the pod inherits the `ProfileBinding` by running the following command:

  ```terminal
  $ oc get pod test-pod -o jsonpath='{.spec.containers[*].securityContext.seLinuxOptions.type}'
  ```

  ```terminal {title="Example output"}
  profile.process
  ```

### Replicate controllers and SecurityContextConstraints {#spo-replicating-controllers_spo-selinux}

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**

1. Create a project by running the following command:

   ```terminal
   $ oc new-project nginx-secure
   ```
2. Create the following `RoleBinding` object to allow SELinux policies to be used in the `nginx-secure` namespace:

   ```yaml
   kind: 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
   ```
3. Create the `Role` object:

   ```yaml
   apiVersion: 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
   ```
4. Create the `ServiceAccount` object:

   ```yaml
   apiVersion: v1
   kind: ServiceAccount
   metadata:
     creationTimestamp: null
     name: spo-deploy-test
     namespace: nginx-secure
   ```
5. Create the `Deployment` object:

   ```yaml
   apiVersion: 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: 8080
   ```

   The `spec.template.spec.securityContext.seLinuxOptions.type` must exist before the Deployment is created.

   > [!NOTE]
   > The 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 {#spo-recording-profiles_spo-selinux}

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.

> [!NOTE]
> 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**

1. Create a project by running the following command:

   ```terminal
   $ oc new-project my-namespace
   ```
2. Label the namespace with `enable-recording=true` by running the following command:

   ```terminal
   $ oc label ns my-namespace spo.x-k8s.io/enable-recording=true
   ```
3. Create a `ProfileRecording` object containing a `recorder: logs` variable:

   ```yaml
   apiVersion: 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
   ```
4. 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]
   ```
5. Confirm the pod is in a `Running` state by entering the following command:

   ```terminal
   $ oc -n my-namespace get pods
   ```

   ```terminal {title="Example output"}
   NAME     READY   STATUS    RESTARTS   AGE
   my-pod   2/2     Running   0          18s
   ```
6. 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-enricher
   ```

   ```terminal {title="Example output"}
   I0517 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**

1. Remove the pod:

   ```terminal
   $ oc -n my-namespace delete pod my-pod
   ```
2. Confirm the Security Profiles Operator reconciles the two SELinux profiles:

   ```terminal
   $ oc get selinuxprofiles -lspo.x-k8s.io/recording-id=test-recording
   ```

   ```terminal {title="Example output for SELinux profile"}
   NAME                   USAGE                                 STATE
   test-recording-nginx   test-recording-nginx.process   Installed
   test-recording-redis   test-recording-redis.process   Installed
   ```

### Merge per-container profile instances {#spo-container-profile-instances_spo-selinux}

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**

1. Edit a `ProfileRecording` object to include a `mergeStrategy: containers` variable:

   ```yaml
   apiVersion: 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
   ```
2. 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
   ```
3. 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
   ```
4. To record the individual profiles, delete the deployment by running the following command:

   ```terminal
   $ oc delete deployment nginx-deploy -n my-namespace
   ```
5. To merge the profiles, delete the profile recording by running the following command:

   ```terminal
   $ oc delete profilerecording test-recording -n my-namespace
   ```
6. 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-namespace
   ```

   ```terminal {title="Example output for SELinux profile"}
   NAME                          USAGE                            STATE
   test-recording-nginx-record   test-recording-nginx-record.process   Installed
   ```
7. 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 {#spo-selinux-runasany_spo-selinux}

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.

**Additional resources**
{._additional-resources}

- [Managing security context constraints](/openshift-docs-markdown/authentication/managing-security-context-constraints#managing-pod-security-policies)
- [Managing SCCs in OpenShift](https://cloud.redhat.com/blog/managing-sccs-in-openshift)
- [About security profiles](/openshift-docs-markdown/security/security_profiles_operator/spo-understanding#spo-about_spo-understanding)
- [Use the log enricher](/openshift-docs-markdown/security/security_profiles_operator/spo-advanced#spo-log-enricher_spo-advanced)
- [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/)
- [Pod Security Standard](https://kubernetes.io/docs/concepts/security/pod-security-standards/#privileged)
