Uso de puertos privilegiados con usuarios no administradores en Linux

por | septiembre 3, 2018

Con un usuario normal sin permisos de administración no puedes hacer uso de puertos por debajo del 1024 por motivos de seguridad. Estos puertos son utilizados por servicios conocidos como FTP, servidores web Apache o Nginx, DNS, etc… y requieren de root o usuario con permisos adecuados.

Podemos hacer una prueba sencilla con netcat (nc) intentando abrir un socket en un puerto por debajo del 1024 con un usuario no administrativo:

[jota@jota-pc ~]$ nc -v -l 90
nc: Permission denied

Ahora en un puerto no privilegiado no habrá problemas:

[jota@jota-pc ~]$ nc -v -l 9000
Listening on [0.0.0.0] (family 0, port 9000)

Tenemos distintas opciones para conseguir que un usuario normal pueda utilizar estos puertos.

Uso de setcap

Con setcap (Set Capabilities) podemos permitir que un usuario no administrativo haga uso de determinadas aplicaciones sin necesidad de privilegios especiales.

Para el caso que nos ocupa la sintaxis para permitir el uso de puertos privilegiados por un usuario normal sería:

setcap 'cap_net_bind_service=+ep' /ruta/binario

No se pueden aplicar propiedades con setcap en ficheros no regulares (por ejemplo enlaces simbólicos). Como /bin/nc es un enlace simbólico en Debian pasaría lo siguiente:

[root@jota-pc ~]# setcap cap_net_bind_service=+ep /bin/nc
Failed to set capabilities on file `/bin/nc' (Invalid argument)
The value of the capability argument is not permitted for a file. Or the file is not a regular (non-symlink) file

Resulta que en Debian el binario que tenemos de netcat corresponde a la implementación en OpenBSD:

[root@jota-pc ~]# ls -l /bin/nc
lrwxrwxrwx 1 root root 20 Aug  7  2015 /bin/nc -> /etc/alternatives/nc
[root@jota-pc ~]# ls -l /etc/alternatives/nc
lrwxrwxrwx 1 root root 15 Aug 25  2017 /etc/alternatives/nc -> /bin/nc.openbsd

De esta manera aplicamos la propiedad sobre el binario:

[root@jota-pc ~]# setcap cap_net_bind_service=+ep /bin/nc.openbsd

Comprobamos con getcap:

[root@jota-pc ~]# getcap /bin/nc.openbsd
/bin/nc.openbsd = cap_net_bind_service+ep

Ahora de nuevo con el usuario jota abro un socket en el puerto 90 sin problema:

[jota@jota-pc ~]$ nc -v -l 90
Listening on [0.0.0.0] (family 0, port 90)

Para quitar la propiedad utilizamos la opción -r

[root@jota-pc ~]# setcap -r /bin/nc.openbsd
[root@jota-pc ~]# getcap /bin/nc.openbsd

El principal problema de esta aproximación es que abrimos la puerto a que cualquier usuario pueda hacer uso de ese binario abriendo puertos privilegiados. Una solución a esto es utilizar ACL (Access Control List) quitando permisos de ejecución al fichero para «otros» y poniendo un ACL que sólo permita a mi usuario ejecutarlo (además de al usuario/grupo propietario del binario):

[jota@jota-pc ~]$ ls -l /bin/nc.openbsd 
-rwxr-xr-x 1 root root 31208 Mar  3  2017 /bin/nc.openbsd
[root@jota-pc ~]# chmod 750 /bin/nc.openbsd
[root@jota-pc ~]# ls -l /bin/nc.openbsd 
-rwxr-x--- 1 root root 31208 Mar  3  2017 /bin/nc.openbsd

Aplicamos ACL con setfactl y comprobamos con getfacl:

[root@jota-pc ~]# setfacl -m u:jota:x /bin/nc.openbsd
[root@jota-pc ~]# getfacl /bin/nc.openbsd 
getfacl: Removing leading '/' from absolute path names
# file: bin/nc.openbsd
# owner: root
# group: root
user::rwx
user:jota:--x
group::r-x
mask::r-x
other::---

Para quitar el ACL aplicado:

setfacl -x u:jota /bin/nc.openbsd

Regla prerouting con iptables

Otra solución consiste en utilizar la tabla nat de iptables para redirigir las peticiones de puertos inferiores a puertos superiores. Imaginemos que tenemos un servidor web Apache que escucha en el puerto 80 y necesitamos levantar este servicio con un usuario no administrativo, nos encontraríamos con la misma casuística. Con iptables podríamos hacer:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000

Nosotros podríamos levantar el servidor web Apache cambiando el puerto de escucha a 8000, ej.:

Listen 192.168.1.101:8000

Las peticiones de los usuarios, aplicaciones o servicios llegarían al puerto 80 e iptables se encargaría internamente y de forma transparente de redirigir estas peticiones al puerto 8000 que es donde hemos levantado nuestro servicio.

Un poco de sudo para el usuario

Podemos dar permisos de sudo a un usuario en concreto para ejecutar un binario con permisos de administrador. Para ello editamos el fichero /etc/sudoers como root con el comando visudo. Para el ejemplo inicial con nc introduciríamos:

jota    ALL= /bin/nc

Ahora con el usuario jota hacemos sudo al comando:

[jota@jota-pc ~]$ sudo nc -v -l 90
[sudo] password for jota: 
Listening on [0.0.0.0] (family 0, port 90)

Cada vez que lo ejecutemos tendremos que introducir nuestra contraseña. Si esto se nos hace demasiado pesado podemos configurar /etc/sudoers para que no pida contraseña:

jota    ALL=(ALL) NOPASSWD:/bin/nc

Ahora ejecutamos nc:

[jota@jota-pc ~]$ sudo nc -v -l 90
Listening on [0.0.0.0] (family 0, port 90)

En cuanto a la primera opción expuesta en el artículo, es interesante echarle un vistazo a la página del manual correspondiente con man capabilities para ver todas las posibilidades que ofrece.