Univers Libre

Containerize ZNC with systemd-nspawn

Written on 20 April 2019, 04:03 CEST
Tags: sysadmin, tech.

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: