leamas / ddupdate

Update DNS Data for Dynamic IP Addresses
MIT License
40 stars 28 forks source link

Add an example how to run ddupdate under an unprivileged systemd user #65

Open ppetr opened 2 years ago

ppetr commented 2 years ago

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).

leamas commented 2 years ago

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/, /var/lib/, /var/cache/, /var/log/, /var/tmp, /tmp, /dev/shm are generally incompatible with this scheme. This rules out daemons that upgrade the system as one example, as that involves writing to /usr

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

ppetr commented 2 years ago

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.

leamas commented 2 years ago

But perhaps my use-case is an uncommon one.

Probably, yes.

EDIT: But interesting!

ppetr commented 2 years ago

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
leamas commented 2 years ago

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...

ppetr commented 2 years ago

Gladly 😊

leamas commented 2 years ago

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.

ppetr commented 2 years ago

Now I'm trying out a slightly different approach:

I'll let you know how it works.