Day 5 — Getting started with AppArmor
The main behind a tool like AppArmor is to reduce the application attack surface. It creates one more layer of security between our application and system functionality. Let say our use case is we want our container not to write inside the specific filesystem or directory. This is where we can use tools like AppArmor. AppArmor implements fine-grained control over the processes running inside the container. It’s a Linux Security Module that confines your program to a limited set of resources.
Installation
AppArmor is installed by default in most Linux distributions. In the case of Ubuntu, you can verify it using
# apt list --installed |grep -i AppArmorapparmor/bionic-updates,bionic-security,now 2.12-4ubuntu5.1 amd64 [installed]
apparmor-profiles/bionic-updates,bionic-updates,bionic-security,bionic-security,now 2.12-4ubuntu5.1 all [installed]
apparmor-utils/bionic-updates,bionic-security,now 2.12-4ubuntu5.1 amd64 [installed]
libapparmor1/bionic-updates,bionic-security,now 2.12-4ubuntu5.1 amd64 [installed]
OR
- Install it using the below command
sudo apt install apparmor-profiles
- To check if the AppArmor service is up and running.
# systemctl status apparmor
● apparmor.service - AppArmor initialization
Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; vendor preset: enabled)
Active: active (exited) since Sun 2021-10-31 00:46:35 UTC; 18min ago
Docs: man:apparmor(7)
http://wiki.apparmor.net/
Main PID: 482 (code=exited, status=0/SUCCESS)
Tasks: 0 (limit: 2314)
CGroup: /system.slice/apparmor.service
- The next step is to verify if the AppArmor module is enabled in all the nodes in the Kubernetes cluster. The value of Y means the AppArmor module is already loaded.
# cat /sys/module/apparmor/parameters/enabled
Y
- To apply AppArmor to your application, it’s applied as a profile. This profile must be loaded inside the kernel. As we can see, several profiles are already loaded in this host. These profiles are simple text files and define which an application can use resources.
# cat /sys/kernel/security/apparmor/profiles
/usr/{sbin/traceroute,bin/traceroute.db} (complain)
/usr/sbin/smbldap-useradd (complain)
/usr/sbin/smbldap-useradd///etc/init.d/nscd (complain)
/usr/sbin/smbd (complain)
/usr/sbin/nscd (complain)
/usr/sbin/nmbd (complain)
/usr/sbin/mdnsd (complain)
/usr/sbin/identd (complain)
/usr/sbin/dovecot (complain)
/usr/sbin/dnsmasq (complain)
/usr/sbin/dnsmasq//libvirt_leaseshelper (complain)
/usr/sbin/avahi-daemon (complain)
/usr/lib/dovecot/ssl-params (complain)
/usr/lib/dovecot/pop3-login (complain)
/usr/lib/dovecot/pop3 (complain)
/usr/lib/dovecot/managesieve-login (complain)
/usr/lib/dovecot/managesieve (complain)
/usr/lib/dovecot/log (complain)
/usr/lib/dovecot/lmtp (complain)
/usr/lib/dovecot/imap-login (complain)
/usr/lib/dovecot/imap (complain)
/usr/lib/dovecot/dovecot-lda (complain)
/usr/lib/dovecot/dovecot-lda///usr/sbin/sendmail (complain)
/usr/lib/dovecot/dovecot-auth (complain)
/usr/lib/dovecot/dict (complain)
/usr/lib/dovecot/deliver (complain)
/usr/lib/dovecot/config (complain)
/usr/lib/dovecot/auth (complain)
/usr/lib/dovecot/anvil (complain)
/usr/lib/chromium-browser/chromium-browser (complain)
/usr/lib/chromium-browser/chromium-browser//xdgsettings (complain)
/usr/lib/chromium-browser/chromium-browser//sanitized_helper (enforce)
/usr/lib/chromium-browser/chromium-browser//lsb_release (complain)
/usr/lib/chromium-browser/chromium-browser//chromium_browser_sandbox (complain)
/usr/lib/chromium-browser/chromium-browser//browser_openjdk (enforce)
/usr/lib/chromium-browser/chromium-browser//browser_java (enforce)
syslogd (complain)
syslog-ng (complain)
klogd (complain)
ping (complain)
docker-default (enforce)
/usr/sbin/tcpdump (enforce)
/usr/lib/snapd/snap-confine (enforce)
/usr/lib/snapd/snap-confine//mount-namespace-capture-helper (enforce)
man_groff (enforce)
man_filter (enforce)
/usr/bin/man (enforce)
/usr/bin/lxc-start (enforce)
/usr/lib/connman/scripts/dhclient-script (enforce)
/usr/lib/NetworkManager/nm-dhcp-helper (enforce)
/usr/lib/NetworkManager/nm-dhcp-client.action (enforce)
/sbin/dhclient (enforce)
lxc-container-default-with-nesting (enforce)
lxc-container-default-with-mounting (enforce)
lxc-container-default-cgns (enforce)
lxc-container-default (enforce)
- The simple Apparmor profile will look like this. What this profile is doing it’s denying all write access to the root filesystem.
#include <tunables/global>
profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
#include <abstractions/base>
file,
# Deny all file writes.
deny /** w,
}
Reference: https://kubernetes.io/docs/tutorials/clusters/apparmor/
- To check which are the profile is loaded, you can use the aa-status command. As you can see, 56 profiles are already loaded, and 19 are loaded in enforce mode.
# aa-status
apparmor module is loaded.
56 profiles are loaded.
19 profiles are in enforce mode.
/sbin/dhclient
/usr/bin/lxc-start
/usr/bin/man
/usr/lib/NetworkManager/nm-dhcp-client.action
/usr/lib/NetworkManager/nm-dhcp-helper
/usr/lib/chromium-browser/chromium-browser//browser_java
/usr/lib/chromium-browser/chromium-browser//browser_openjdk
/usr/lib/chromium-browser/chromium-browser//sanitized_helper
/usr/lib/connman/scripts/dhclient-script
/usr/lib/snapd/snap-confine
/usr/lib/snapd/snap-confine//mount-namespace-capture-helper
/usr/sbin/tcpdump
docker-default
lxc-container-default
lxc-container-default-cgns
lxc-container-default-with-mounting
lxc-container-default-with-nesting
man_filter
man_groff
37 profiles are in complain mode.
/usr/lib/chromium-browser/chromium-browser
/usr/lib/chromium-browser/chromium-browser//chromium_browser_sandbox
/usr/lib/chromium-browser/chromium-browser//lsb_release
/usr/lib/chromium-browser/chromium-browser//xdgsettings
/usr/lib/dovecot/anvil
/usr/lib/dovecot/auth
/usr/lib/dovecot/config
/usr/lib/dovecot/deliver
/usr/lib/dovecot/dict
/usr/lib/dovecot/dovecot-auth
/usr/lib/dovecot/dovecot-lda
/usr/lib/dovecot/dovecot-lda///usr/sbin/sendmail
/usr/lib/dovecot/imap
/usr/lib/dovecot/imap-login
/usr/lib/dovecot/lmtp
/usr/lib/dovecot/log
/usr/lib/dovecot/managesieve
/usr/lib/dovecot/managesieve-login
/usr/lib/dovecot/pop3
/usr/lib/dovecot/pop3-login
/usr/lib/dovecot/ssl-params
/usr/sbin/avahi-daemon
/usr/sbin/dnsmasq
/usr/sbin/dnsmasq//libvirt_leaseshelper
/usr/sbin/dovecot
/usr/sbin/identd
/usr/sbin/mdnsd
/usr/sbin/nmbd
/usr/sbin/nscd
/usr/sbin/smbd
/usr/sbin/smbldap-useradd
/usr/sbin/smbldap-useradd///etc/init.d/nscd
/usr/{sbin/traceroute,bin/traceroute.db}
klogd
ping
syslog-ng
syslogd
14 processes have profiles defined.
14 processes are in enforce mode.
docker-default (2300)
docker-default (2365)
docker-default (2371)
docker-default (2410)
docker-default (2497)
docker-default (2562)
docker-default (2653)
docker-default (2685)
docker-default (3412)
docker-default (3419)
docker-default (4481)
docker-default (4500)
docker-default (4687)
docker-default (4754)
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
- These profiles can be loaded in three different modes
* enforce: AppArmor monitors and enforce the profile rules
* complain: AppArmor will not enforce any profile rules but log the events
* unconfined: Nothing is enforced and it doesn't even log the events
- To use AppArmor with pods. go to and create the file default deny file
cd /etc/apparmor.d
# cat default.deny
#include <tunables/global>profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
#include <abstractions/base>file,# Deny all file writes.
deny /** w,
}
- To use AppArmor with Pod, you need to use the annotations whose format is key and value
- Remember with annotations, you need to specify the container name, not the pod name
key: container.apparmor.security.beta.kubernetes.io/<container_name>
NOTE: Container runtime must need to support AppArmor, and profile/AppArmor must be present on each node.
apiVersion: v1
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
containers:
- name: hello
image: busybox
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
Reference: https://kubernetes.io/docs/tutorials/clusters/apparmor/
- To load the profile
apparmor_parser -v default.deny
Addition succeeded for "k8s-apparmor-example-deny-write".
- Also, not the file name and the profile name is different. With annotation specify the profile name(k8s-apparmor-example-deny-write) not the file name(default.deny).
- Create the pod
kubectl create -f /tmp/pod.yaml
pod/hello-apparmor created
- Verify the profile is loaded properly
# kubectl describe pod hello-apparmor |grep -i apparmor
Name: hello-apparmor
Annotations: container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
- Test it. As you can see the file creation inside the / is denied
# kubectl exec -it hello-apparmor -- touch /tmp/test
touch: /tmp/test: Permission denied
command terminated with exit code 1
- You can create your custom profile use aa-genprof command http://manpages.ubuntu.com/manpages/bionic/man8/aa-genprof.8.html
Conclusion
AppArmor adds one more layer of security between our application and system functionality. But getting a written profile is entirely depends upon your application and requires lots of work.