Skip to content

Instantly share code, notes, and snippets.

@dcode
Last active May 5, 2023 21:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcode/baef6565490de0e2689d741d357f025d to your computer and use it in GitHub Desktop.
Save dcode/baef6565490de0e2689d741d357f025d to your computer and use it in GitHub Desktop.
A walkthrough of installing `multicast-relay` using systemd-nspawn on your UnifiOS 3.x device so that your Sonos works across VLANs again.

Create the filesystem overlay

If you're going to create several nspawn containers, you can save some disk space by using overlayfs to use a common base. Follow the instructions in the unifi-utilities documentation for UnifiOS 3.0+ containers. I called my container debian-base.

Next, we need an upperdir where the file changes are saved and a workdir where temporary file changes are saved. I'm going to create a container for multicast-relay, replacing the podman container that I used prior to 3.x. An important note is that upperdir and workdir must not be on the overlayfs that UnifiOS uses. I opted to store this data on the hard drive that I use for video storage at /volume1.

So, we have the following directories, which you need to create:

  • /var/lib/machines/multicast-relay: This will be the root directory of our new container.
  • /volume1/machines/multicast-relay/upper: This stores the differences from the base container.
  • /volume1/machines/multicast-relay/work: This is a temporary directory for changes before they're saved to upper. This could possibly be on a tmpfs mount, but I didn't want to mess with available memory.
  • /data/custom/machines/debian-base: This is the base container directory created by following the README linked above.

Do it:

mkdir -p /var/lib/machines/multicast-relay /volume1/machines/multicast-relay/{upper,work}

Now add the matching entry to your fstab. I'm not 100% sure how permanent this is, but I'll find out when I either reboot or do a firmware upgrade.

Append this to /etc/fstab

overlay /var/lib/machines/multicast-relay overlay noauto,x-systemd.automount,lowerdir=/data/custom/machines/debian-base,upperdir=/volume1/machines/multicast-relay/upper,workdir=/volume1/machines/multicast-relay/work 0 0

If everything is good, you can mount /var/lib/machines/multicast-relay. If there's any error from that, you have some homework to do. This should automount on reboot, but I haven't tested by rebooting my UDM Pro yet. We'll see 🤞.

Configure the container

With your overlay in place, you can start the container and install your stuff.

systemd-nspawn -M multicast-relay -D /var/lib/machines/multicast-relay

You should now be at a root shell inside the container. I configured this all manually, but you can use the script of my trial and error to save some time. Minimal Debian doesn't have curl or wget, so you can install one of those and download the script, or copy-paste into vi or something. This is left as an exercise to the reader.

To escape this container, you'll have to press the cheat code given at the start of your session. (HINT: Press Ctrl + ] 3 times.)

NOTE: I didn't set the root password, which you will see in many tutorials. It isn't needed. The host can bypass any login prompt using machinectl shell <container>. You might want to set a root password and/or other user if you are using the container as a lightweight VM.

Run the container

If this is your first systemd-nspawn service, you'll need to create the config directory.

mkdir -p /etc/systemd/nspawn

Now create the container launch configuration in the file indicated. multicast-relay requires the ability to open raw sockets, so we grant that capability and strip the rest. Be sure you specify the interfaces you wish to use in the Environment= line. Optionally, you can specify additional multicast-relay options in OPTS

# /etc/systemd/nspawn/multicast-relay.nspawn
[Exec]
Boot=on
Capability=CAP_NET_RAW
Environment=INTERFACES="br0 br50"
Environment=OPTS=

[Network]
Private=off
VirtualEthernet=off
ResolvConf=off

Once that file is in place, you're ready to enable the container on boot and run it.

machinectl enable multicast-relay
machinectl start multicast-relay

Troubleshooting

You can view the stdout of the container in the host journal.

journalctl -xe

Output from multicast-relay itself won't be found here though. You should be able to view a containers journal from the host, but I got an error when I tried suggesting the Debian container has a newer journal format than the UnifiOS host.

No matter, you can just enter the container and get a root shell (no login required).

machinectl shell multicast-relay

Now, we're setting the interface and options via environment variables set on the host. If you want to double check that's working correctly, check the environment of systemd from within the container.

cat /proc/1/environ | tr '\0' '\n'

output:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
container=systemd-nspawn
HOME=/root
USER=root
LOGNAME=root
container_uuid=c05b41ce-0995-412d-bd2d-338759b24e54
NOTIFY_SOCKET=/run/host/notify
container_host_version_id=11
container_host_id=debian
INTERFACES=br0 br50

You could use the same command to view the environment of our service. In this case, we can use the PID of dash, the shell script interpreter from start.sh.

root@DreamMachinePro:~# cat /proc/$(pidof dash)/environ | tr '\0' '\n'
LANG=C.UTF-8
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
INVOCATION_ID=d1cfaf43a00b4e79bcddca6a1595837c
JOURNAL_STREAM=8:51317778
SYSTEMD_EXEC_PID=47
INTERFACES=br0 br50
#!/bin/bash
set -eux -o pipefail
# Install dependencies
apt install -y --no-install-recommends python3 python3-netifaces git ca-certificates
# Checkout latest commit of default branch of multicast-relay
cd /opt
git clone --depth=1 https://github.com/alsmith/multicast-relay.git
# Drop a startup script
cat <<'EOF' | tee /opt/multicast-relay/start.sh
#!/bin/dash
# This script adapted from scyto
# https://github.com/scyto/multicast-relay/blob/master/start.sh
SCRIPT_DIR=$(dirname $(readlink -f $0))
echo "starting multicast-relay"
echo "Using Interfaces: ${INTERFACES}"
echo "Using Options --foreground " $OPTS
python3 "${SCRIPT_DIR}/multicast-relay.py" --interfaces ${INTERFACES} --foreground $OPTS
EOF
chmod +x /opt/multicast-relay/start.sh
# Install and enable a systemd service
cat <<'EOF' | tee /etc/systemd/system/multicast-relay.service
[Unit]
Description=multicast relay service
[Service]
PassEnvironment=INTERFACES OPTS
ExecStart=/opt/multicast-relay/start.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable multicast-relay.service
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment