about unbound and systemd units

Hi,

I'm doing some package maintenance for Unbound in SLES. We are
migrating from cron to systemd-timers and I've come up, based on what
I've seen being done in other distros, with the following solution
regarding unbound-anchor and unbound relation and I'd like some
feedback on this, specially if you'd done it in a different way. I was
getting suggestions to have unbound-anchor.timer enabled by default
(even if unbound.service is not) but I'd say this way is better because
it only runs unbound-anchor.servce if unbound.servce is running, but I
might be completely wrong:

unbound-anchor.service

Hi Rubén,

I was getting suggestions to have unbound-anchor.timer enabled by
default (even if unbound.service is not) but I'd say this way is
better because it only runs unbound-anchor.servce if unbound.servce
is running, but I might be completely wrong:

I think there is value in maintaining the root.key file even if unbound
isn't running. The rational is that other things (like unbound-host or
packages using libunbound2) might want a current one.

Not maintaining the root.key lead to at least one bug report in Ubuntu
[1] and for that reason, I believe that Ubuntu/Debian [2] should also
adopt a similar approach.

unbound-anchor.service
----------------------
[Unit]
Description=update of the root trust anchor for DNSSEC validation in
unbound
Documentation=man:unbound-anchor(8)

[Service]
Type=oneshot
User=unbound
ExecStart=/usr/sbin/unbound-anchor -a /var/lib/unbound/root.key -c
/etc/unbound/icannbundle.pem
SuccessExitStatus=1

unbound-anchor.timer
--------------------
[Unit]
Description=daily update of the root trust anchor for DNSSEC
Documentation=man:unbound-anchor(8)
BindsTo=unbound.service

[Timer]
# Current DNSKEY TTL in root zone is 172800 seconds, i.e.
172800/60/60/24 = 2 days.
# It means that unboud-anchor should be run at least once a day.
OnCalendar=daily
Persistent=true
AccuracySec=24h

[Install]
WantedBy=unbound.service

unbound.service
---------------
[Unit]
Description=Unbound recursive Domain Name Server
After=syslog.target network.target
After=unbound-keygen.service
Wants=unbound-keygen.service
After=unbound-anchor.timer
Wants=unbound-anchor.timer
Before=nss-lookup.target
Wants=nss-lookup.target

