Securing BIND 9 with AppArmor/Firejail/Systemd Workshop

1 BIND 9 Security workshop

The virtual machines have a domain name in the form apparmorNNN.dnslab.org.

Please login to the machines with a modern web browser under the URL https://apparmorNNN.dnslab.org:9090 with the username user and the password DNSandBIND. You can also login with SSH and the same username and password.

The virtual machines run the cockpit tool (https://cockpit-project.org) to provide a terminal in the web browser.

Then select the terminal (last menue option on the left) and start the tutorial.

1.1 Virtual machines

Every participant has a VM number. Please replace the NNN in the instructions with your participant number from the table below:

VM Number Name

1.2 Basic Installation

  • For the sessions you need to become the user root with the sudo command, for example with sudo -s
  • A command line promt starting with % indicates a root shell

2 Session 1 - Fixing an AppArmor permission issue

  • Login to the Lab VM machine and switch to the root user with sudo -i
  • Check that AppArmor is installed and enabled
% aa-status
apparmor module is loaded.
8 profiles are loaded.
8 profiles are in enforce mode.
   /usr/bin/man
   /usr/sbin/chronyd
   lsb_release
   man_filter
   man_groff
   nvidia_modprobe
   nvidia_modprobe//kmod
   tcpdump
0 profiles are in complain mode.
2 processes have profiles defined.
2 processes are in enforce mode.
   /usr/sbin/chronyd (737)
   /usr/sbin/chronyd (739)
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
  • Install the BIND 9 DNS Server
    % apt install bind9
    
  • Check that the BIND 9 process is running under AppArmor control
    % ps auxZ | grep named
    named (enforce)                 bind       11913  0.8  2.5 221376 25636 ?        Ssl  09:20   0:00 /usr/sbin/named -f -u bind
    unconfined                      root       12068  0.0  0.0   5204   732 pts/1    S+   09:20   0:00 grep named
    
  • Create a non-standard directory for storing secondary zone files (we propose there are technical or organisational reasons that demand this change)
    % mkdir -p /srv/bind/zonefiles/secondary
    % chown bind: /srv/bind/zonefiles/secondary
    
  • Remove the default BIND 9 configuration /etc/bind/named.conf and replace it with the content below
    options {
       recursion no;
       directory "/var/cache/bind";
    };
    
    zone "dnssec.works" {
      type secondary;
      file "/srv/bind/zonefiles/secondary/dnssec.works";
      primaries { 5.45.109.212; 2a03:4000:6:2115::2; };
    };
    
  • Save the file and check the configuration for errors
    % named-checkconf
    
  • Restart the BIND 9 service and check it's status
    % systemctl restart bind9
    
  • There should be a problem reported: AppArmor does not allow BIND 9 to write to the secondary zone file
    % systemctl status bind9
    ● named.service - BIND Domain Name Server
         Loaded: loaded (/lib/systemd/system/named.service; enabled; vendor preset: enabled)
         Active: active (running) since Wed 2021-10-20 09:30:22 UTC; 2s ago
           Docs: man:named(8)
       Main PID: 12365 (named)
          Tasks: 5 (limit: 1132)
         Memory: 17.0M
            CPU: 66ms
         CGroup: /system.slice/named.service
                 └─12365 /usr/sbin/named -f -u bind
    
    Oct 20 09:30:22 apparmor001 named[12365]: running
    Oct 20 09:30:22 apparmor001 named[12365]: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
    Oct 20 09:30:22 apparmor001 named[12365]: zone dnssec.works/IN: Transfer started.
    Oct 20 09:30:22 apparmor001 named[12365]: resolver priming query complete
    Oct 20 09:30:22 apparmor001 named[12365]: transfer of 'dnssec.works/IN' from 5.45.109.212#53: connected using 161.35.104.3#60547
    Oct 20 09:30:23 apparmor001 named[12365]: zone dnssec.works/IN: transferred serial 3151
    Oct 20 09:30:23 apparmor001 named[12365]: transfer of 'dnssec.works/IN' from 5.45.109.212#53: Transfer status: success
    Oct 20 09:30:23 apparmor001 named[12365]: transfer of 'dnssec.works/IN' from 5.45.109.212#53: Transfer completed: 2 messages, 259 records, 32087 bytes, 0.272 secs (117966 bytes/sec)>
    Oct 20 09:30:23 apparmor001 named[12365]: zone dnssec.works/IN: sending notifies (serial 3151)
    Oct 20 09:30:23 apparmor001 named[12365]: dumping master file: /srv/bind/zonefiles/secondary/tmp-pV7V2CdOuA: open: permission denied
    
  • See the AppArmor "deny" message in the Linux audit logfile
    % grep denied /var/log/audit/audit.log
    type=AVC msg=audit(1634722223.143:20): apparmor="DENIED" operation="mknod" profile="named" name="/srv/bind/zonefiles/secondary/tmp-pV7V2CdOuA" pid=12365 comm="isc-worker0000" requested_mask="c" denied_mask="c" fsuid=112 ouid=112FSUID="bind" OUID="bind"
    
  • Try to change the AppArmor profile for BIND 9 in /etc/apparmor.d/usr.sbin.named to fix the permission issue
  • The Solution will be published after 15 minutes of the lab start

2.1 Solution

  • Add the following line to the (local) AppArmor profile
    /srv/bind/zonefiles/secondary/** lrw,
    
  • Restart AppArmor and BIND 9
    % systemctl restart apparmor
    % systemctl restart bind9
    

3 Session 2 - Secure BIND 9 with Firejail

  • Login to the lab VM and switch to the root user with sudo -i
  • Disable AppArmor (until next reboot)
    % systemctl stop apparmor
    % aa-teardown
    
  • Install the BIND 9 DNS Server
    % apt install bind9
    
  • Create a non-standard directory for storing secondary zone files (we propose there are technical or organisational reasons that demand this change)
    % mkdir -p /srv/bind/zonefiles/secondary
    % chown bind: /srv/bind/zonefiles/secondary
    
  • If you have worked on session 1 above, remove the secondary zone file
    % rm /srv/bind/zonefiles/secondary/dnssec.works
    
  • Remove the default BIND 9 configuration /etc/bind/named.conf and replace it with the content below
    options {
       recursion no;
       directory "/var/cache/bind";
    };
    
    zone "dnssec.works" {
      type secondary;
      file "/srv/bind/zonefiles/secondary/dnssec.works";
      primaries { 5.45.109.212; 2a03:4000:6:2115::2; };
    };
    
  • Save the file and check the configuration for errors
    % named-checkconf
    
  • Install Firejail (The Debian 11 package for Firejail pulls in a large number of dependencies. This is an artifact of the Debian 11 packaging system and not required by Firejail)
    % apt install firejail firejail-profiles
    
  • Install the Firejail profile for BIND 9 in /etc/firejail/named.profile
       # Firejail profile for BIND 9 (named)
    # Description: Authoritative DNS Server and DNS resolver
    name named
    
    # Persistent local customizations
    include named.local
    
    # Persistent global definitions
    include globals.local
    
    noblacklist /sbin
    noblacklist /usr/sbin
    
    blacklist /tmp/.X11-unix
    blacklist ${RUNUSER}/wayland-*
    
    include disable-common.inc
    include disable-devel.inc
    include disable-exec.inc
    include disable-interpreters.inc
    include disable-passwdmgr.inc
    include disable-programs.inc
    include disable-xdg.inc
    
    include whitelist-usr-share-common.inc
    
    whitelist /etc/bind
    whitelist /etc/passwd
    whitelist /var/cache/bind
    whitelist /var/run
    
    caps.keep net_admin,net_bind_service,setgid,setuid,sys_chroot,sys_resource
    ipc-namespace
    machine-id
    netfilter
    no3d
    nodvd
    nonewprivs
    nosound
    notv
    nou2f
    novideo
    protocol inet,inet6,unix,netlink
    seccomp.drop _sysctl,acct,add_key,adjtimex,clock_adjtime,delete_module,fanotify_init,finit_module,get_mempolicy,init_module,io_cancel,io_destroy,io_getevents,io_setup,io_submit,ioperm,iopl,kcmp,kexec_file_load,kexec_load,keyctl,lookup_dcookie,mbind,migrate_pages,modify_ldt,mount,move_pages,open_by_handle_at,perf_event_open,perf_event_open,pivot_root,process_vm_readv,process_vm_writev,ptrace,remap_file_pages,request_key,set_mempolicy,swapoff,swapon,sysfs,syslog,umount2,uselib,vmsplice
    
    disable-mnt
    private
    private-dev
    private-tmp
    writable-var
    
    dbus-user none
    dbus-system none
    
    # mdwe can break modules/plugins
    memory-deny-write-execute
    
  • Install a new systemd-unit for the Firejail protected BIND 9 service /etc/systemd/system/bind9-hardened.service
    [Unit]
    Description=BIND Domain Name Server
    Documentation=man:named(8)
    After=network.target
    Wants=nss-lookup.target
    Before=nss-lookup.target
    
    [Service]
    EnvironmentFile=-/etc/default/named
    ExecStart=/usr/bin/firejail /usr/sbin/named -f $OPTIONS
    ExecReload=/usr/sbin/rndc reload
    ExecStop=/usr/sbin/rndc stop
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    Alias=bind9.service
    
  • Load the new service unit into Systemd, stop the non-hardened BIND 9 service and start the new hardened BIND 9
    % systemctl daemon-reload
    % systemctl stop bind9
    % systemctl disable bind9
    % systemctl enable --now bind9-hardened
    % systemctl disable bind9
    Removed /etc/systemd/system/bind9.service.
    Removed /etc/systemd/system/multi-user.target.wants/named.service.
    root@apparmor001:/home/user# systemctl enable --now bind9-hardened
    Created symlink /etc/systemd/system/bind9.service → /etc/systemd/system/bind9-hardened.service.
    Created symlink /etc/systemd/system/multi-user.target.wants/bind9-hardened.service → /etc/systemd/system/bind9-hardened.service.
    root@apparmor001:/home/user# systemctl status bind9-hardened
    ● bind9-hardened.service - BIND Domain Name Server
         Loaded: loaded (/etc/systemd/system/bind9-hardened.service; enabled; vendor preset: enabled)
         Active: active (running) since Wed 2021-10-20 09:52:31 UTC; 7s ago
           Docs: man:named(8)
       Main PID: 18946 (firejail)
          Tasks: 7 (limit: 1132)
         Memory: 19.2M
            CPU: 158ms
         CGroup: /system.slice/bind9-hardened.service
                 ├─18946 /usr/bin/firejail /usr/sbin/named -f -u bind
                 ├─18947 /usr/bin/firejail /usr/sbin/named -f -u bind
                 └─18953 /usr/sbin/named -f -u bind
    
    Oct 20 09:52:32 apparmor001 named[18953]: running
    Oct 20 09:52:32 apparmor001 named[18953]: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
    Oct 20 09:52:32 apparmor001 named[18953]: resolver priming query complete
    
  • Check that BIND 9 named is running under Firejail control
    % firejail --top
    

4 Session 3 - Harden the BIND 9 Systemd Unit

  • Login to the lab VM and switch to the root user with sudo -i
  • Install the BIND 9 DNS Server
    % apt install bind9
    
  • If you have worked on Session 2 (Firejail), disabled the hardened BIND 9 service unit, enable the default BIND 9 service unit and re-enable AppArmor
    % systemctl stop bind9-hardened
    % systemctl disable bind9-hardened
    % systemctl enable --now named
    % systemctl start apparmor
    
  • Analyze the security of the default BIND 9 Systemd unit and note down the exposure level for named.service
    % systemd-analyze security bind9
    
  • Create a new Systemd service unit in /etc/systemd/system/named-secure.service
    [Unit]
    Description=BIND Domain Name Server
    Documentation=man:named(8)
    After=network.target
    Wants=nss-lookup.target
    Before=nss-lookup.target
    
    [Service]
    EnvironmentFile=-/etc/default/named
    ExecStart=/usr/sbin/named -f $OPTIONS
    ExecReload=/usr/sbin/rndc reload
    ExecStop=/usr/sbin/rndc stop
    Restart=on-failure
    
    AppArmorProfile=named
    Personality=x86-64
    LockPersonality=yes
    UMask=077
    MemoryMax=2G
    TasksMax=20
    
    RestrictNamespaces=Yes
    RestrictRealtime=True
    MemoryDenyWriteExecute=True
    
    NoNewPrivileges=Yes
    PrivateDevices=Yes
    DevicePolicy=closed
    
    PrivateTmp=Yes
    PrivateMounts=Yes
    ProtectClock=Yes
    ProtectControlGroups=Yes
    ProtectHome=Yes
    ProtectKernelLogs=Yes
    ProtectKernelModules=Yes
    ProtectKernelTunables=Yes
    ProtectProc=invisible
    ProcSubset=pid
    ProtectSystem=Yes
    ProtectHostname=Yes
    
    RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
    
    SystemCallFilter=~@clock @debug @module @mount @raw-io @reboot @swap @cpu-emulation @obsolete
    
    CapabilityBoundingSet=~CAP_SETPCAP CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_(DAC_OVERWRITE CAP_FOWNER CAP_IPC_OWNER
    CapabilityBoundingSet=~CAP_SYS_MODULE CAP_SYS_RAWIO CAP_SYS_TIME CAP_AUDIT_CONTROL CAP_KILL CAP_MKNOD CAP_NET_BROADCAST CAP_NET_RAW
    CapabilityBoundingSet=~CAP_SYS_NICE CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_BPF CAP_SYS_BOOT CAP_LINUX_IMMUTABLE CAP_IPC_LOCK
    CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_LEASE CAP_SYS_PACCT CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
    
    [Install]
    WantedBy=multi-user.target
    Alias=bind9-secure.service
    
  • Load the new Systemd unit, stop the old BIND 9 service and start BIND 9 with the new secure unit definition
    % systemctl daemon-reload
    % systemctl stop named
    % systemctl disable named
    % systemctl enable --now named-secure
    
  • Check the status of the BIND 9 service (the Tasks and Memory limits indicate that the new service unit is active)
    % systemctl status named-secure
    ● named-secure.service - BIND Domain Name Server
         Loaded: loaded (/etc/systemd/system/named-secure.service; enabled; vendor preset: enabled)
         Active: active (running) since Wed 2021-10-20 10:13:32 UTC; 2s ago
           Docs: man:named(8)
       Main PID: 19439 (named)
          Tasks: 5 (limit: 20)
         Memory: 17.3M (max: 2.0G)
            CPU: 109ms
         CGroup: /system.slice/named-secure.service
                 └─19439 /usr/sbin/named -f -u bind
    
    Oct 20 10:13:33 apparmor001 named[19439]: managed-keys-zone: loaded serial 16
    Oct 20 10:13:33 apparmor001 named[19439]: zone dnssec.works/IN: loaded serial 3151 (DNSSEC signed)
    Oct 20 10:13:33 apparmor001 named[19439]: all zones loaded
    Oct 20 10:13:33 apparmor001 named[19439]: running
    Oct 20 10:13:33 apparmor001 named[19439]: zone dnssec.works/IN: sending notifies (serial 3151)
    Oct 20 10:13:33 apparmor001 named[19439]: missing expected cookie from 2001:500:1::53#53
    Oct 20 10:13:33 apparmor001 named[19439]: missing expected cookie from 2001:500:1::53#53
    Oct 20 10:13:33 apparmor001 named[19439]: missing expected cookie from 2001:500:1::53#53
    Oct 20 10:13:33 apparmor001 named[19439]: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
    Oct 20 10:13:33 apparmor001 named[19439]: resolver priming query complete
    
  • Check the security score of the new Systemd unit and compare with the original result
    % systemd-analyze security named-secure