#97

Feature Request: Enable AppArmor support on Ubuntu playground nodes (currently blocked by SELinux)

Openomkar-shelke25opened 2mo ago

Problem

AppArmor and SELinux are mutually exclusive Linux Security Modules. The Ubuntu-based playground nodes are configured with SELinux by default, which prevents AppArmor from being used entirely.


Error Observed on node-01

After creating profile at /etc/apparmor.d/app-profile and running:

sudo apparmor_parser -r /etc/apparmor.d/app-profile

Got error:

Cache read/write disabled: interface file missing.
(Kernel needs AppArmor 2.4 compatibility patch.)
Warning: unable to find a suitable fs in /proc/mounts, is it mounted?
Use --subdomainfs to override.
sudo systemctl status apparmor
Active: inactive (dead)
mount | grep apparmor
# no output — apparmor fs not mounted

Root Cause

SELinux is active on nodes, blocking AppArmor from loading. AppArmor requires kernel boot argument: security=apparmor This cannot be set at runtime — needs platform-level support.


What We Were Trying to Do

Step 1: AppArmor Profile created on node-01

sudo nano /etc/apparmor.d/app-profile
#include <tunables/global>

profile app-profile flags=(attach_disconnected) {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  # Allow capabilities nginx needs
  capability chown,
  capability dac_override,
  capability setuid,
  capability setgid,
  capability net_bind_service,

  # Entrypoint and scripts
  /docker-entrypoint.sh          rix,
  /docker-entrypoint.d/**        rix,

  # Nginx binary
  /usr/sbin/nginx                rix,
  /usr/bin/**                    rix,
  /bin/**                        rix,
  /sbin/**                       rix,

  # Read access
  /etc/**                        r,
  /proc/**                       r,
  /sys/**                        r,

  # Nginx config and web files
  /usr/share/nginx/**            r,
  /etc/nginx/**                  r,

  # Nginx cache
  /var/cache/nginx/              rw,
  /var/cache/nginx/**            rw,

  # Nginx logs
  /var/log/nginx/**              rw,
  /var/log/**                    rw,

  # Nginx PID and run files
  /tmp/**                        rw,
  /run/**                        rw,
  /var/run/**                    rw,

  # Network
  network inet stream,
  network inet dgram,

  # Dev
  /dev/null                      rw,
  /dev/zero                      r,
  /dev/random                    r,
  /dev/urandom                   r,
  /dev/pts/**                    rw,
}

Step 2: Load Profile on node-01

sudo apparmor_parser -r /etc/apparmor.d/app-profile
sudo aa-status | grep app-profile

Expected output:

   app-profile                     ✅ profile loaded in enforce mode

Step 3: Kubernetes Pod YAML

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: node-01     # pin pod to node-01 where profile is loaded
  securityContext:
    appArmorProfile:
      type: Localhost                   # use locally loaded profile from node-01
      localhostProfile: app-profile     # must match /etc/apparmor.d/app-profile on node-01
  containers:
    - name: app-container
      image: nginx

Step 4: Apply Pod

kubectl apply -f app-pod.yaml
kubectl get pod app-pod -o wide

Expected output:

NAME      READY   STATUS    RESTARTS   AGE   NODE
app-pod   1/1     Running   0          10s   node-01    ✅ pod running on node-01

Step 5: Confirm Profile Active Inside Pod

kubectl exec app-pod -- cat /proc/1/attr/current

Expected output:

app-profile (enforce)               ✅ profile enforcing inside pod

Step 6: Test DENY — Write to Restricted Path

kubectl exec app-pod -- touch /etc/testfile

Expected output:

touch: cannot touch '/etc/testfile': Permission denied    ✅ blocked by AppArmor

Step 7: Test ALLOW — Write to /tmp

kubectl exec app-pod -- touch /tmp/testfile

Expected output:

# no error                          ✅ allowed by AppArmor

Step 8: Kernel Audit Logs Confirm Profile Loaded on node-01

sudo dmesg | grep apparmor

Expected output:

audit: type=1400 apparmor="STATUS" operation="profile_load"
       profile="unconfined" name="app-profile"
       comm="apparmor_parser"        ✅ profile loaded into kernel

audit: type=1400 apparmor="STATUS" operation="profile_replace"
       info="same as current profile, skipping"
       name="app-profile"
       comm="apparmor_parser"        ✅ profile reloaded successfully

Feature Request

Please add boot-level support to enable AppArmor on playground nodes. This requires kernel boot argument:

security=apparmor

This is critical for practicing:

  • Kubernetes AppArmor profiles (CKS exam topic)
  • Pod securityContext with localhostProfile
  • apparmor_parser profile loading on worker nodes
  • Testing enforce vs complain mode

References

Activity

0

No activity yet.