Skip to content

Instantly share code, notes, and snippets.

@t-book
Last active April 18, 2024 15:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-book/4f8773aba1fa64b07345b825e61eb7b0 to your computer and use it in GitHub Desktop.
Save t-book/4f8773aba1fa64b07345b825e61eb7b0 to your computer and use it in GitHub Desktop.

Apparmor

https://wiki.ubuntu.com/AppArmor

Apparmor can be used to limit different permissions for a userspace on side of the kernel. In other words we can decide what a program (... inside a docker container) can do. Ubuntu runs apparmor by default. This Readme targets docker and skips the aa_genprof workflow (see: https://www.youtube.com/watch?v=Uq1d60TLebE&t=155s) for standalone applications (Haven't found a way to use it with docker daemon)

To follow these steps you need to install apparmor-utils

sudo apt-get install -y apparmor-utils

Overview Linux capabilities: https://blog.container-solutions.com/linux-capabilities-in-practice

Workflow

Check the status

sudo apparmor_status

create and enable a new profile from scatch

vim my_profile
sudo cp my_profile /etc/apparmor/my_profile
sudo apparmor_parser -r -W /etc/apparmor.d/containers/my_profile

... or use a tool to generate it

A good tool for profile generation: https://github.com/genuinetools/bane

Force a profile mode

Apparmor profiles can have different modes. We can decide if the profile is active (enfore) or if it just complains.

  • sudo aa-enforce /etc/apparmar.d/my_profile
  • sudo aa-complain /etc/apparmar.d/my_profile

We can check rule violation in logs grep -i complain /var/log/syslog

Docker and Apparmor

On Ubuntu docker per default applies a standard profile called docker-default. As one cannot find this profile readable on disc (See: https://lucascavalare.github.io/2020-03-15-AppArmor_Docker/), this is how it looks like:

#include <tunables/global>


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

  #include <abstractions/base>


  network,
  capability,
  file,
  umount,


  signal (receive) peer=unconfined
,

  signal (send,receive) peer=docker-default,


  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 mount,

  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/kernel/security/** rwklx,


  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
  ptrace (trace,read) peer=docker-default,

}

We can start from this default template for defining more restrictive profiles.

setup the profile

  • sudo cp docker-default /etc/apparmor.d/containers/docker-default-secure
  • sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-default-secure

edit the profile

  • sudo vim /etc/apparmor.d/containers/docker-default-secure # make changes

for example deny access to read/write to /tmp/a/**

deny /tmp/a/** rwlx,

or deny some linux capability, which is interesting to cut permissions for a root user inside the container:

deny capability fsetid,

After changing the profile we need to reload it:

  • sudo apparmor_parser -r /etc/apparmor.d/containers/docker-default-secure reload, changes are immediately applied to containers

Use the profile with docker

  • docker run --security-opt="apparmor:docker-default-secure" -d nginx

In case of a violation you will see a denied message in syslog or the containers stdout.

or in docker-compose:

  # Nginx is serving django static and media files and proxies to django and geonode
  geonode:
    image: geonode/nginx:3.x
    build: ./scripts/docker/nginx/
    container_name: nginx4${COMPOSE_PROJECT_NAME}
    environment:
      - HTTPS_HOST=${HTTPS_HOST}
      - HTTP_HOST=${HTTP_HOST}
      - HTTPS_PORT=${HTTPS_PORT}
      - HTTP_PORT=${HTTP_PORT}
      - LETSENCRYPT_MODE=${LETSENCRYPT_MODE}
      - RESOLVER=127.0.0.11
    ports:
      - "${HTTP_PORT}:80"
      - "${HTTPS_PORT}:443"
    volumes:
      - nginx-confd:/etc/nginx
      - nginx-certificates:/geonode-certificates
      - statics:/mnt/volumes/statics
    restart: on-failure
    security_opt:
      - apparmor:docker-default-secure

test inside a container

... try to read the user capabilities

https://man7.org/linux/man-pages/man7/capabilities.7.html

apt update
apt-get install libcap2-bin
capsh --print

or try to create a file (see deny example above)

touch /tmp/a/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment