Cómo aislar procesos con Systemd en Ubuntu 20.04

El autor seleccionó el Fondo de Código Libre y Abierto para recibir una donación como parte del programa Write for DOnations.
Introducción
El sandboxing es una técnica de seguridad informática que se centra en aislar un programa o proceso de las partes de un sistema con las que no necesita interactuar durante su funcionamiento normal. Cuando se inicia un programa nuevo, este tiene todas las capacidades del usuario con el que se ejecuta. Estas capacidades suelen ser mucho mayores que las que el programa necesita para realizar su función. Esto puede generar problemas de seguridad cuando un actor malintencionado manipula el programa para acceder a algunas de sus capacidades no utilizadas y hacer algo que el programa normalmente no haría.
El propósito del sandbox es identificar exactamente qué habilidades y recursos necesita un programa y luego bloquear todo lo demás.
El conjunto de herramientas de administración del sistema systemd se utiliza en casi todas las principales distribuciones de Linux para iniciar, detener y administrar programas y procesos. Tiene muchas opciones de sandbox que restringen la forma en que el proceso que inicia accede al sistema host, lo que lo hace más seguro.
El objetivo de este tutorial no es crear el entorno sandbox más estricto posible, sino utilizar las configuraciones recomendadas y fácilmente habilitables para hacer que su sistema sea más seguro.
En este tutorial, realizarás una demostración práctica de cómo usar las técnicas de sandbox de systemd en Ubuntu 20.04 para implementar y probar estas técnicas de manera eficiente. Cualquier proceso que se ejecute en un sistema Linux que use systemd puede volverse más seguro con estas técnicas.
Prerrequisitos
Necesitarás lo siguiente para comenzar esta guía:
- Un sistema Ubuntu 20.04.
- Un usuario no root con privilegios sudo. Siga la configuración inicial del servidor con Ubuntu 20.04 para obtener información sobre cómo hacer esto en Ubuntu 20.04.
- Conocimientos básicos sobre la gestión de procesos con systemd. Consulta la guía Systemd Essentials: Working with Services, Units, and the Journal para conocer los conceptos básicos.
- Será útil tener conocimientos básicos sobre los archivos de unidad de Systemd. Consulta la guía Comprensión de las unidades y los archivos de unidad de Systemd para obtener más información.
Paso 1: Instalación de lighttpd
En este tutorial, aislaremos el servidor web lighttpd. No elegimos lighttpd porque sea menos seguro que otro software, sino porque es un programa pequeño con una sola función que se puede aislar fácilmente. Esto lo convierte en una excelente opción para una aplicación de aprendizaje.
Actualicemos el sistema para iniciar:
- sudo apt update
Verifique los paquetes que se actualizarán en su sistema antes de escribir y
:
- sudo apt upgrade
Luego instala lighttpd:
- sudo apt install lighttpd
Este proceso de instalación instalará y habilitará automáticamente un archivo de servicio systemd para lighttpd. Esto hará que lighttpd se inicie al reiniciar el sistema.
Ahora que tenemos lighttpd instalado y ejecutándose en nuestro sistema, nos familiarizaremos con las herramientas systemd que usaremos cuando comencemos a crear un sandbox.
Paso 2: preparación del sistema
En este paso, se familiarizará con los comandos systemd que utilizará y preparará su sistema para permitirle aislar de manera eficiente un proceso.
systemd es un nombre genérico para un conjunto de herramientas que tienen nombres diferentes. Las dos que usará son systemctl
y journalctl
. systemctl
administra procesos y sus archivos de servicio, mientras journalctl
interactúa con el registro del sistema.
systemd utiliza archivos de servicio para definir cómo se gestionará un proceso. systemd carga estos archivos desde varias ubicaciones en el sistema de archivos. El siguiente comando le mostrará la ubicación del archivo de servicio activo y mostrará las modificaciones que se estén utilizando:
- sudo systemctl cat process.service
Debes reemplazarlo process
con el proceso en el que estás trabajando. Aquí se utiliza lighttpd:
- sudo systemctl cat lighttpd.service
Esta es la salida del comando anterior:
Output# /lib/systemd/system/lighttpd.service[Unit]Description=Lighttpd DaemonAfter=network-online.target[Service]Type=simplePIDFile=/run/lighttpd.pidExecStartPre=/usr/sbin/lighttpd -tt -f /etc/lighttpd/lighttpd.confExecStart=/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.confExecReload=/bin/kill -USR1 $MAINPIDRestart=on-failure[Install]WantedBy=multi-user.target
Esta salida muestra que el archivo de servicio se encuentra en /lib/systemd/system/lighttpd.service
y que no hay opciones de anulación en uso. Las opciones de anulación agregan o modifican el archivo de servicio base. Usará anulaciones para aislar lighttpd con un archivo de anulación dedicado.
Los archivos de anulación se encuentran en . systemd tiene un comando dedicado que creará un archivo de anulación en la ubicación correcta y se ejecutará después de guardar y salir del editor. Le indica a systemd que use cualquier configuración nueva que haya escrito./etc/systemd/system/process.service.d/override.conf
edit
systemctl daemon-reload
systemctl daemon-reload
El comando de edición systemd tiene el siguiente formato:
- sudo systemctl edit process.service
Cuando ejecutas este comando, systemd normalmente elegirá tu editor CLI predeterminado, pero no siempre es así y es posible que te encuentres en vi o incluso en ed. Puedes configurar qué editor usará systemd configurando la SYSTEMD_EDITOR
variable de shell.
Establezca esta variable de shell agregando una línea a su ~/.bashrc
archivo. Abra este archivo con un editor de texto:
- nano ~/.bashrc
Y añade la siguiente línea:
~/.bashrc
export SYSTEMD_EDITOR=editor
Cambie editor
a su editor CLI preferido. Aquí está la línea configurada para usar el editor nano:
~/.bashrc
export SYSTEMD_EDITOR=nano
Confirme que esto esté configurado después de cerrar sesión y volver a iniciarla con el echo
comando:
- echo $SYSTEMD_EDITOR
Este comando imprimirá el nombre del editor que usted configure.
La SYSTEMD_EDITOR
variable shell solo se configura en el shell del usuario y no en el shell de root que se abre con sudo
. Para pasar esta variable al shell de root, invoque systemctl edit
usando sudo -E
:
- sudo -E systemctl edit process.service
La recomendación final facilitará la depuración de su entorno aislado al mostrarle los errores que hayan causado sus cambios. Estos errores se registrarán en el registro del sistema, al que se accede con el journalctl
comando.
Durante el sandbox, se realizarán muchos cambios que interrumpirán el proceso que se está intentando aislar. Por ese motivo, es una buena idea abrir una segunda terminal y dedicarla a seguir el registro del sistema. Esto ahorrará tiempo al volver a abrir el registro del sistema.
Siga el registro del sistema en la segunda terminal ejecutando:
- sudo journalctl -f -u process.service
-f
:Siga o siga el registro del sistema para que las nuevas líneas se muestren inmediatamente.-u process.service
:Muestra únicamente las líneas de registro de lo queprocess
estás haciendo en el sandbox.
Lo siguiente es lo que debe ejecutar para imprimir solo los errores de lighttpd:
- sudo journalctl -f -u lighttpd.service
A continuación, comenzará a editar el override.conf
archivo y comenzará a poner en sandbox lighttpd.
Paso 3: Implementación de un usuario y un grupo
En este paso, configurará el usuario no root con el que se ejecutará lighttpd.
En su configuración predeterminada, lighttpd comienza a ejecutarse como usuario root y luego cambia al usuario y grupo www-data . Esto es un problema porque mientras lighttpd se ejecuta como root, puede hacer todo lo que root puede hacer, es decir, cualquier cosa.
systemd proporciona la capacidad de iniciar y ejecutar el proceso como un usuario no root, evitando así este problema.
Regrese a su primera sesión de terminal y comience a editar el archivo de anulación ejecutando:
- sudo -E systemctl edit lighttpd.service
Ahora, agregue las siguientes líneas:
archivo de anulación de lighttpd
[Service]User=www-dataGroup=www-data
[Service]
:Le dice a systemd que las siguientes opciones deben aplicarse a la[Service]
sección.User=www-data
:Define el usuario con el que se iniciará el proceso.Group=www-data
:Define el grupo como el cual se iniciará el proceso.
A continuación, guarde y salga del editor y reinicie lighttpd con el siguiente comando:
- sudo systemctl restart lighttpd.service
lighttpd no podrá iniciarse porque estaba usando la root
autorización para escribir un archivo PID en una ubicación que pertenece a root . El usuario www-data no puede escribir en directorios que pertenecen a root . Este problema se indica en el registro del sistema que aparecerá en su segunda sesión de terminal:
journalctl error messageAug 29 11:37:35 systemd lighttpd[7097]: 2020-08-29 11:37:35: (server.c.1233) opening pid-file failed: /run/lighttpd.pid Permission denied
Para resolver este problema se sigue el proceso de sandbox que es:
- Implementar una restricción de espacio aislado.
- Reinicie el proceso y verifique si hay errores.
- Corrija cualquier error.
A continuación, resolverá el problema del archivo PID mientras sigue aplicando las restricciones de usuario y grupo que estableció en esta sección.
Paso 4: Gestión del archivo PID
Un archivo PID es un archivo que contiene el PID o número de identificación de proceso de un proceso en ejecución. Los programas de larga duración como lighttpd los utilizan para gestionar sus propios procesos. El problema que encontró en la sección anterior fue que lighttpd no pudo escribir su archivo PID en /run/lighttpd.pid
, porque /run/
es propiedad de root .
systemd tiene la RuntimeDirectory
opción para este problema, que utilizarás para darle a lighttpd una ubicación donde pueda escribir su archivo PID.
La RuntimeDirectory
opción le permite especificar un directorio /run/
que se creará con el usuario y el grupo que configuró en el Paso 3 cuando systemd inicie lighttpd. lighttpd podrá escribir su PID en este directorio sin necesidad de la autoridad de root .
Primero, abra y edite el archivo de anulación con el mismo comando que utilizó en el Paso 3 :
- sudo -E systemctl edit lighttpd.service
A continuación, agregue la siguiente línea debajo de las dos líneas que ya agregó al archivo de anulación:
archivo de anulación de lighttpd
RuntimeDirectory=lighttpd
Guardar y salir del editor.
No se agrega la ruta completa al directorio con RuntimeDirectory
, solo el nombre del directorio bajo /run/
. En este caso, el directorio que creará systemd es /run/lighttpd/
.
Ahora debe configurar lighttpd para escribir su archivo PID en el nuevo directorio /run/lighttpd/
en lugar de /run/
.
Abra el archivo de configuración de lighttpd con un editor de texto:
- sudo nano /etc/lighttpd/lighttpd.conf
Cambie la siguiente línea:
/etc/lighthttpd/lighthttpd.conf
server.pid-file = "/run/lighttpd.pid"
A:
/etc/lighthttpd/lighthttpd.conf
server.pid-file = "/run/lighttpd/lighttpd.pid"
Guardar y salir del editor.
Ahora, reinicie lighttpd:
- sudo systemctl restart lighttpd.service
No se inicia porque no puede hacer algo que requiere una de las capacidades del usuario root . A continuación, resolverá este nuevo problema.
Paso 5: Tomar prestadas las capacidades de la raíz
La siguiente línea en el registro del sistema explica el problema que detuvo el inicio de lighttpd:
journalctl error messageAug 29 12:07:22 systemd lighttpd[7220]: 2020-08-29 12:07:22: (network.c.311) can't bind to socket: 0.0.0.0:80 Permission denied
Sólo root puede abrir un puerto de red por debajo del número 1024
. lighttpd está intentando abrir el puerto HTTP 80
, pero se le niega porque el usuario www-data no puede hacerlo.
El problema se resuelve dándole al proceso lighttpd una pequeña parte del poder de la raíz , es decir, abrir puertos por debajo de 1024
.
El “poder” de root se divide en habilidades llamadas “capacidades”. El usuario root tiene todas las capacidades y, por lo tanto, puede hacer cualquier cosa. Dividir el poder de root en capacidades significa que se pueden otorgar individualmente a procesos que no sean root. Esto permite que ese proceso haga algo que habría requerido un usuario root completo , pero que un usuario normal ahora puede hacer con una de las capacidades de root .
La opción systemd para darle a un proceso una o más de las capacidades de la raíz es la AmbientCapabilities
opción.
Abra el archivo de anulación:
- sudo -E systemctl edit lighttpd.service
Luego agrega la siguiente línea debajo de las líneas que ya agregaste:
archivo de anulación de lighttpd
AmbientCapabilities=CAP_NET_BIND_SERVICE
La CAP_NET_BIND_SERVICE
capacidad permite que un proceso abra puertos bajo 1024
.
Guardar y salir del archivo.
lighttpd ahora podrá iniciarse.
Ahora tiene un servidor web lighttpd en funcionamiento que ha hecho más seguro que su configuración predeterminada. Hay más opciones de sandbox proporcionadas por systemd que puede usar para hacer que su proceso de destino sea aún más seguro. Exploraremos algunas de ellas en las siguientes secciones.
En el siguiente paso, restringirá a qué puede acceder lighttpd en el sistema de archivos.
Paso 6: Bloqueo del sistema de archivos
El proceso lighttpd se ejecuta como el usuario www-data y, por lo tanto, puede acceder a cualquier archivo del sistema al que www-data tenga permiso de lectura y escritura. En el caso de www-data , no es mucho, pero aún así es más de lo que lighttpd necesita.
La primera y más sencilla configuración de sandbox es la ProtectHome
opción. Esta opción impide que el proceso lea o escriba en cualquier cosa que se encuentre debajo de /home/
. lighttpd no necesita acceder a nada que se encuentre debajo de , /home/
por lo que implementar esto protegerá todos sus archivos privados sin afectar a lighttpd.
Abra el archivo de anulación:
- sudo -E systemctl edit lighttpd.service
Luego agregue la siguiente línea al final del archivo:
archivo de anulación de lighttpd
ProtectHome=true
Guarde y salga del editor, luego reinicie lighttpd para verificar que esté funcionando como espera con el siguiente comando:
- sudo systemctl restart lighttpd.service
Ha protegido /home/
, pero aún queda el resto del sistema de archivos. Esto se soluciona con la ProtectSystem
opción , que impide que un proceso escriba en partes del sistema de archivos.
La ProtectSystem
opción tiene tres configuraciones que ofrecen niveles de protección crecientes. Son las siguientes:
true
:Establece los siguientes directorios como de solo lectura:/usr/
/boot/
/efi/
full
:Establece los siguientes directorios como de solo lectura:/usr/
/boot/
/efi/
/etc/
strict
:Establece los siguientes directorios como de solo lectura:- Todo el sistema de archivos
Un nivel de protección más alto es más seguro, así que configure la ProtectSystem
opción strict
agregando la siguiente línea al archivo de anulación:
archivo de anulación de lighttpd
ProtectSystem=strict
Guarde y salga del editor y reinicie lighttpd con el siguiente comando:
- sudo systemctl restart lighttpd.service
lighttpd no podrá iniciarse porque necesita escribir sus archivos de registro /var/log/lighttpd/
y la strict
configuración no lo permite. La siguiente línea en el registro del sistema muestra el problema:
journalctl error messageAug 29 12:44:41 systemd lighttpd[7417]: 2020-08-29 12:44:41: (server.c.752) opening errorlog '/var/log/lighttpd/error.log' failed: Read-only file system
Este problema fue anticipado por systemd con la LogsDirectory
opción. Toma el nombre de un directorio en el /var/log/
que el proceso tiene permiso para escribir sus registros.
Abra el archivo de anulación nuevamente en su primera sesión de terminal:
- sudo -E systemctl edit lighttpd.service
El directorio de registro de lighttpd es /var/log/lighttpd/
así que agregue la siguiente línea al final del archivo de anulación:
archivo de anulación de lighttpd
LogsDirectory=lighttpd
Guarde y salga del editor y reinicie lighttpd:
- sudo systemctl restart lighttpd.service
lighttpd ahora podrá iniciarse y ejecutarse.
Nota: Si está poniendo en aislamiento un proceso que no sea lighttpd y desea permitir que su proceso tenga acceso de escritura a un directorio específico fuera de él, /var/log/
utilice la opción ReadWritePaths.
En el siguiente paso, limitará la forma en que el proceso lighttpd puede interactuar con el resto del sistema restringiendo las llamadas al sistema que puede realizar.
Paso 7: Restricción de llamadas al sistema
Una llamada al sistema es la forma en que un programa solicita algo al núcleo. La cantidad de llamadas al sistema es bastante grande e incluye acciones como leer, escribir y eliminar archivos, tareas relacionadas con el hardware como montar un sistema de archivos, iniciar un proceso, reiniciar y muchas más.
systemd ha creado grupos de llamadas del sistema que los procesos, como lighttpd, suelen utilizar y que excluyen las llamadas que no utilizan. Las llamadas del sistema bloqueadas son cosas como montar un sistema de archivos y reiniciar el sistema, que lighttpd nunca necesita hacer.
Primero, abra el archivo de anulación:
- sudo -E systemctl edit lighttpd.service
Agregue la siguiente línea al final del archivo para usar la SystemCallFilter
opción para configurar el @system-service
grupo:
archivo de anulación de lighttpd
SystemCallFilter=@system-service
Guarde y salga del editor y reinicie lighttpd:
- sudo systemctl restart lighttpd.service
En la siguiente sección, aplicará las opciones restantes de sandbox recomendadas.
Paso 8: Implementación de otras opciones
La documentación de systemd recomienda que se habiliten las siguientes opciones para procesos en red de larga ejecución como lighttpd. Todas estas configuraciones son opcionales, pero cada una de ellas hace que el proceso que estás poniendo en sandbox sea más seguro y deberías usarlas si puedes.
Debes habilitar estas opciones una a la vez y reiniciar el proceso después de cada una. Si las agregas todas a la vez, será mucho más difícil depurar un problema.
Las opciones recomendadas a continuación se acompañan de una breve descripción de lo que hacen. Agregue estas líneas a su archivo de anulación debajo de las líneas que ya agregó:
archivo de anulación de lighttpd
NoNewPrivileges=true
Esta opción impide que el proceso aislado y cualquiera de sus secundarios obtengan nuevos privilegios.
archivo de anulación de lighttpd
ProtectKernelTunables=true
Esta opción evita que el proceso cambie cualquier variable del kernel.
archivo de anulación de lighttpd
ProtectKernelModules=true
Esta opción detiene el proceso de carga o descarga módulos del kernel.
archivo de anulación de lighttpd
ProtectKernelLogs=true
Esta opción impide que el proceso lea y escriba directamente en el registro del núcleo. Debe utilizar la aplicación de registro del sistema para registrar los mensajes de registro.
archivo de anulación de lighttpd
ProtectControlGroups=true
Esta opción impide que el proceso modifique los grupos de control del sistema.
archivo de anulación de lighttpd
MemoryDenyWriteExecute=true
Esta opción impide que el proceso modifique cualquier código que se esté ejecutando en la memoria del sistema.
archivo de anulación de lighttpd
RestrictSUIDSGID=true
Esta opción impide que el proceso establezca el ID de usuario (SUID) o el ID de grupo (SGID) en archivos o directorios. Esta capacidad se puede utilizar de forma abusiva para elevar privilegios.
archivo de anulación de lighttpd
KeyringMode=private
Esta opción impide que el proceso acceda al conjunto de claves del kernel de otros procesos que se ejecutan como el mismo usuario.
archivo de anulación de lighttpd
ProtectClock=true
Esta opción evita que el proceso cambie los relojes del sistema de hardware y software.
archivo de anulación de lighttpd
RestrictRealtime=true
Esta opción impide que el proceso habilite la programación en tiempo real que puede usarse de forma abusiva para sobrecargar la CPU.
archivo de anulación de lighttpd
PrivateDevices=true
Esta opción impide que el proceso acceda a los dispositivos físicos conectados al sistema, como dispositivos de almacenamiento o dispositivos USB.
archivo de anulación de lighttpd
PrivateTmp=true
Esta opción obliga al proceso a utilizar directorios privados /tmp/
. /var/tmp/
Esto impide que el proceso pueda leer los archivos temporales de otros programas que se almacenan en esos directorios compartidos del sistema.
archivo de anulación de lighttpd
ProtectHostname=true
Esta opción evita que el proceso cambie el nombre de host del sistema.
El proceso que ha protegido ahora es mucho más seguro que en su configuración predeterminada. Ahora puede tomar estas técnicas y usarlas para cualquier otro proceso que necesite proteger en su sistema Linux.
Conclusión
En este artículo, hizo que el programa lighttpd fuera más seguro mediante el uso de las opciones de sandbox de systemd. Puede utilizar estas técnicas con cualquier proceso que administre systemd, lo que le permitirá seguir mejorando la seguridad de su sistema.
La lista completa de opciones de seguridad y sandbox se encuentra en la documentación en línea de systemd. Además, consulte otros temas de seguridad en la Comunidad de DigitalOcean.
Deja una respuesta