Today I will describe how to deal with missing AppArmor profiles for microk8s on LXD.
Initial information
Guest operating system version.
$ lsb_release -a
No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04 LTS Release: 20.04 Codename: focal
LXD version on the host operating system.
$ lxd --version
4.0.2
Install apparmor-utils
inside the guest operating system.
$ sudo apt install apparmor-utils
Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: python3-apparmor python3-libapparmor Suggested packages: vim-addon-manager The following NEW packages will be installed: apparmor-utils python3-apparmor python3-libapparmor 0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded. Need to get 157 kB of archives. After this operation, 966 kB of additional disk space will be used. Do you want to continue? [Y/n] y Get:1 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 python3-libapparmor amd64 2.13.3-7ubuntu5.1 [26.7 kB] Get:2 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 python3-apparmor amd64 2.13.3-7ubuntu5.1 [78.6 kB] Get:3 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 apparmor-utils amd64 2.13.3-7ubuntu5.1 [51.4 kB] Fetched 157 kB in 0s (589 kB/s) Selecting previously unselected package python3-libapparmor. (Reading database ... 18379 files and directories currently installed.) Preparing to unpack .../python3-libapparmor_2.13.3-7ubuntu5.1_amd64.deb ... Unpacking python3-libapparmor (2.13.3-7ubuntu5.1) ... Selecting previously unselected package python3-apparmor. Preparing to unpack .../python3-apparmor_2.13.3-7ubuntu5.1_amd64.deb ... Unpacking python3-apparmor (2.13.3-7ubuntu5.1) ... Selecting previously unselected package apparmor-utils. Preparing to unpack .../apparmor-utils_2.13.3-7ubuntu5.1_amd64.deb ... Unpacking apparmor-utils (2.13.3-7ubuntu5.1) ... Setting up python3-libapparmor (2.13.3-7ubuntu5.1) ... Setting up python3-apparmor (2.13.3-7ubuntu5.1) ... Setting up apparmor-utils (2.13.3-7ubuntu5.1) ...
The issue
microk8s
does not start as it cannot change apparmor profile
.
$ microk8s
cannot change profile for the next exec call: No such file or directory
Guest logs will indicate the same issue.
$ sudo tail /var/log/kern.log
[...] Jul 4 20:51:55 kube-worker-3 kernel: [ 40.914921] audit: type=1400 audit(1593895915.057:50): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-containerd" pid=6308 comm="snap-confine" Jul 4 20:51:55 kube-worker-3 kernel: [ 40.915857] audit: type=1400 audit(1593895915.057:52): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-scheduler" pid=6314 comm="snap-confine" Jul 4 20:51:55 kube-worker-3 kernel: [ 40.917363] audit: type=1400 audit(1593895915.061:54): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-proxy" pid=6313 comm="snap-confine" Jul 4 20:51:55 kube-worker-3 kernel: [ 40.917501] audit: type=1400 audit(1593895915.061:55): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-apiserver" pid=6306 comm="snap-confine" Jul 4 20:52:03 kube-worker-3 kernel: [ 48.863759] audit: type=1400 audit(1593895923.034:146): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-proxy" pid=8382 comm="snap-confine" Jul 4 20:52:03 kube-worker-3 kernel: [ 48.867327] audit: type=1400 audit(1593895923.034:149): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-etcd" pid=8379 comm="snap-confine" Jul 4 20:52:03 kube-worker-3 kernel: [ 48.874465] audit: type=1400 audit(1593895923.042:152): apparmor="DENIED" operation="change_onexec" info="label not found" error=-2 profile="/snap/core/9436/usr/lib/snapd/snap-confine" name="snap.microk8s.daemon-apiserver" pid=8375 comm="snap-confine" [...]
Issue details
Execute microk8s
snap using debug mode to confirm that it cannot change AppArmor profile to snap.microk8s.microk8
.
$ SNAPD_DEBUG=1 SNAP_DEBUG_CONFINE=1 microk8s
2020/07/04 21:37:58.884019 cmd_linux.go:207: DEBUG: restarting into "/snap/core/current/usr/bin/snap" 2020/07/04 21:37:58.900289 cmd_run.go:398: DEBUG: SELinux not enabled DEBUG: umask reset, old umask was 02 DEBUG: security tag: snap.microk8s.microk8s DEBUG: executable: /snap/core/9665/usr/lib/snapd/snap-exec DEBUG: confinement: classic DEBUG: base snap: core DEBUG: ruid: 2018, euid: 0, suid: 0 DEBUG: rgid: 2018, egid: 2018, sgid: 2018 DEBUG: apparmor label on snap-confine is: /snap/core/9665/usr/lib/snapd/snap-confine DEBUG: apparmor mode is: enforce DEBUG: preparing classic execution environment DEBUG: creating lock directory /run/snapd/lock (if missing) DEBUG: set_effective_identity uid:0 (change: no), gid:0 (change: yes) DEBUG: opening lock directory /run/snapd/lock DEBUG: set_effective_identity uid:0 (change: no), gid:2018 (change: yes) DEBUG: opening lock file: /run/snapd/lock/microk8s.lock DEBUG: set_effective_identity uid:0 (change: no), gid:0 (change: yes) DEBUG: set_effective_identity uid:0 (change: no), gid:2018 (change: yes) DEBUG: sanity timeout initialized and set for 30 seconds DEBUG: acquiring exclusive lock (scope microk8s, uid 0) DEBUG: sanity timeout reset and disabled DEBUG: releasing lock 5 DEBUG: set_effective_identity uid:2018 (change: yes), gid:2018 (change: yes) DEBUG: creating user data directory: /home/ansible/snap/microk8s/1496 DEBUG: requesting changing of apparmor profile on next exec to snap.microk8s.microk8s cannot change profile for the next exec call: No such file or directory
AppArmor is enabled inside the guest operating system.
$ aa-enabled
Yes
Currently loaded AppArmor policy inside the guest operating system.
$ aa-status
apparmor module is loaded. 17 profiles are loaded. 17 profiles are in enforce mode. /snap/core/9436/usr/lib/snapd/snap-confine /snap/core/9436/usr/lib/snapd/snap-confine//mount-namespace-capture-helper snap-update-ns.core snap-update-ns.lxd snap.core.hook.configure 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 0 profiles are in complain mode. 0 processes have profiles defined. 0 processes are in enforce mode. 0 processes are in complain mode. 0 processes are unconfined but have a profile defined.
microk8s
profiles are missing as the guest operating system is using unconfined
AppArmor profile, so load these by hand to verify that this is the case here.
$ apparmor_parser --add /var/lib/snapd/apparmor/profiles/snap.microk8s.*
AppArmor policy after adding definitions.
$ aa-status
apparmor module is loaded. 53 profiles are loaded. 18 profiles are in enforce mode. /snap/core/9436/usr/lib/snapd/snap-confine /snap/core/9436/usr/lib/snapd/snap-confine//mount-namespace-capture-helper snap-update-ns.core snap-update-ns.lxd snap-update-ns.microk8s snap.core.hook.configure 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 35 profiles are in complain mode. snap.microk8s.add-node snap.microk8s.cilium snap.microk8s.config snap.microk8s.ctr snap.microk8s.daemon-apiserver snap.microk8s.daemon-apiserver-kicker snap.microk8s.daemon-cluster-agent snap.microk8s.daemon-containerd snap.microk8s.daemon-controller-manager snap.microk8s.daemon-etcd snap.microk8s.daemon-flanneld snap.microk8s.daemon-kubelet snap.microk8s.daemon-proxy snap.microk8s.daemon-scheduler snap.microk8s.disable snap.microk8s.enable snap.microk8s.helm snap.microk8s.helm3 snap.microk8s.hook.configure snap.microk8s.hook.install snap.microk8s.hook.remove snap.microk8s.inspect snap.microk8s.istioctl snap.microk8s.join snap.microk8s.juju snap.microk8s.kubectl snap.microk8s.leave snap.microk8s.linkerd snap.microk8s.microk8s snap.microk8s.refresh-certs snap.microk8s.remove-node snap.microk8s.reset snap.microk8s.start snap.microk8s.status snap.microk8s.stop 0 processes have profiles defined. 0 processes are in enforce mode. 0 processes are in complain mode. 0 processes are unconfined but have a profile defined.
microk8s
is now usable.
$ microk8s
Available subcommands are: add-node cilium config ctr disable enable helm helm3 istioctl join juju kubectl leave linkerd refresh-certs remove-node reset start status stop inspect
It works, so make this change permanent.
First solution
This is the easiest possible solution. Copy microk8s
AppArmor profiles to the host operating system.
Create a temporary directory inside the guest operating system.
$ sudo lxc exec kube-worker-3 -- mkdir /tmp/microk8s
Copy microk8s
AppArmor profiles.
$ sudo lxc exec kube-worker-3 -- find /var/lib/snapd/apparmor/profiles/ -name "snap.microk8s.*" -exec cp {} /tmp/microk8s/ \;
List files inside the guest operating system.
$ sudo lxc exec kube-worker-3 -- ls /tmp/microk8s/
snap.microk8s.add-node snap.microk8s.daemon-apiserver snap.microk8s.daemon-controller-manager snap.microk8s.daemon-proxy snap.microk8s.helm snap.microk8s.hook.remove snap.microk8s.juju snap.microk8s.microk8s snap.microk8s.start snap.microk8s.cilium snap.microk8s.daemon-apiserver-kicker snap.microk8s.daemon-etcd snap.microk8s.daemon-scheduler snap.microk8s.helm3 snap.microk8s.inspect snap.microk8s.kubectl snap.microk8s.refresh-certs snap.microk8s.status snap.microk8s.config snap.microk8s.daemon-cluster-agent snap.microk8s.daemon-flanneld snap.microk8s.disable snap.microk8s.hook.configure snap.microk8s.istioctl snap.microk8s.leave snap.microk8s.remove-node snap.microk8s.stop snap.microk8s.ctr snap.microk8s.daemon-containerd snap.microk8s.daemon-kubelet snap.microk8s.enable snap.microk8s.hook.install snap.microk8s.join snap.microk8s.linkerd snap.microk8s.reset
Copy these files to the host operating system.
$ sudo lxc file pull -r kube-worker-3/tmp/microk8s /etc/apparmor.d/
List files copied to the host operating system.
$ ls /etc/apparmor.d/microk8s/
snap.microk8s.add-node snap.microk8s.daemon-apiserver snap.microk8s.daemon-controller-manager snap.microk8s.daemon-proxy snap.microk8s.helm snap.microk8s.hook.remove snap.microk8s.juju snap.microk8s.microk8s snap.microk8s.start snap.microk8s.cilium snap.microk8s.daemon-apiserver-kicker snap.microk8s.daemon-etcd snap.microk8s.daemon-scheduler snap.microk8s.helm3 snap.microk8s.inspect snap.microk8s.kubectl snap.microk8s.refresh-certs snap.microk8s.status snap.microk8s.config snap.microk8s.daemon-cluster-agent snap.microk8s.daemon-flanneld snap.microk8s.disable snap.microk8s.hook.configure snap.microk8s.istioctl snap.microk8s.leave snap.microk8s.remove-node snap.microk8s.stop snap.microk8s.ctr snap.microk8s.daemon-containerd snap.microk8s.daemon-kubelet snap.microk8s.enable snap.microk8s.hook.install snap.microk8s.join snap.microk8s.linkerd snap.microk8s.reset
Read microk8s
AppArmor profiles inside the host operating system.
$ sudo apparmor_parser --replace /etc/apparmor.d/microk8s/
These profiles will be read automatically after the host operating system reboots.
Second solution
Load microk8s
AppArmor profiles inside the guest operating system using rc.local
mechanism.
Create /etc/rc.local
shell script inside the guest operating system.
$ echo -e '#!/bin/bash\n\napparmor_parser --replace /var/lib/snapd/apparmor/profiles/snap.microk8s.*\nexit 0\n' | sudo tee /etc/rc.local
#!/bin/bash apparmor_parser --replace /var/lib/snapd/apparmor/profiles/snap.microk8s.* exit 0
I am using replace
action instead of add
to avoid unnecessary errors as these profiles will be available system-wide. Yes, it is enough to add additional profiles once.
Ensure that the executable bit is set.
$ sudo chmod +x /etc/rc.local
Execute systemd-rc-local-generator
to create a systemd service.
$ /usr/lib/systemd/system-generators/systemd-rc-local-generator
Reboot the guest operating system and inspect rc-local
service.
$ systemctl status rc-local
● rc-local.service - /etc/rc.local Compatibility Loaded: loaded (/lib/systemd/system/rc-local.service; enabled-runtime; vendor preset: enabled) Drop-In: /usr/lib/systemd/system/rc-local.service.d └─debian.conf Active: active (exited) since Sat 2020-07-04 22:43:40 UTC; 44s ago Docs: man:systemd-rc-local-generator(8) Process: 201 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS) Jul 04 22:43:39 kube-master systemd[1]: Starting /etc/rc.local Compatibility... Jul 04 22:43:40 kube-master systemd[1]: Started /etc/rc.local Compatibility.
microk8s
works as expected.
$ microk8s
Available subcommands are: add-node cilium config [...]