4

I understand the security implications of allowing a non-root user to start a webserver on ports 80 and 443.

I would like to remove the privilege check to allow any non-root user to invoke a webserver on ports 80 and 443 on Ubuntu 16 and CentOS 7.

I have root access on the machine, but I need the webserver to be run as a different username to address NFS permission issues; this alternative username cannot have root access.

So far, I've looked at:

  • setcap cap_net_bind_service=ep /path/to/the/executable (doesn't stick because the executable is recompiled remotely and stored on NFS)
  • editing the sudoers file to enable passwordless sudo as the user from root (still has NFS access issues)

Ideally, I'd like something like setcap, but on the ports, rather than the executable, to drop the privilege check entirely.

Related: https://stackoverflow.com/questions/413807/is-there-a-way-for-non-root-processes-to-bind-to-privileged-ports-on-linux

authbind looked promising, but it doesn't seem to work with GoLang webservers

Ubuntu 16 is too old to allow changing the unprivileged port start range:

> sudo sysctl net.ipv4.ip_unprivileged_port_start=80
sysctl: cannot stat /proc/sys/net/ipv4/ip_unprivileged_port_start: No such file or directory
2
  • What webserver?
    – roaima
    Nov 19, 2020 at 19:14
  • It's a custom webserver written in GoLang, invoked with arguments in a tcsh script.
    – Elle Fie
    Nov 19, 2020 at 19:25

2 Answers 2

3

I think setcap will be your answer. I think the real question here is: How will my system recognize when the webserver on the NFS has been touched so it can run a setcap command on it?

I think you'll want to set up an inotify or systemd.path to monitor this webserver. When that binary gets replaced, you'll detect it and trigger the setcap command that works for you. This works especially well if your webserver runs via systemd already.

Here's an example with systemd.path assuming your server runs as systemd service webserver.service

# /etc/systemd/system/webcap.path
[Unit]
Description=Watching changes in the webserver binary
# Start monitoring only after the webserver is running.
After=webserver.service

[Path]
# Whenever someone writes to this path (binary is replaced), do something
PathModified=/path/to/webserver
# This is the service you launch when the above condition is met
Unit=webcap.service

[Install]
#Whenever the webserver is started, this monitor will also start
WantedBy=webserver.service
# /etc/systemd/system/webcap.service
[Unit]
Description=Update caps of webserver

[Service]
Type=oneshot
ExecStart=ssetcap cap_net_bind_service=ep /path/to/webserver
2
  • 1
    There was another answer to this question a moment ago. It was radically different and involved wraping the auth layer. I think the author deleted it. I'd be interested to see it again or to understand why it was deleted. It didn't look wrong, just different.
    – Stewart
    Nov 19, 2020 at 20:41
  • I agree, it had something to do with a setuid helper script?
    – Elle Fie
    Nov 19, 2020 at 20:43
3

While I liked Stewart's answer, it adds yet another piece to the pipeline that can break (systemd), so I ended up using capsh per this answer. I had to recompile it from source to get the ambient capabilities feature (I stored the resulting binary as /sbin/capsh2), then I was able to set this as the launch command:

/sbin/capsh2 --keep=1 --user=nonrootuser --inh=cap_net_bind_service --addamb=cap_net_bind_service -- -c /path/to/webserver

When this command is run as root, the webserver correctly starts as nonrootuser and is able to bind to ports 80 and 443 in "userspace".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.