Under Unices (including Linux), running a listening service on low numbered privileged TCP ports (< 1024) can usually only be done by root (superuser). This does not necessarily mean that service needs to run with effective user id of root, but at service port binding time, user running the program needs to have sufficient privileges. As lot of low numbered ports are commonly used for many standard services (HTTP is usually over port 80 and HTTPS port 443, etc), the notion of privileged ports can be considered "security through convention" approach, preventing random user with UNIX shell account popping up some apparently "official" service. On the other hand, requirement for privileged account can become a security issue, if the program that needs to be run on privileged port is in some aspects untrusted.
privbind/authbind
Debian packages include privbind utility that allows to execute application as an unprivileged user with extra privilege of binding to reserved ports. Its manual is contradictory, specifying both that:
- privbind has no SUID parts, and runs within the confines of a single process
- privbind works by starting two processes. One drops privileges and runs (exec(2)) the command, the other remains as root.
Similar package available on Debian is authbind, also using LD_PRELOAD mechanism and thus suffering from the same limitations.
setcap + chrpath
However, on Linux kernels with capabilities support (2.2+), using per-thread NET_BIND_SERVICE capability can be used to run service on privileged port without runtime superuser privileges. This capability can be enabled with setcap "eip" flags (effective, inheritable and permitted, execute as root), e.g. on Java binaries:
setcap cap_net_bind_service=+eip $JAVA_HOME/bin/java setcap cap_net_bind_service=+eip $JAVA_HOME/jre/bin/java
While necessary, setcap might not be sufficient (as unprivileged user):
java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directoryExamining the java binary with readelf shows:
xyz@sdk$ readelf -d java Dynamic section at offset 0x71c contains 28 entries: Tag Type Name/Value ... skipped ... 0x00000001 (NEEDED) Shared library: [libc.so.6] 0x0000000e (SONAME) Library soname: [lib.so] 0x0000000f (RPATH) Library rpath: [$ORIGIN/../lib/amd64/jli]According to thread in openjdk distro-pkg-dev list, glibc disallows relative paths and $ORIGIN expansion when running binary with capabilities as ordinary user (for security reasons). One solution to that is to change the RPATH encoded in ELF binary to absolute path. From Debian packages chrpath can be used for this:
chrpath -r $JAVA_HOME/lib/amd64/jli/ $JAVA_HOME/bin/java chrpath -r $JAVA_HOME/jre/lib/amd64/jli/ $JAVA_HOME/jre/bin/java
setcap + patchelf
Changing RPATH can also be done with patchelf, not currently included in Debian packages. Sylvain Duloutre's blog post describes granting identical port binding capabilities to JVM binaries with setcap and patchelf, with RPATH change performed like this:
patchelf --set-rpath $JAVA_HOME/lib/amd64/jli $JAVA_HOME/bin/java patchelf --set-rpath $JAVA_HOME/jre/lib/amd64/jli $JAVA_HOME/jre/bin/java
NB!
Granting capabilities to (JVM) binaries can introduce security concerns and should only be done where access to such binaries is suitably restricted.
No comments:
Post a Comment