Containerize ZNC with systemd-nspawn

Published on

On my previous server, I had an IRC client (weechat) running in a screen session. I recently migrated this setup to a ZNC bouncer running on my server + weechat running on my laptop, mainly to avoid network lags through SSH and to have a better desktop integration (notifications, etc.).

My server hosts my e-mails, important and confidential documents (among other things) and I always thought that running an always connected IRC client on the same machine was a bad idea.

So in the same time I got rid of my weechat+screen to migrate to ZNC, I had a look at systemd-nspawn, also known as systemd containers. It’s actually pretty much like LXC containers, but managed with the systemd logic in mind.

Here is a quick tutorial of how I containerized ZNC into a systemd container on a Debian stretch system:

  1. First, install systemd-container and debootstrap packages:

    # apt install systemd-container debootstrap
  2. Create directories used by systemd-container (which aren’t create on install):

    # mkdir /var/lib/machines/ /etc/systemd/nspawn/

    /var/lib/machines/ will host the containers’ hierarchy and containers unit files will be stored into /etc/systemd/nspawn/.

  3. Install a Debian system + znc package using debootstrap into /var/lib/machines/znc/:

    # cd /var/lib/machines/
    # debootstrap --include znc stretch znc
  4. To make the container start on boot, you have to enable the corresponding systemd-nspawn instance and the machines target (which will start all enabled instances of systemd-nspawn unit:

    # systemctl enable systemd-nspawn@znc machines.target
  5. At this stage, your container is ready. You can start your container (run its init process) with:

    # systemd-nspawn -D /var/lib/machines/znc/

    Or execute a command in the container:

    # systemd-nspawn -D /var/lib/machines/znc/ hostname

    systemd-nspawn comes with the machinectl command which allows you to easily manage your containers:

    # machinectl [list|list-images|start|stop|status|…]

    See its man page for all supported sub-commands and options.

  6. Now, let’s configure some few extra stuff to run ZNC. Create a dedicated user in the container:

    # systemd-nspawn -D /var/lib/machines/znc/ useradd -u 1002 -g 1002 -m znc
  7. And customize the container’s options by putting a config file into /etc/systemd/nspawn/:

    [Exec]
    # Don't start the init process inside the container, instead execute
    # `znc`
    Boot=off
    Parameters=/usr/bin/znc --foreground
    
    # Drop all default capabilities: the container will run with no
    # capabilities, ZNC doesn't need any
    DropCapability=CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID CAP_IPC_OWNER CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETGID CAP_SETFCAP CAP_SETPCAP CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_PTRACE CAP_SYS_TTY_CONFIG CAP_SYS_RESOURCE CAP_SYS_BOOT CAP_AUDIT_WRITE CAP_AUDIT_CONTROL
    
    # Start `znc` as znc
    User=znc
    
    # Those 2 options aren't supported by the version of systemd shiped
    with Debian stretch. Ephemeral=on makes the container discard on
    shutdown any modification made during its runtime (Docker style) and
    NoNewPrivileges ensures that the code executed inside the container
    won't be able to gain greater privileges (with setuid bit for instance)
    #Ephemeral=on
    #NoNewPrivileges=on
    
    # Use private user namespace (equivalent to LXC's unprivilegied mode)
    PrivateUsers=on
        
    [Files] 
    # Mount my .znc configuration directory onto the container (with write
    access) and the znc.pem containing the private key, certificates chain
    and DH param (read-only)
    Bind=/home/romain/.znc/:/home/znc/.znc/
    BindReadOnly=/var/lib/acme/live/irc.univers-libre.net/combined:/home/znc/.znc/znc.pem
        
    [Network]
    # Use the same network stack as the host
    VirtualEthernet=no

    This file will automatically be read by systemd-nspawn before starting the container. All these options can be specified as commandline parameters to systemd-nspawn as well.

Resources: