#938987 Overly restrictive CapabilityBoundingSet

Package:
nsd
Source:
nsd
Description:
authoritative domain name server
Submitter:
Joel Johnson
Date:
2023-01-29 12:09:03 UTC
Severity:
minor
#938987#5
Date:
2019-08-30 19:51:51 UTC
From:
To:
--- i/debian/nsd.service
+++ w/debian/nsd.service
@@ -8,7 +8,7 @@ Type=notify
  Restart=always
  ExecStart=/usr/sbin/nsd -d
  ExecReload=+/bin/kill -HUP $MAINPID
-CapabilityBoundingSet=CAP_CHOWN CAP_IPC_LOCK CAP_NET_BIND_SERVICE 
CAP_SETGID CAP_SETUID CAP_SYS_CHROOT
+CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_IPC_LOCK
CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT
  MemoryDenyWriteExecute=true
  NoNewPrivileges=true
  PrivateDevices=true

In a recent upgrade to buster, the nsd service failed to start after
upgrade. After spending more time than expected digging through the
details, it turns out that the CAP_DAC_OVERRIDE capability should be
included in the systemd service unit file since the
CapabilityBoundingSet configuration was added in the latest version [1].

The affected host in particular has been through several release
upgrades (I also ran into #932010). I use "include:" lines in my
configuration file set in order to ease management and version control
of my zone files. As a result, the included configuration files were not
readable. They have permission bits 0440 and are owned by the nsd user.

I haven't done a deep investigation, but it appears that with systemd
starting the process as root, without the CAP_DAC_OVERRIDE capability
available it's unable to read files owned by the 'nsd' user.

Relatedly, have you considered directly running the daemon as the nsd
user using AmbientCapabilities=CAP_NET_BIND_SERVICE? That removes the
need for CAP_{CHOWN,SETGID,SETUID}. In my local testing I haven't found
a need for CAP_IPC_LOCK, and don't use chroot configuration so haven't
needed CAP_SYS_CHROOT.

It sort of looks like the Debian service unit file was derived
incrementally from the upstream one. It'd be useful for external diff
review to rebase the content and ordering based on the upstream file
provided in contrib/nsd.service. In fact, you may consider a Debian
patch making targeted adjustments (ReadWritePaths) and shipping the
upstream config directly. Notably, the upstream unit file doesn't list
CAP_CHOWN in the bounding set, is there a Debian specific rationale for
needing to include it?

Thanks,
Joel

[1] https://salsa.debian.org/dns-team/nsd/commit/8a6039c

#938987#10
Date:
2019-09-03 18:39:54 UTC
From:
To:
Hi Joel,

thanks for the report.

The systemd service file has been in part of the package for 5 years,
with the default ordering of sections (unit, service, install).
The upstream service while was more less recently added (~1 year ago).

Since systemd hardening has been available and recommended, the
corresponding directives where added from upstream.
Admittedly this still requires some fine tuning such as:
https://salsa.debian.org/dns-team/nsd/merge_requests/1

As such, I am a bit reluctant to ship, use or patch around the upstream
service file.

However the DAC_OVERRIDE capability is quite excessive as is bypasses
all permission checks. Giving the process this capability would be the
quite contrary to the intent of settting CapabilityBoundingSet.

Best regards,
Markus

#938987#15
Date:
2019-09-04 14:59:33 UTC
From:
To:
To be clean, I'm all for the systemd hardening. I'm just reporting a
case where the current configuration appears to have a gap. I'm in
agreement that the DAC_OVERRIDE may be too big of a hammer, but I wasn't
able to quickly identify another alternative without switching to the
AmbientCapabilities path.

The nsd daemon provides config file inclusion support, however a mixed
file ownership as I had becomes nonfunctional. It was non-intuitive to
me that the nsd daemon, running as the nsd user, didn't have permissions
to read files owned by the nsd user. During investigation I figured out
that the systemd initially launches as root and then setuid to nsd. The
workaround was ensuring that the base config and all included data files
are owned by root, but perhaps there's a cleaner way to support this.

Joel

#938987#20
Date:
2019-11-27 10:50:30 UTC
From:
To:
Thank you very much! Adding CAP_DAC_OVERRIDE solved it for me as well. Not sure how many hours it would have taken for me to figure it out.

Does systemd or the linux kernel log capability violations somewhere? (is it even possible)

#938987#25
Date:
2020-01-23 19:09:36 UTC
From:
To:
Confirmed, on every system upgraded to buster, nsd fails to start (even
with a blank configuration file i.e. all settings at defaults):

systemd[1]: Starting Name Server Daemon...
nsd[10191]: error: could not open zone list /var/lib/nsd/zone.list:
Permission denied
nsd[10191]: error: could not read zonelist file /var/lib/nsd/zone.list
systemd[1]: nsd.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: nsd.service: Failed with result 'exit-code'.
systemd[1]: Failed to start Name Server Daemon.

Since the default for the config parameter "zonelistfile"
is "/var/lib/nsd/zone.list", the process needs access to this file
(seemingly even if you do not use dynamic zones).

I don't pretend to understand all this .service file gubbins, I note that
it already has ReadWritePaths=/var/lib/nsd so I don't know what's wrong.
Since I didn't feel it wise to give the process full root access to the
filesystem, I simply commented out the CapabilityBoundingSet line

Please can you fix this regression.

Thanks
David

#938987#30
Date:
2020-05-04 01:50:15 UTC
From:
To:
I ran into this issue too. I think that, in principle, daemons should
not be able to write to their own configuration files, so making the
files owned by root is a good thing anyway. The only real trouble is
that things break on upgrade due to the earlier default ownership.


One other related issue is that the current CapabilityBoundingSet
appears to break the ip-transparent nsd option.

May 03 17:59:40 juniper nsd[20346]: setsockopt(...,IP_TRANSPARENT, ...) failed for udp: Operation not permitted
May 03 17:59:40 juniper nsd[20346]: setsockopt(...,IP_TRANSPARENT, ...) failed for udp: Operation not permitted
May 03 17:59:40 juniper nsd[20346]: setsockopt(...,IP_TRANSPARENT, ...) failed for tcp: Operation not permitted
May 03 17:59:40 juniper nsd[20346]: setsockopt(...,IP_TRANSPARENT, ...) failed for tcp: Operation not permitted

I had this in use as a workaround for:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=765831

...and apparently I don't need the workaround anymore, but other use
cases for that option will presumably be broken still.

Thanks,
Corey

#938987#37
Date:
2022-03-13 20:51:40 UTC
From:
To:
Hey folks,

I hit this on sid. nsd is failing to start with a sensible config,
done within the bounds of allowed NSD configuration, and fails to
start. This has taken me a huge amount of time to track down.

What is the NSD maintainer's opinion of the correct way to get nsd to
behave with a sensible config, here? I'm reluctant to trim caps, since
tightening them seems good, but they're too tight, it appears.

  paultag

#938987#42
Date:
2022-07-29 02:52:41 UTC
From:
To:
Hello,

I got bitten by a problem where NSD would not start and looks very similar to what it’s reported here. Since I found a way to make it work without fiddling with systemd I felt like reporting back.

In my case, using Debian 11, the service starts correctly when freshly installed. The problem presents itself when using dynamic zones, specifically when the file /var/lib/nsd/zone.list comes into existence. Usually created automatically when using something like this `nsd-control addzone example.com example`. After this the service won’t start with a 'permission denied’ to read the zone.list file. This file gets created with owner nsd and group nsd. What needs to be done is change the ownership of this file to root:root and everything works. Even adding new zones work and the ownership of the file remains root:root.

The cause for this could apparently be that the service initially starts as root and then drops to nsd. Not too sure about the cause though. This was discussed here[1].

A definitive solution could be that the package creates that file with the correct ownership “root:root” and no content.

Also, please let me know if anyone see any possible problems with this fix.

Thanks.

[1]: https://www.mail-archive.com/nsd-users@nlnetlabs.nl/msg00078.html

#938987#47
Date:
2023-01-29 11:58:16 UTC
From:
To:
By using "nsd-control addzone", I also got NSD into the situation that
it would not start anymore on Debian 11.

The current systemd service settings also prevent "nsd-control write"
from working (when trying to pull zones from old nameserver via axfr to
nsd on this machine):