Package: unscd
Version: 0.54-1
Severity: wishlist
I wrote a hardening dropin (attached) for unscd.service.
$ systemd-analyze security
UNIT EXPOSURE PREDICATE HAPPY
unscd.service 9.6 UNSAFE 😨 # before
unscd.service 1.1 OK 🙂 # after
Please consider adding some/all of it to debian/unscd.service.
You may need to "dial back" the hardening a little, e.g.
PADL libnss-ldap (dead since 2016, but still in Debian 11) probably needs
AF_INET AF_INET6.
Two further improvements require source code changes:
* Removing NSCD_SOCKET_OLD from nscd.c.
I *think* glibc hasn't used this path for over a decade now!
Removing it will allow systemd to block write access to /run.
* Make unscd only drop privileges if it starts as root (or so).
This will allow systemd to drop privileges before unscd starts, and
block CAP_SET[UG]ID and sete[ug]id(2).
I am using unscd with its default nscd.conf.
It provides a short-term cache for nss-pam-ldapd,
reducing the load on the LDAP server (slapd or samba-ad) by 90% to 99%.
My test case was to do this (testing passwd negative-ttl only) :
# for i in {1..9999}; do touch "$i"; chown -h 1234 "$i"; done
# time find -nouser -fprintf /dev/null .
real 0m1.176s # unscd is stopped
real 0m0.446s # unscd is running
With my dropin, this test still passes, so I think unscd is both running and
working.
-- System Information:
Debian Release: 11.0
APT prefers stable-updates
APT policy: (990, 'stable-updates'), (990, 'stable-security'), (990,
'stable'), (500, 'proposed-updates'), (500, 'unstable'), (500, 'testing'), (1,
'experimental')
Architecture: amd64 (x86_64)
Kernel: Linux 5.10.0-8-amd64 (SMP w/8 CPU threads)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE,
TAINT_UNSIGNED_MODULE
Locale: LANG=en_AU.UTF-8, LC_CTYPE=en_AU.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
# This file goes in /etc/systemd/system/unscd.service.d/hardening.conf
[Service]
PrivateNetwork=yes
## We can't drop root privs before starting, because
## it wants to bind BOTH of these:
##
## define NSCD_SOCKET "/var/run/nscd/socket"
## define NSCD_SOCKET_OLD "/var/run/.nscd_socket"
##
## Probably the latter should just be removed from unscd.c entirely, since
## the implementations of glibc that use it are probably looooong gone.
# User=unscd
# DynamicUser=yes
RuntimeDirectory=unscd
WorkingDirectory=/run/nscd
CapabilityBoundingSet=
# FIXME: once we Users=unscd, tighten this up.
CapabilityBoundingSet=CAP_SETUID CAP_SETGID
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
DevicePolicy=closed
IPAddressDeny=any
NoNewPrivileges=yes
PrivateDevices=yes
PrivateMounts=yes
PrivateTmp=yes
# FIXME: once we Users=unscd, tighten this up.
# UPDATE: er, we probably need PrivateUsers=no anyway, because
# nscd's job is to see & cache users. :-)
#PrivateUsers=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
# FIXME: once NSCD_SOCKET_OLD is gone, remove this.
ReadWritePaths=/run
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
# FIXME: once we Users=unscd, tighten this up.
#SystemCallFilter=~@privileged
SystemCallFilter=~@resources
RestrictRealtime=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
UMask=0077
ProtectHostname=yes
ProcSubset=pid