Private Mounts in systemd and netns
My previous post originally used bind mounts (mount --bind /proc/self/ns/net /var/run/netns/vpn
) instead of
symbolic links (ln -sf /proc/self/ns/net /var/run/netns/vpn
) to name the private namespace. The mount
method created
two complications:
- The
/var/run/netns/vpn
had to exist (the old unit ranip netns add vpn
to create yet another namespace). - Mounts are problematic with
PrivateMounts=yes
.
The latter only became a problem with systemd’s v254 release:
* PrivateNetwork=yes and NetworkNamespacePath= now imply PrivateMounts=yes unless PrivateMounts=no is explicitly specified.
Since I run Arch Linux, I get the latest versions of systemd, and one day this setup just started failing, because:
File system namespaces are set up individually for each process forked off by the service manager. Mounts established in the namespace of the process created by
ExecStartPre=
will hence be cleaned up automatically as soon as that process exits and will not be available to subsequent processes forked off forExecStart=
(and similar applies to the various other commands configured for units).—
man 5 systemd.exec
,PrivateMounts=
This meant that while I had a vpn
netns created, the commands that I was running assuming that it was in the vpn
netns were actually being run in the unit’s private namespace. So, when the VPN interface started up, there was nothing
useful in the netns. No veth
devices, no route to the internet.
I solved this problem by:
- Using symbolic links to name the namespace, and
- Using
JoinsNamespaceOf
instead of using name for the namespace in the other units.
This actually improved and simplified the setup, in addition to not having to create the vpn
netns unnecessarily:
JoinsNamespaceOf
directly expresses the relationship between the VPN service and the netns service.- Any other services that should be in the namespace of the VPN service can now use
JoinsNamespaceOf=openvpn-client@<whatever>
, instead of having to join some arbitrary namespace.
This creates a nice tree of namespace relationships. The vpn
name for the netns now exists only for convenience, for
when (if) I need to run ip netns exec
to do something in it.
(Note that each unit that uses JoinsNamespaceOf
must also have PrivateNetwork
enabled.)