780 - La guía definitiva de Hardening y Despliegue en Podman. De 0 a Producción en 2 minutos
Acompañame en la migración de atareao.es a Podman. Aprende a usar Quadlets, YADM y hardening avanzado para un servidor rootless e inexpugnable.
Desde el 15 de enero vengo hablándote de Podman, y en el episodio de hoy vuelvo con el auto alojamiento, con el SelfHosting, pero esta vez todo orquestado con Podman. Es decir, cómo montar tu propio servidor con Podman para alojar tus aplicaciones y servicios de una manera sencilla y eficiente. Y es que, el problema principal aquí radica precisamente en que no tenemos, por ahora, una herramienta como Portainer o Dockge o cualquiera de las herramientas de las que te he hablado a lo largo de los distintos episodios, para gestionar nuestros stacks. Esto obliga a darle una vuelta a la gestión, para hacerla lo mas eficiente posible. Y la verdad, si lo piensas, todo esto no son mas que archivos de configuración dotfiles. Y siendo así, está claro que la mejor manera de gestionarlos, o mejor dicho, la manera mas eficiente de gestionarlos es simplemente utilizando un gestor de dotfiles, por ejemplo yadm. Así en este episodio, te cuento como lo he montado, para que tu también lo puedas replicar de la misma forma.

