View previous topic :: View next topic |
Author |
Message |
serafean n00b
Joined: 11 Apr 2016 Posts: 21
|
Posted: Mon Apr 11, 2016 9:40 am Post subject: Tip : systemd, capabilities and rootless nginx, radvd, tor |
|
|
Hi all,
I wanted to try out how powerful systemd unit files are, so came up with this challenge : start nginx rootless, with as less access as possible, listening on ports 80 and 443.
Software versions : systemd-229, linux-4.5, nginx 1.8.1
The unit file:
Code: | [Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
User=nginx
Group=nginx
Type=forking
PIDFile=/var/run/nginx/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -QUIT $MAINPID
#Security
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
ReadOnlyDirectories=/etc/ssl/nginx
ReadWriteDirectories=/var/log/nginx /var/www/
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target |
First off was giving nginx the capability to bind ports 443 and 80, this was done with AmbientCapabilities and CapabilityBoundingSet.
Then locking it out of the rest of the system.
- PrivateDevices - Hides all device nodes (except for random, null, and some others)
- PrivateTmp - mounts a separate tmp that is used only by nginx (namespaces are used AFAIK)
- ProtectHome - Disables access to /home and /root
- ProtectSystem - enables only read access to most of the FS
Finally, poking holes in the lock :
- ReadOnlyDirectories - disable writing to dir containing SSL certificates. Since running as user nginx requires all these to have nginx of their owner, nginx could theoretically overwrite its certificates. This prevents that.
- ReadWriteDirectories - Allow rw access to log directory, and to data directory.
It works, I haven't found anything broken (yet).
This concept is reusable : I've adapted it to tvheadend, dnsmasq and radvd. I'm hoping to convert more of my unit files so as to have less services dependend on them dropping their privileges.
The greatest pain are PID files : /run isn't writeable by non-root processes, so for each such a service, there has to exist an entry in /etc/tmpfiles.d creating /run/${SERVICE_NAME}/ with appropriate access rights, so the PID file can be written somewhere.
Here's to hoping someone finds this useful
Comments very welcome.
Serafean.
Last edited by serafean on Wed Nov 30, 2016 2:16 pm; edited 1 time in total |
|
Back to top |
|
|
serafean n00b
Joined: 11 Apr 2016 Posts: 21
|
Posted: Sun Apr 17, 2016 11:40 am Post subject: |
|
|
After a week running in this mode, I deem it usable
Joining dnsmasq.service.
Code: | [Unit]
Description=A lightweight DHCP and caching DNS server
After=network.target
[Service]
User=dnsmasq
Group=dnsmasq
Type=simple
PIDFile=/var/run/dnsmasq/dnsmasq.pid
ExecStartPre=/usr/sbin/dnsmasq --test
ExecStart=/usr/sbin/dnsmasq -k -x /var/run/dnsmasq/dnsmasq.pid
ExecReload=/bin/kill -HUP $MAINPID
#Security
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target | And radvd.service
Code: | [Unit]
Description=Router advertisement daemon for IPv6
Documentation=man:radvd(8)
After=network.target
[Service]
User=radvd
Group=radvd
Type=forking
ExecStartPre=/usr/sbin/radvd --configtest
ExecStart=/usr/sbin/radvd --logmethod stderr --debug 0
ExecReload=/usr/sbin/radvd --configtest ; \
/bin/kill -HUP $MAINPID
PIDFile=/run/radvd/radvd.pid
# Performance
CPUSchedulingPolicy=idle
#Hardening
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target |
|
|
Back to top |
|
|
candrews Developer
Joined: 10 Aug 2005 Posts: 162
|
|
Back to top |
|
|
Ant P. Watchman
Joined: 18 Apr 2009 Posts: 6920
|
Posted: Fri Jul 01, 2016 11:38 am Post subject: |
|
|
Huh, makes me wonder why something as simple as radvd doesn't have a USE=caps for that, it really shouldn't run as root. Quagga does that already... |
|
Back to top |
|
|
serafean n00b
Joined: 11 Apr 2016 Posts: 21
|
Posted: Sat Oct 01, 2016 3:09 pm Post subject: |
|
|
candrews wrote: |
In the future, you may want to do the same - having these changes made in Gentoo means you don't have to maintain them and everyone gets the benefits. | Oh wow! I wasn't thinking that far out I was just playing around xD
Thanks for taking the time to report those enhancments.
I read throught the bug reports, and if someone could enlighten me as to why starting a process as root and letting it drop its capabilities is better than this, I'd be very grateful. |
|
Back to top |
|
|
serafean n00b
Joined: 11 Apr 2016 Posts: 21
|
Posted: Wed Nov 30, 2016 1:43 pm Post subject: |
|
|
And another one for tor : Code: | [Unit]
Description=The Onion Router
After=network-online.target
[Service]
User=tor
Group=tor
ExecStartPre=/usr/bin/tor --verify-config -f /etc/tor/torrc
ExecStart=/usr/bin/tor --RunAsDaemon 0 -f /etc/tor/torrc
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
TimeoutStopSec=32
LimitNOFILE=30000
# Hardening options:
#CapabilityBoundingSet = CAP_NET_BIND_SERVICE
# Capabilities aren't enough to have ports < 1024
RuntimeDirectory=tor
RuntimeDirectoryMode=0700
ReadWriteDirectories=/var/lib/tor/
PrivateTmp = yes
PrivateDevices = yes
ProtectHome = yes
ProtectSystem = strict
NoNewPrivileges = yes
[Install]
WantedBy=multi-user.target |
will be submitting it to bugzilla[/code] |
|
Back to top |
|
|
ssokolow n00b
Joined: 20 Apr 2005 Posts: 22
|
Posted: Wed Apr 19, 2017 4:59 pm Post subject: |
|
|
I decided to develop something for my Debian VPS using your nginx.service as a base and I managed to lock it down even further (and make the service file easier to use) by making the following tweaks:
- Instead of replicating everything, use .include /lib/systemd/system/nginx.service at the top of the file as described here.
- Instead of using tmpfiles.d, use RuntimeDirectory=nginx inside the service file and set PIDFile and the pid line in nginx.conf to /run/nginx/nginx.pid
Another tweak I'm planning to do is to set nginx to log via syslog(), configure my syslogd to split the entries out into nginx/access.log and nginx/error.log and then remove the ReadWriteDirectories line. (That way, if someone compromises nginx, they won't have the ability to modify its logs to cover their tracks. The manual way to do this is to use chattr +a to set the logs append-only and then modify your logrotate config to temporarily unset that attr.)
Using syslog also has the added benefit that it's easy to forward logs to a central location if you have more than one server. (I use it to have my FreeBSD router log to my desktop Linux PC's syslog infrastructure so it's easy to keep long-term audit logs.)
It may also make it feasible to ask systemd to run nginx under a random/temporary UID and GID. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|