Ejecutar servicios con usuarios sin privilegios
Ejecutar un servicio como root convierte un error en acceso total al servidor. Use una cuenta aislada por servicio o systemd con DynamicUser para limitar daños.
Por qué no debe ejecutar todo como root
root puede hacer cualquier cosa en el equipo: leer todos los archivos, cambiar cualquier configuración y eliminar todo el sistema. Cuando ejecuta un servicio como root, concede todo ese poder al servicio. Si el servicio tiene un error que un atacante puede explotar, no obtiene sólo acceso al servicio: obtiene root, y root controla todo el servidor. Ejecutar el servicio con un usuario sin privilegios limita los daños. Un error en un servicio que se ejecuta con una cuenta limitada sólo permite al atacante acceder a lo que esa cuenta puede modificar, que debería ser casi nada.
Este es el principio de mínimo privilegio: conceder a cada parte del sistema exactamente los permisos que necesita para hacer su trabajo, y ninguno más. Es la medida más eficaz para limitar el alcance de una intrusión y, en un servidor moderno, aplicarla tiene un coste mínimo.
Una cuenta dedicada por servicio
El enfoque clásico consiste en crear un usuario de sistema independiente para cada servicio. Ese usuario es propietario únicamente de los archivos del servicio y no puede iniciar sesión. Una cuenta de sistema para una aplicación web podría ser la siguiente:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvcCada opción es importante. --system la convierte en una cuenta de servicio, no en una cuenta de usuario humano. --no-create-home omite la creación de un directorio personal que no necesita. --shell /usr/sbin/nologin impide que, aunque un atacante obtenga de algún modo la cuenta, pueda abrir una shell con ella. La cuenta existe únicamente para ser propietaria de un proceso y de sus archivos.
A continuación, conceda a ese usuario sólo los archivos que necesita:
sudo chown -R appsvc:appsvc /opt/myappAhora el servicio lee y escribe en su propio directorio y no tiene acceso al resto del disco. Si alguna vez es explotado, los archivos que el atacante puede modificar se limitan a /opt/myapp. La cuenta aún puede leer todo lo que sea legible para cualquier usuario, pero no puede modificar el resto del sistema.
Haz que systemd lo ejecute con esa cuenta
Cuando la cuenta exista, indique a systemd que ejecute el servicio con ella. En el archivo de unidad, una línea es suficiente:
[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvcUser=appsvc indica que el proceso se inicia con los privilegios limitados de esa cuenta en lugar de los de root. Esta es la forma habitual y recomendada de ejecutar una aplicación con systemd, y conviene aplicarla a todos los servicios para los que cree una unidad. Reducir privilegios no sirve si systemd supervisa el proceso equivocado. Si la unidad sigue informando de que está activa después de que el daemon se haya cerrado sin mostrar mensajes, compruebe que haya seleccionado el Type= adecuado para la forma en que se inicia el proceso.
O bien omita la cuenta por completo con DynamicUser
systemd puede ir un paso más allá y crear un usuario temporal por usted. Este usuario existe sólo mientras se ejecuta el servicio. Establezca DynamicUser=yes y no tendrá que administrar ninguna cuenta:
[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myappAl iniciarse, systemd asigna un ID de usuario que no está en uso. Al detenerse, lo libera. El servicio también obtiene un /tmp privado, una vista de sólo lectura de la mayor parte del sistema de archivos y un directorio de estado escribible en /var/lib/myapp, que StateDirectory= prepara y le entrega. SysV init no podía hacer nada parecido. Allí, la reducción de privilegios dependía de lo que hiciera el script de inicio de cada servicio. Esta limitación explica en gran medida por qué las distribuciones cambiaron a systemd inicialmente. Para un servicio autocontenido que sólo necesita su propio directorio de estado, DynamicUser=yes es la forma que requiere menos esfuerzo para obtener un aislamiento sólido, porque no existe ninguna cuenta permanente que un atacante pueda atacar.
Escribir unidades manualmente resulta laborioso, y configurar correctamente las directivas de refuerzo es la parte que aporta más valor. El generador de la guía de servicios y temporizadores de systemd puede completar estas opciones por usted para que la unidad sea correcta desde el primer intento.
Cómo encaja con el resto
El principio de mínimo privilegio es una capa de seguridad. Funciona junto con las demás capas, no las sustituye. Un firewall con denegación predeterminada controla qué puede llegar al servicio. Ejecutarlo con un usuario sin privilegios controla qué puede hacer el servicio si sufre una intrusión. El SSH reforzado impide que los atacantes accedan al servidor desde el principio. Ninguna de estas medidas es suficiente por sí sola. Juntas evitan que un error en un servicio se convierta en el compromiso de todo el servidor. Alojar un servicio que protege secretos muestra dónde terminan estas capas: una cuenta restringida limita lo que un proceso comprometido puede tocar, pero un gestor de contraseñas autoalojado como Vaultwarden sigue dependiendo de cómo proteja su token de administración y su archivo de copia de seguridad. El aislamiento de usuarios no cubre ninguno de los dos.
Antes de continuar, repase una lista de comprobación de seguridad para todo el servidor y genere una copia personalizada con la que pueda trabajar:
FAQ
¿Por qué no debo ejecutar un servicio como root?
Porque root puede hacer cualquier cosa en el equipo. Si explotan un servicio que se ejecuta como root, el atacante obtiene control de todo el servidor, no sólo del servicio. Ejecutar el servicio con una cuenta limitada y sin privilegios contiene los daños a lo que esa cuenta puede acceder. Reserve root para la administración y ejecute todos los servicios de larga duración con un usuario restringido.
¿Cómo creo un usuario que no pueda iniciar sesión?
Ejecute sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME. El shell nologin impide que la cuenta abra una sesión interactiva aunque sus credenciales se vean comprometidas. --system la identifica como una cuenta de servicio y --no-create-home omite un directorio personal que no necesita. Asígnele únicamente la propiedad de sus propios archivos mediante chown.
¿Qué es systemd DynamicUser?
DynamicUser=yes indica a systemd que cree un usuario temporal para el servicio. El usuario existe sólo mientras el servicio se ejecuta, por lo que nunca tendrá que administrar una cuenta permanente. También proporciona al servicio un /tmp privado, una vista del sistema de archivos prácticamente de sólo lectura y un directorio de estado administrado. Es la forma que requiere menos esfuerzo para ejecutar un servicio autónomo con una identidad desechable y con pocos privilegios.
¿Ejecutar un servicio con un usuario que no sea root sustituye a un firewall?
No. Protegen aspectos distintos. Ejecutar un servicio con un usuario sin privilegios limita lo que puede hacer si resulta comprometido. Un firewall limita lo que puede llegar al servicio. Use ambos, junto con un SSH reforzado, para que cada capa cubra lo que las demás no pueden proteger.
¿Qué archivos debe poseer el usuario del servicio?
Sólo los archivos que el servicio necesita realmente, y nada más. Asigne a la cuenta la propiedad de su propio directorio de trabajo y de sus datos, y deje todo lo demás bajo la propiedad de root. Un patrón adecuado es sudo chown -R svc-app:svc-app /opt/svc-app para el directorio de la aplicación, mientras que la configuración de /etc permanece bajo la propiedad de root y sólo puede leerla el servicio. El objetivo es que, si el proceso resulta comprometido, los archivos que puede modificar se limiten a sus propios datos y no al resto del sistema.