Skip to content

AppArmor Docker configuration #13972

@Juma7C9

Description

@Juma7C9

The current docker image does not work OOTB on a host with AppArmor enabled (e.g. any recent Debian/Ubuntu release), as the default AppArmor profile provided by Docker is too restrictive.

A possible workaround is to pass apparmor=unconfined as a security option (see #13085), but a better option would be to use a custom profile adapted to the application requirements, without throwing away the rest of the security features.

Unfortunately I wasn't able to find an easy way to inspect and adapt the profile used by Docker at runtime, so I've used the template from the repo (see github.com/moby/profiles/apparmor/template.go), to which I've just added the few syscalls which were previously blocked (mount and userns):

#include <tunables/global>

profile collabora-docker flags=(attach_disconnected,mediate_deleted) {

  #include <abstractions/base>

  network,
  capability,
  file,
  umount,
  mount,   # <-- was `deny mount`
  userns,  # <-- was undefined, so blocked by default

  # Host (privileged) processes may send signals to container processes.
  signal (receive) peer=unconfined,
  # runc may send signals to container processes (for "docker stop").
  signal (receive) peer=runc,
  # crun may send signals to container processes (for "docker stop" when used with crun OCI runtime).
  signal (receive) peer=crun,
  # dockerd may send signals to container processes (for "docker kill").
  signal (receive) peer=unconfined,
  # Container processes may send signals amongst themselves.
  signal (send,receive) peer=@{profile_name}, # <-- Replaced `peer=docker-default` with the current one

  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
  # deny write to files not in /proc/<number>/** or /proc/sys/**
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9/]*}/** w,
  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/kcore rwklx,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/devices/virtual/powercap/** rwklx,
  deny /sys/kernel/security/** rwklx,

  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
  ptrace (trace,read,tracedby,readby) peer=@{profile_name} # <-- Same as above
}
A few notes, mostly for a future myself.
  • I named the profile collabora-docker, but the name can be any "normal" string -- provided that it does not conflict with the name or path of any executable on the system, otherwise AppArmor will automatically apply the profile to it).
  • Any deny ... instruction will silently block the execution of the corresponding entry (i.e. without leaving a message to syslog); similarly the allowed instructions will be silently allowed. All the instructions not defined in the profile will be denied with a corresponding message to the syslog. It is also possible to prepend an instruction with audit ..., to print a message to the syslog whenever it is encountered (either allowed or denied).
  • I replaced peer=docker-default with peer=@{profile_name} so not to break IPC inside the container (signals and ptrace). Conversely it should block them across containers (being in different peer groups, i.e. under different profiles), but that should not be an issue as long as no signals are sent between processes in different containers (if that's even possible, as each container is in its own PID namespace).
  • Many rules are automatically inherited by the #include clauses, so the signal and ptrace instructions are partially redundant (check the preprocessed output using apparmor_parser -p /path/to/profile).

To activate the profile the best way is to place the file under /etc/apparmor.d, then run systemctl reload apparmor, which will automatically load all the profiles in the aforementioned dir.

I tested the profile under Debian 13/Trixie (Docker 26.1.5, AppArmor 4.1.0) with a userns profile using the following options, and it seems to be working without any issue nor complaints in the log:

security_opt:
  - seccomp=cool-seccomp-profile.json
  - apparmor=collabora-docker
  - no-new-privileges=true

Obviously a more restrictive profile --designed around the exact operations needed by the application-- would be even better, but that's well above my capabilities ^.^

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions