Filters
How to search
  • Plain words find names, texts and examples.
  • "two words" finds the exact phrase.
  • --name or -x finds an option.
  • #word looks only inside commands.
  • @grep finds lessons and labs that use grep.
  • Filters: cat:network · in:labs · in:notes · level:rhcsa · ch:8 · root:yes. Put ! before a word to leave it out. OR between words means one of them.
  • ↑ ↓ to move · Enter to open · Esc to close · / to search

Chapter 3Name in the course: System hardening

Harden the nodes with AppArmor and seccomp

A container escape lands on the node. Keep the node small, and limit what containers may do with the kernel: AppArmor profiles limit file and capability access; seccomp profiles limit system calls. Both are set in the pod spec.

  • 4 Lessons
  • 6 Commands

$ run as a normal user# run as root (or with sudo)

Skills in this chapter

  • Small host OS, AppArmor, seccomp, IAM and network access

3.1

Keep the host small

Every package, service and open port on a node is a possible way in.

systemctl list-units --type=service --state=running
ss -tulpn
apt list --installed | wc -l
systemctl disable --now snapd cups avahi-daemon
echo "install dccp /bin/true" > /etc/modprobe.d/disable-dccp.conf
  • Use a minimal OS image (or a container-optimised OS).
  • No SSH passwords, no shared admin accounts.
  • Cloud IAM: the node's identity gets only the rights the kubelet needs.

3.2

Use AppArmor profiles

An AppArmor profile on the node says what a program may do. The pod asks for it in securityContext.appArmorProfile.

/etc/apparmor.d/k8s-deny-write
#include <tunables/global>
profile k8s-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>
  file,
  deny /** w,
}
apparmor_parser -q /etc/apparmor.d/k8s-deny-write
aa-status | grep k8s-deny-write
pod-apparmor.yaml
apiVersion: v1
kind: Pod
metadata: { name: ro-app, namespace: shop }
spec:
  nodeName: worker1
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: k8s-deny-write
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "touch /tmp/x; sleep 3600"]
kubectl logs ro-app -n shop
touch: /tmp/x: Permission denied

Remember

The profile must be loaded on the node where the pod runs — pin the pod (or load the profile on every node).

3.3

Use seccomp profiles

seccomp filters system calls. RuntimeDefault (the runtime's safe list) fits almost every app; custom profiles live in /var/lib/kubelet/seccomp/.

pod-seccomp.yaml
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: nginx:1.27
      securityContext:
        seccompProfile:
          type: Localhost
          localhostProfile: profiles/audit.json
/var/lib/kubelet/seccomp/profiles/audit.json
{ "defaultAction": "SCMP_ACT_LOG" }
TypeMeaning
RuntimeDefaultThe container runtime's default filter
LocalhostA JSON profile on the node (path relative to the seccomp folder)
UnconfinedNo filter — avoid

3.4

Restrict capabilities and privilege

Drop all Linux capabilities and add back only what the app needs.

container securityContext
securityContext:
  allowPrivilegeEscalation: false
  privileged: false
  capabilities:
    drop: ["ALL"]
    add: ["NET_BIND_SERVICE"]

Exam tip

Read the task for the exact place: some settings are pod-level (spec.securityContext), some container-level (spec.containers[].securityContext) — capabilities and allowPrivilegeEscalation only exist per container.

All commands in this chapter