0

AppArmor profile:

cat /etc/apparmor.d/test-deny-write 
#include <tunables/global>

profile test-deny-write flags=(attach_disconnected) {
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

Loaded:

sudo apparmor_parser --Complain test-deny-write

sudo aa-status
apparmor module is loaded.
38 profiles are loaded.
31 profiles are in enforce mode.
   /snap/snapd/20290/usr/lib/snapd/snap-confine
   /snap/snapd/20290/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/bin/man
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/lib/NetworkManager/nm-dhcp-helper
   /usr/lib/connman/scripts/dhclient-script
   /usr/lib/snapd/snap-confine
   /usr/lib/snapd/snap-confine//mount-namespace-capture-helper
   /usr/sbin/chronyd
   /usr/sbin/tcpdump
   /{,usr/}sbin/dhclient
   cri-containerd.apparmor.d
   lsb_release
   man_filter
   man_groff
   nvidia_modprobe
   nvidia_modprobe//kmod
   snap-update-ns.google-cloud-cli
   snap-update-ns.lxd
   snap.lxd.activate
   snap.lxd.benchmark
   snap.lxd.buginfo
   snap.lxd.check-kernel
   snap.lxd.daemon
   snap.lxd.hook.configure
   snap.lxd.hook.install
   snap.lxd.hook.remove
   snap.lxd.lxc
   snap.lxd.lxc-to-lxd
   snap.lxd.lxd
   snap.lxd.migrate
7 profiles are in complain mode.
   snap.google-cloud-cli.anthoscli
   snap.google-cloud-cli.bq
   snap.google-cloud-cli.docker-credential-gcloud
   snap.google-cloud-cli.gcloud
   snap.google-cloud-cli.gsutil
   snap.google-cloud-cli.kubectl
   test-deny-write
13 processes have profiles defined.
13 processes are in enforce mode.
   /usr/sbin/chronyd (1376) 
   /usr/sbin/chronyd (1377) 
   /metrics-server (12860) cri-containerd.apparmor.d
   /usr/bin/dumb-init (13041) cri-containerd.apparmor.d
   /nginx-ingress-controller (13053) cri-containerd.apparmor.d
   /manager (13086) cri-containerd.apparmor.d
   /usr/local/nginx/sbin/nginx (13114) cri-containerd.apparmor.d
   /manager (13136) cri-containerd.apparmor.d
   /usr/local/nginx/sbin/nginx (13155) cri-containerd.apparmor.d
   /usr/local/nginx/sbin/nginx (13156) cri-containerd.apparmor.d
   /usr/local/nginx/sbin/nginx (13157) cri-containerd.apparmor.d
   /manager (13250) cri-containerd.apparmor.d
   /manager (13565) cri-containerd.apparmor.d
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.

Pod definition:

cat n.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  annotations:
    container.apparmor.security.beta.kubernetes.io/nginx: localhost/test-deny-write
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    ports:
    - containerPort: 80
  dnsPolicy: ClusterFirst
  restartPolicy: Always

kubectl apply -f n.yaml 
pod/nginx created

kubectl get pods
NAME                              READY   STATUS             RESTARTS     AGE
...
nginx                             0/1     CrashLoopBackOff   1 (3s ago)   7s

kubectl logs nginx
/docker-entrypoint.sh: 13: cannot create /dev/null: Permission denied
/docker-entrypoint.sh: No files found in /docker-entrypoint.d/, skipping configuration
2024/01/06 11:27:50 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)

Now AppArmor works in enforce mode despite profile being loaded in complain mode. Only way to make it work as complain not enforce is to change the profile definition from deny to audit:

  ...
  audit /** w,
  ...

Then reload the profile in --Complain mode again. This makes the pod start and events are getting audited:

kubectl logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/06 11:36:57 [notice] 1#1: using the "epoll" event method
2024/01/06 11:36:57 [notice] 1#1: nginx/1.25.3
2024/01/06 11:36:57 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 
2024/01/06 11:36:57 [notice] 1#1: OS: Linux 5.15.0-1047-gcp
2024/01/06 11:36:57 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/01/06 11:36:57 [notice] 1#1: start worker processes
2024/01/06 11:36:57 [notice] 1#1: start worker process 29
2024/01/06 11:36:57 [notice] 1#1: start worker process 30

kubectl exec -it nginx -- sh
# echo a > a.log

sudo dmesg
...
[ 1943.792266] audit: type=1400 audit(1704541508.355:101): apparmor="AUDIT" operation="open" profile="test-deny-write" name="/dev/tty" pid=16159 comm="sh" requested_mask="w" fsuid=0 ouid=0
[ 1950.228525] audit: type=1400 audit(1704541514.792:102): apparmor="AUDIT" operation="mknod" profile="test-deny-write" name="/a.log" pid=16159 comm="sh" requested_mask="c" fsuid=0 ouid=0
[ 1950.234413] audit: type=1400 audit(1704541514.800:103): apparmor="AUDIT" operation="open" profile="test-deny-write" name="/a.log" pid=16159 comm="sh" requested_mask="wc" fsuid=0 ouid=0

But this defeats the purpose of complain mode.

System details:

uname -a
Linux node-qf1h 5.15.0-1047-gcp #55~20.04.1-Ubuntu SMP Wed Nov 15 11:38:25 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

kubectl version
Client Version: v1.28.0
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.28.0

AppArmor docs is not very helpful here: https://gitlab.com/apparmor/apparmor/-/wikis/Complain-Mode

Complain mode

learning application behavior TODO

complain mode and deny rules

One some versions of apparmor broken .... TODO

Is this AppArmor bug or did I miss something?

0

2 Answers 2

0

You didn't include complain, in the profile

#include <tunables/global>

profile test-deny-write flags=(attach_disconnected) {
  complain,
  #include <abstractions/base>

  file,

  # Deny all file writes.
  deny /** w,
}

As you can see the line complain, misses in your configuration. the line complain, is used to specify that this profile should operate in complain mode

2
  • What would be the point of using --Complain flag then, if the file needs to be modified anyway?
    – ipastusi
    Commented Jan 6 at 14:58
  • I don't know, that's something you need to ask the developers.I'm just the messenger I didn't design it. But when running in production I could imagine that most profiles are in enforced mode, and only with a change it's allowed to put a profile in complain mode , and running aa-complain is auditable
    – Turdie
    Commented Jan 6 at 16:08
0

The --complain flag works, and affects enforcement of the profile on the container host until you restart it or reload the profile.

The --complain flag does not affect your pod, because the container manager your are using reloads the profile specifically for the pod.

You can use aa-complain to set a profile to run in complain mode in a way that persists across restarts, reloads and pods.

1
  • There is no difference between sudo aa-complain /etc/apparmor.d/test-deny-write and sudo apparmor_parser --Complain /etc/apparmor.d/test-deny-write for the original profile. All processes are still running in enforce mode.
    – ipastusi
    Commented Jan 7 at 11:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .