tl;dr: if you don't care about the example and just want to know how
the heck to interpret this tool, read the first couple paragraphs, and
then skip to the last paragraph or two.
I think people have a deep misunderstanding of what `systemd-analyze
security` does, and in particular are expecting it to be much more
sophisticated than it is. Per systemd-analyze(1):
> Note that this only analyzes the per-service security features
> systemd itself implements. This means that any additional security
> mechanisms applied by the service code itself are not accounted
> for. The exposure level determined this way should not be
> misunderstood: a high exposure level neither means that there is no
> effective sandboxing applied by the service code itself, nor that
> the service is actually vulnerable to remote or local attacks.
The key takeaway from this is that the tool only considers
security-related systemd service file directives (see
systemd.exec(5)). It does NOT consider *anything* else like:
* AppArmor or SELinux confinement
* Service architecture (for example, whether it is composed of
several mutually-distrusting daemons)
* Service complexity
* Whether the service does anything to confine itself, like dropping
privileges after starting up as root
* Service vulnerability mitigations (for example, being written in a
memory-safe language, or being compiled with
-fstack-protector-strong or something)
and etc.
I case you're not familiar with the directives `systemd-analyze
security` looks at, basically what you need to know is that they can
be turned on in a service's definition and systemd will automatically
restrict the ability of the service's processes to do various things,
like reading other services' temporary files, or mucking with kernel
tunables. (These restrictions are imposed *on top of* any existing
restrictions. For example if the service is not run as root it already
can't muck with kernel tunables, so that systemd restriction does
nothing unless the service either runs as root or can escalate
privileges to root).
As an example of how these work, take the default systemd service file
for Apache httpd from my production Debian 10 machine:
```
% systemctl cat apache2.service
# /lib/systemd/system/apache2.service
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
Documentation=
https://httpd.apache.org/docs/2.4/
[Service]
Type=forking
Environment=APACHE_STARTED_BY_SYSTEMD=true
ExecStart=/usr/sbin/apachectl start
ExecStop=/usr/sbin/apachectl stop
ExecReload=/usr/sbin/apachectl graceful
PrivateTmp=true
Restart=on-abort
[Install]
WantedBy=multi-user.target
```
If I run `systemd-analyze security apache2.service`, I get a long
detailed listing of things I could add to this unit file to improve
the sandboxing that systemd applies to the service (I'm not including
an example here because it's so long, but you can run this on any
service to get something similar). The score given is 9.2 UNSAFE
Now, Apache has absolutely no business writing to users' home
directories, so let's say I want systemd to enforce that. (Normally it
wouldn't have any business reading from home directories either except
that on my system I have it set up to serve `/home/$USER/public_html`
directories.) Apache also has no business writing to /usr, /boot,
/etc, /sys, /proc. Moreover it does not need most of the devices in
/dev. It would be nice, therefore, if it was not allowed to do these
things if it ever gets compromised.
So, what I can do is tell systemd to restrict these things. I create a
systemd drop-in file (maybe with `systemctl edit apache2.service`)
with the following content:
```
[Service]
ProtectSystem=full
ProtectHome=read-only
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
```
Now if I run `systemd-analyze security apache2.service` again, the
reported score is 7.9 EXPOSED. So it went down 1.3 points because I
added those sandboxing options, which as systemd-analyze(8) mentions,
are the *only* things that systemd considers.
Note in particular that systemd-analyze does *not* know or care that
Apache drops privileges when it starts up and thus can't write to /usr
et. al. anyway. So these options are useful for defense in depth
hardening, but the only time they'll matter is if Apache is
compromised, and *then* the attacker finds a way to escalate to root
privileges. At that point the attacker will not be able to write to
these directories despite having root.
It's probably a good idea to turn these options on for lots and lots
of services, because they're *so* easy to just put in the service file
and they provide defense in depth. But `systemd-analyze security`'s
warnings are much too scary. Instead of UNSAFE a better label honestly
would be NEEDS A LOT OF WORK or something like that. Think of the tool
as treating the service's code like a black box: it doesn't know
anything about what goes on inside the service (in fact in the general
case it is mathematically *impossible* for it to do so; see Rice's
theorem[1]) so it works with what it knows, which is declarative
systemd configuration. Another way to think about the scores are as an
upper bound on danger, enforced by systemd/the Linux kernel.
Hopefully this gives a sense of how better to interpret these
warnings.
-AJ
[1]:
https://en.wikipedia.org/wiki/Rice%27s_theorem