[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/unbound
#ExecStartPre=/sbin/runuser --shell /bin/sh -c "/usr/sbin/unbound-
anchor -a /var/lib/unbound/root.key -c /etc/unbound/icannbundle.pem"
unbound
ExecStartPre=/usr/bin/sudo -u unbound /usr/sbin/unbound-anchor -a
/var/lib/unbound/root.key -c /etc/unbound/icannbundle.pem

This ^ ExecStartPre is probably a leftover :wink:

ExecStartPre=/usr/sbin/unbound-checkconf
ExecStart=/usr/sbin/unbound -d $UNBOUND_OPTIONS

[Install]
WantedBy=multi-user.target

Aside from the minor caveat mentioned above, it looks good to me.

Regards,
Simon

1: https://bugs.launchpad.net/bugs/1771545
2: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900241

first post. please be gentle

In OpenWrt, we run scripts around the root.key to prevent flash wear. Unbound works and chroots in /var/lib/unbound, So after checking root.key for cleanliness, it is copied back to /etc/unbound. Because unbound-anchor is one shot anyway, you could make unbound-anchor.service actually call a script instead. If Unbound is running and therefore doing its RFC5011 work, then don't run unbound-anchor. This script would check root.key for finger prints of success and no damage. If you modularize it, then it can also be used by the Unbound.service start and stop scripts.
Just some thoughts
Eric

These seem like good words.

The one possible wrinkle is that it's not enough for inbound to run to
do 5011; it needs to run over period that exceeds the hold-down timer
(section 2.2). So knowing that inbound is doing its RFC 5011 work is
more complicated than knowing that it is running.

The whole business of trust anchor bootstrap is long overdue for
rethinking. The current mechanisms meet particular use-cases but, I
think it's fair to say, are widely considered to be less than
adequate. This is work that I hope to pick up again in the dnsop wg,
following the less than universally-loved
draft-jabley-dnsop-validator-bootstrap.

Joe

Hi & thanks for all the replies,

I see now that it's also relevant to have unbound-anchor timer running
even if unbound service is not running.

About the ExecStartPre, it wasn't really a leftover: I was thinking
(and this is also relevant even if the timer is enabled by default)
that unbound-anchor.timer doesn't give any guaranties that unbound-
anchor.service will have been run at least once before unbound.service
starts. But it does feel kinda hacky to do it this way...

Regards,

Rubén

I see now that it's also relevant to have unbound-anchor timer running
even if unbound service is not running.

Note that this means that your unbound-libs / libunbound package needs
to have the unbound-anchor binary tool. We had to move it from our
unbound package to our unbound-libs package when we did this.

About the ExecStartPre, it wasn't really a leftover: I was thinking
(and this is also relevant even if the timer is enabled by default)
that unbound-anchor.timer doesn't give any guaranties that unbound-
anchor.service will have been run at least once before unbound.service
starts. But it does feel kinda hacky to do it this way...

If you make it Type=oneshot then it should only get run after the first
ever start of the unbound service. Although that does cover the scenario
where someone manually deletes the file generated by unbound-anchor, so
on fedora we run it in the unbound daemon service file as ExecStartPre=
as well.

So we have:

/usr/lib/systemd/system/unbound-anchor.service
[Unit]
Description=update of the root trust anchor for DNSSEC validation in
unbound
Documentation=man:unbound-anchor(8)

[Service]
Type=oneshot
User=unbound
ExecStart=/usr/sbin/unbound-anchor -a /var/lib/unbound/root.key -c
/etc/unbound/icannbundle.pem -f /etc/resolv.conf -R
SuccessExitStatus=1

/usr/lib/systemd/system/unbound-anchor.timer
[Unit]
Description=daily update of the root trust anchor for DNSSEC
Documentation=man:unbound-anchor(8)

[Timer]
# Current DNSKEY TTL in root zone is 172800 seconds, i.e.
# 172800/60/60/24 = 2 days.
# It means that unboud-anchor should be run at least once a day.
OnCalendar=daily
Persistent=true
AccuracySec=24h

[Install]

/usr/lib/systemd/system/unbound.service
[Unit]
Description=daily update of the root trust anchor for DNSSEC
Documentation=man:unbound-anchor(8)

[Timer]
# Current DNSKEY TTL in root zone is 172800 seconds, i.e.
# 172800/60/60/24 = 2 days.
# It means that unboud-anchor should be run at least once a day.
OnCalendar=daily
Persistent=true
AccuracySec=24h

[Install]
WantedBy=timers.target

[paul@thinkpad tmp]$ cat /usr/lib/systemd/system/unbound.service [Unit]
Description=Unbound recursive Domain Name Server
After=network.target
After=unbound-keygen.service
Wants=unbound-keygen.service
Wants=unbound-anchor.timer
Before=nss-lookup.target
Wants=nss-lookup.target

[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/unbound
ExecStartPre=/usr/sbin/unbound-checkconf
ExecStartPre=-/usr/sbin/unbound-anchor -a /var/lib/unbound/root.key -c
/etc/unbound/icannbundle.pem -f /etc/resolv.conf -R
ExecStart=/usr/sbin/unbound -d $UNBOUND_OPTIONS
ExecReload=/usr/sbin/unbound-control reload

[Install]
WantedBy=multi-user.target

(and we still have /usr/lib/systemd/system/unbound-keygen.service but
would like to move that per default to unix domain sockets so it is
no longer needed)

Paul

Hi Paul,

Nope. I guess unbound-anchor drops privs or keeps the existing
owner/group intact.

paul@bofh7:~$ ls -l /var/lib/unbound/
total 8
-rw-r--r--. 1 unbound unbound 1251 Nov 21 00:00 root.key
-rw-r--r--. 1 unbound unbound 1251 Oct 2 2017 root.key.rpmsave

Paul

Interesting, here with version 1.7.3, it doesn't drop privs and always
create a new temp file (owned by root) that is renamed to root.key:

# cd /tmp
# > root.key
# chown unbound: root.key
# ll root.key
-rw-r--r-- 1 unbound unbound 0 Nov 21 08:46 root.key
# unbound-anchor -a /tmp/root.key
# ll root.key
-rw-r--r-- 1 root root 1252 Nov 21 08:46 root.key

strace confirmed the renaming:
openat(AT_FDCWD, "/tmp/root.key", O_RDONLY) = 3
openat(AT_FDCWD, "/tmp/root.key", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
openat(AT_FDCWD, "/tmp/root.key", O_RDONLY) = 7
openat(AT_FDCWD, "/tmp/root.key.14619-0", O_WRONLY|O_CREAT|O_TRUNC,
0666) = 10
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_CLOEXEC) = 12
rename("/tmp/root.key.14619-0", "/tmp/root.key") = 0

I don't know why it's different on Fedora but thanks for sharing!

Regards,
Simon