eric chiang
eric chiang

Reputation: 2755

Script with cap_net_bind_service can't listen on port 80

I'm attempting to give a script the cap_net_bind_service Linux capability. However using setcap doesn't seem to be working.

$ cat listen.sh 
#!/bin/bash

python -m SimpleHTTPServer 80
$ getcap listen.sh 
listen.sh =
$ sudo setcap cap_net_bind_service=+eip ./listen.sh 
$ getcap listen.sh 
listen.sh = cap_net_bind_service+eip
$ ls -al listen.sh 
-rwxrwxr-x. 1 eric eric 43 Jul 11 23:01 listen.sh
$ ./listen.sh 
Traceback (most recent call last):
  File "/usr/lib64/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  ...
  File "/usr/lib64/python2.7/SocketServer.py", line 434, in server_bind
    self.socket.bind(self.server_address)
  File "/usr/lib64/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 13] Permission denied

Using sudo still works fine.

$ sudo ./listen.sh 
Serving HTTP on 0.0.0.0 port 80 ...

This is on Fedora 23 workstation.

$ cat /proc/version 
Linux version 4.4.9-300.fc23.x86_64 ([email protected]) (gcc version 5.3.1 20160406 (Red Hat 5.3.1-6) (GCC) ) #1 SMP Wed May 4 23:56:27 UTC 2016

I'm a little lost at this point, have tried turning off firewalld to no effect, and can't figure out how to debug this.

Upvotes: 3

Views: 7488

Answers (2)

Cameron Kerr
Cameron Kerr

Reputation: 1875

setcap(8) only sets capabilities on files. When it comes to interpreters, I think you're in for a rough ride. capabilities(7) -- I'm reading from RHEL 7.4 -- lists 'Thread' capability sets as well as 'File' capabilities. In 'Thread' capability sets, there is a notion of 'Ambient' sets, as well as 'Inheritable'. The important distinction is that 'inheritable capabilities are not generally preserved across execve(2) when running as a non-root user', you should set the Ambient capability set.

Note: RHEL 7 (7.4) has this backported. 'Ambient' capabilities apparently came out in Linux 4.3, and RHEL 7 is nominally a 3.10 series kernel.

I struck a similar issue as yourself, except I was trying to use a Python server under Systemd. I found that I needed to set the 'Ambient' capability set. I imagine this is what gets inherited. Here's an example Systemd unit file:

[Unit]
Description=Docker Ident Service
After=network.target
Wants=docker.service

[Service]
Type=simple
ExecStart=/usr/local/sbin/docker-identd
Restart=on-failure
RestartSec=43s

User=docker-identd
Group=docker

CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

Nice=12

StandardOutput=syslog
StandardError=syslog
SyslogFacility=daemon
SyslogIdentifier=docker-identd
SyslogLevel=info

[Install]
WantedBy=multi-user.target

Thanks for the question. It gave me an opportunity to learn a bit more.

For some further reading as to how Ambiant Capabililities works in practice, have a look at Kubernetes should configure the ambient capability set #56374

Cheers, Cameron

Upvotes: 5

aef
aef

Reputation: 4708

File capabilities applied to scripts (executables with shebang headers) won't have any effect. Instead you need to apply the capabilities to the binary interpreter used to execute the script (usually the command mentioned in the shebang header).

In your case just apply the capabilities to the python executable.

Upvotes: 1

Related Questions