La guía definitiva de Hardening y Despliegue en Podman. De 0 a Producción en 2 minutos
Desde el principio
Para poder hacer el ejercicio completo, y por supuesto, antes de llevarme atareao.es y el resto de infra a Podman, he contratado un servidor en Hetzner. El objetivo, era por supuesto, hacer un despliegue completo con Podman, montando un blog con WordPress y el resto de servicios que normalmente utilizo, como puede ser el servicio de estadísticas, etc.
Así que he empezado desde el principio, creando un usuario para gestionar todo esto, y configurando para que fuera lo más cómodo posible. Y aquí es donde he cambiado ligeramente mi pensamiento. Al fin y al cabo, el usuario que levante todos los contenedores no necesita para nada ganar derechos de root. Y esto me ha llevado a configurar dos usuarios. Uno que se encargue de las tareas administrativas, y otro que se encargue de levantar los contenedores. De esta forma, el usuario que levante los contenedores no tiene ningún derecho de root, ni siquiera posibilidad de sudo, y por lo tanto, si por ejemplo, un atacante consiguiera acceder a ese usuario, no podría hacer nada más que lo que le permitiera ese usuario. Y esto es algo que me parece fundamental para la seguridad de mi servidor.
Y así comienza la primera parte de este episodio, con la preparación del servidor, el hardening y la creación de usuarios.
Primeros pasos
Para estos primeros pasos he creado un sencillo script con el que hacer el hardening y crear los usuarios con los que haremos tanto las labores de mantenimiento e instalación en aquellos casos que sea necesario, como el usuario que se encargará de levantar los contenedores.
Las herramientas
Lo primero que hace el escript es actualizar el sistema e instalar algunas herramientas imprescindible para la gestión del servidor, como pueden ser ufw para la gestión del firewall, fail2ban para la protección contra ataques de fuerza bruta, unattended-upgrades para la gestión de las actualizaciones automáticas, etc.
Por otro lado, instala algunas herramientas que me ayudan para trabajar en el sistema. Herramientas como bat, zoxide y neovim. Y por supuesto instala podman para la gestión de los contenedores.
Los usuarios
El siguiente paso es la creación de los usuarios. Para esto, he creado dos usuarios, uno llamado admin que se encargará de las tareas administrativas, y otro llamado apps que se encargará de levantar los contenedores. El usuario admin tiene permisos de sudo, mientras que el usuario apps no tiene ningún permiso de sudo.
Un detalle importante a resaltar es que el script configura el acceso mediante clave pública inmediatamente, para evitar tener que configurar el acceso posteriormente. De esta forma, una vez ejecutado el script, ya tenemos todo listo para acceder al servidor con el usuario admin y empezar a configurar todo lo necesario para el despliegue de nuestras aplicaciones. Esto es tan sencillo como copiar la clave pública al servidor, no comprometes nada.
Hardening del Kernel
Ya que nos ponemos, también he aprovechado para hacer un poco de hardening del kernel, configurando algunos parámetros de seguridad en el archivo /etc/sysctl.conf. En este caso se actúa sobre cuatro puntos concretos,
- Protección contra ataques de red. Se evita que el sistema sea manipulado por paquetes maliciosos que intenten cambiar las rutas de red, desactivando la aceptación de mensajes ICMP o evitando que el servidor actúe como router.
- Integridad y Registro. Se habilita el filtrado de ruta inversa, de forma que el kenejjjl verifique que los paquetes entrantes tienen una ruta de retorno válida, lo que ayuda a prevenir ataques de spoofing. Además registra en el log del sistema los paquetes sospechosos que no cumplen con esta verificación.
- Seguridad del Kernel y TCP. Se evita que usuarios sin privilegios vean las direcciones de memoria de los punteros del kernel y se restringe el uso de eventos de rendimiento.
A parte de estas reglas de hardening del kernel, también se ha habilitado el uso de puerto a partir del 80 y se aumenta el tamaño máximo de los buffers de recepción.
Hardening de SSH
Por supuesto, otra de las cuestiones y endurecer las condiciones para acceder al servidor a través de SSH. Para esto, se han configurado varias opciones en el archivo de configuración de SSH, como pueden ser,
- Control de acceso y autenticación. Lo primero es modificar el puerto por defecto, el 22, que si bien no es seguridad por si mismo, si por lo menos reduce el ruido. Por otro lado he deshabilitado el acceso al usuario root, y solo se puede acceder con clave publico privada, estableciendose ademas una lista blanca de los usuarios que pueden acceder al servidor.
- Limitación de intentos y sesiones. Si se falla la autenticación la conexión se corta. Establecí un control de inactividad.
- Reducción de la superficie de ataque. Por un lado he deshabilitado los métodos de autenticación antiguos y el PAM. Igualmente deshabilité la posibilidad de ejecutar aplicaciones gráficas remotas.
Uno de los detalles es que al cambiar el puerto en la configuración es necesario reiniciar el socket de ssh con systemctl restart ssh.socket. Inicialmente no lo estaba haciendo solo reiniciaba ssh, y claro, algo no funcionaba.
Fail2ban
Inicialmnete no había configurado fail2ban, pero después de un par de días, me di cuenta que estaba recibiendo muchos intentos de acceso. Lo cierto es que los intentos por contraseña no progresan pero consumían recursos del servidor, así que decidí configurar fail2ban para bloquear las IPs que intentan acceder de forma no autorizada. Para esto, he creado una configuración personalizada para SSH, que bloquea las IPs después de un número determinado de intentos fallidos.
La configuración de fail2ban que he utilizado es bastante agresiva, porque reduce el máximo número de intentos a 2, y la ventana de observación a una hora, de forma que si fallan dos veces en una hora directamente van fuera. Pero además he establecido un baneo de 24 horas.
Configurando el usuario
Para el usuario, dado que solo voy a tener un único usuario que se encargará de hacer los despliegues de los contenedores he implementado otro script, que se encargue de añadir la persistencia del usuario, habilitar e iniciar el socket de Podman, y descargar todos los binarios que utilizo habitualmente.
¿Porque hacerlo así? Porque de esta forma este usuario puede descargar la última versión, teniendo en cuenta que no tiene persmisos de sudo. Otra opción sería descargarlos con el usuario admin y luego pasarlos al usuario apps, pero esto me parece mas sencillo, y además.
Por otro lado, he añadido un hardening para el usuario reduciendo al máximo los permisos entre los distintos directorios y archivos y he inicializado la gesión de secretos. Configurando los secretos de WordPress.
A partir de aquí, funciono de forma completamente normal utilizando yadm, un gestor de archivos de configuración que me va a permitir tener toda la configuración de los quadlets perfectamente versionada.
Levantando servicios
Una vez hecho todo esto comenzamos a trabajar de forma completamente transparente. Recargamos todos los servicios,
systemctl --user daemon-reload
systemctl --user start traefik wordpress pocketid
Ahora tienes que configurar PocketId y posteriormente Traeik para que redirija el tráfico a los distintos servicios, pero una vez hecho esto, todo funciona de forma completamente transparente. Y lo mejor de todo es que ahora tengo un servidor completamente gestionado con Podman, sin necesidad de utilizar herramientas adicionales para la gestión de los contenedores si no quiero.
Configuras tus credenciales OIDC,
# Generar secreto aleatorio
openssl rand -hex 16 | crypta store oidc_secret
# Almacenar credenciales de PocketID (reemplaza con valores reales)
echo "tu-client-id" | crypta store oidc_client_id
echo "tu-client-secret" | crypta store oidc_client_secret
# Aplicar configuración actualizada
unjinja
# Reiniciar Traefik
systemctl --user restart traefik
La simplificación
Para simplificarlo todo y poder levantar únicamente aquellos servicios que realmente me interesa, he implementado una serie de scripts que puedes encontrar en mis dotfiles que permiten habilitar y deshabilitar los servicios de forma sencilla.
Conclusión
A partir de aquí cualquier cambio que quieras hacer, cualquier servicio que quieras añadir, lo hacer en local, lo subes a tu repositorio de dotfiles y luego lo despliegas en el servidor. De esta forma, tienes un control total sobre tu servidor, sin necesidad de utilizar herramientas adicionales para la gestión de los contenedores, y con la seguridad que te da el hecho de que el usuario que levanta los contenedores no tiene ningún permiso de root.