ddupdate
ddupdate copied to clipboard
Add an example how to run ddupdate under an unprivileged systemd user
Recent versions support running services under DynamicUser
, see https://0pointer.net/blog/dynamic-users-with-systemd.html
Below is my working example of such a set-up. I'm not sure if it makes sense to include it in the project as it is, or just keep it somewhere as an example.
# Runs ddupdate under an unprivileged, dynamic user.
# Expects:
# - Configuration in /etc/ddupdate/ddupdate.conf.
# - Credentials in /etc/ddupdate/netrc (possibly readable just by root).
[Unit]
Description=Update DNS data for this host
Documentation=man:ddupdate.8 http://github.com/leamas/ddupdate
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ddupdate
Environment=PATH=/bin:/usr/bin:/sbin:/usr/sbin
# Environment=http_proxy=my.proxy.domain:8888
# Environment=https_proxy=my.proxy.domain:8888
DynamicUser=yes
CacheDirectory=ddupdate
Environment="XDG_CACHE_HOME=%C/ddupdate"
ConfigurationDirectory=ddupdate
Environment="XDG_CONFIG_HOME=%E/ddupdate"
# Since /etc/ddupdate/netrc file is owned by root and is expected to be
# unreadable by unprivileged users, copy it to a runtime directory and make it
# owned by the service's user.
# See https://github.com/systemd/systemd/issues/16060
RuntimeDirectory=ddupdate
RuntimeDirectoryMode=0700
ExecStartPre=+/bin/sh -c "/usr/bin/install --verbose --owner=$(stat --format=%%u %t/ddupdate) --mode=0600 --no-target-directory %E/ddupdate/netrc %t/ddupdate/netrc"
Environment="NETRC=%t/ddupdate/netrc"
[Install]
WantedBy=multi-user.target
Since version 250 systemd also has explicit support for passing credentials (https://systemd.io/CREDENTIALS/), but I don't use recent enough version to be able to use it (and so I suppose many other users are in a similar situation).
At a glance: is the temporary systemd user solving any problem here? I understand that this is extremely useful for typical "system" services which runs with all sort of privileges managing a central configuration typically in /etc. However, ddupdate is running as systemd user service. This means that that this ~not~ note in the link you gave me is applicable:
A service that need to write to files outside of /run/
ddupdate reads and writes files under invoking user's $HOME, so... OTOH, since we run as a regular user this solves many security problems. It also simplifies configuration, since nothing is done as root.
Unless, of course, we start using /etc/netrc. However, this is deprecated for the same reasons: it requires using root access. Also note that /etc/netrc is not part of the official setup (see the manpage).
OTOH, systemd credentials seems promising, it should be a fine replacement for current keyring storage for unattended use. Since I'm on Fedora I actually have 250 on my boxes. When there is time, I will look into this
The problem this is solving is reducing privileges ddupdate
is running with when run as a system service. I run it as such on a few machines of mine. It doesn't make sense for me to run it under a specific user account (some even don't have one). And I also automate the configuration with Ansible centrally. So instead of running it under root
, or setting up a dedicated role account I use DynamicUser
so that systemd takes care of all this for me. I just set up a few environment variables as shown in the example so that ddupdate
uses the right systemd locations.
But perhaps my use-case is an uncommon one.
But perhaps my use-case is an uncommon one.
Probably, yes.
EDIT: But interesting!
FWIW I also have a NetworkManager hook in /etc/NetworkManager/dispatcher.d/99-ddupdate
to run ddupdate
immediately after an address change:
#!/bin/sh
set -e
IFACE="$1"
ACTION="$2"
case "$ACTION" in
dhcp6-change|dhcp4-change)
systemctl --no-ask-password start ddupdate.service
;;
*) ;;
esac
hm... that's a good one! Could you perhaps make it to a script in the dispatcher.d directory and make a PR? One or two lines of comments in the beginning should be documentation enough.
Also, if you have time and motivation: could you update the ddupdate.8 manpage with a section on the new NETRC environment variable? Missed that when I merged...
Gladly 😊
If you do, you need to convert the script to use systemctl --user
and add a line defining the user (see the other script there). This is the normal usecase, and needs to be taken care of somehow.
EDIT: Or make it two scripts, one the one you have and one using --user
. Perhaps cleaner, dunno.
Now I'm trying out a slightly different approach:
- I added
RemainAfterExit=yes
toddupdate.service
so that it's recognized as active or inactive. - I set up the NetworkManager hook to react to:
-
connectivity-change
events - iffull
then the service is started, otherwise stopped. -
dhcp[46]-change
events invoketry-reload-or-restart
on the service. So that if it's active, it's restarted, otherwise no-op.
-
- I removed
ddupdate.timer
completely.
I'll let you know how it works.