Proteger aplicaciones Docker publicadas con Nginx Proxy Manager y Authelia 2FA
Tabla de contenidos
Introducción
Cuando publicamos aplicaciones Docker en Internet —paneles de administración, gestores de contenedores, herramientas de monitorización, interfaces de backup, dashboards o cualquier servicio sensible— es habitual colocarlas detrás de un proxy inverso como Nginx Proxy Manager. Esto permite acceder a cada aplicación mediante un subdominio, gestionar certificados HTTPS y evitar tener que recordar puertos.
Sin embargo, esta configuración por sí sola no siempre es suficiente. Si una aplicación queda expuesta directamente por su puerto Docker, por ejemplo:
http://IP_DEL_SERVIDOR:3552
cualquier protección configurada en el proxy puede saltarse. Además, muchas aplicaciones solo incorporan autenticación básica mediante usuario y contraseña. Para servicios críticos, especialmente aquellos que administran Docker, servidores, copias de seguridad o infraestructura, es recomendable añadir una capa adicional de seguridad.
La solución que se plantea en este manual consiste en:
Internet ↓Nginx Proxy Manager ↓Authelia con doble factor de autenticación ↓Aplicación Docker protegida
De esta forma, antes de llegar a la aplicación real, el usuario debe autenticarse en Authelia y superar una verificación en dos pasos. Solo después Nginx Proxy Manager permitirá el acceso al contenedor interno.
Este enfoque se basa en el mecanismo auth_request de nginx, que permite autorizar una petición en función de la respuesta de un servicio externo de autenticación. Si la subpetición devuelve una respuesta válida, nginx deja pasar al usuario; si no, lo redirige o bloquea el acceso.
Qué vamos a conseguir
Al terminar este procedimiento tendremos:
https://app.ejemplo.com ↓Nginx Proxy Manager ↓Authelia: usuario + contraseña + 2FA ↓Aplicación Docker interna
Y evitaremos esta situación insegura:
http://IP_DEL_SERVIDOR:PUERTO ↓Aplicación Docker directamente expuesta
La aplicación protegida no tendrá publicado su puerto en el host. Solo será accesible desde la red Docker interna compartida con Nginx Proxy Manager.
Por qué usar esta solución
Existen varias alternativas para proteger aplicaciones Docker publicadas:
1. Usar solo el login propio de la aplicación.2. Restringir el acceso por IP.3. Acceder exclusivamente por VPN.4. Usar autenticación básica de nginx.5. Implementar SSO/OIDC en cada aplicación.6. Usar Authelia delante de Nginx Proxy Manager.
La opción de usar únicamente el login propio de cada aplicación es cómoda, pero insuficiente para servicios sensibles. La restricción por IP puede ser útil, pero no siempre es práctica si accedemos desde redes móviles, ubicaciones cambiantes o varios dispositivos. Una VPN es una solución excelente, pero puede ser más incómoda si queremos mantener accesos web publicados de forma controlada.
La autenticación básica de nginx añade una pequeña barrera, pero no aporta segundo factor. En cambio, Authelia permite centralizar autenticación, políticas de acceso y 2FA para múltiples servicios. Además, se integra bien con nginx y Nginx Proxy Manager mediante snippets reutilizables. La documentación oficial de Authelia contempla integraciones específicas tanto con nginx como con Nginx Proxy Manager.
Arquitectura final
La arquitectura que se busca es esta:
Internet ↓Puerto 443 del VPS ↓Nginx Proxy Manager ↓Authelia ↓Red Docker interna "proxy" ↓Aplicación protegida
Ejemplo práctico:
auth.ejemplo.com → Autheliaapp.ejemplo.com → Aplicación Docker protegida
Nginx Proxy Manager será el único servicio expuesto públicamente en los puertos 80 y 443. Authelia y la aplicación protegida estarán disponibles únicamente dentro de una red Docker compartida.
Docker Compose crea redes internas para que los contenedores puedan comunicarse por nombre de servicio, y también permite definir redes externas reutilizables entre varios proyectos Compose.
Requisitos previos
Para seguir este manual se parte de un servidor Linux con:
Docker instaladoDocker Compose instaladoNginx Proxy Manager funcionandoUno o varios servicios Docker publicados mediante subdominiosAcceso SSH al servidorUn dominio propio apuntando al servidor
En los ejemplos se usarán estos datos ficticios:
Dominio principal: ejemplo.comDominio de Authelia: auth.ejemplo.comDominio de la aplicación protegida: app.ejemplo.comRed Docker compartida: proxyUsuario de Authelia: usuario_adminRuta de Authelia: /opt/autheliaRuta de Nginx Proxy Manager: /opt/nginx-proxy-manager
Sustituye estos valores por los tuyos.
1. Comprobar los contenedores actuales
Primero conviene ver qué contenedores están funcionando y qué puertos tienen publicados:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
Un ejemplo de salida problemática sería:
NAMES IMAGE PORTSmi_app imagen/mi_app:latest 0.0.0.0:3552->3552/tcpnpm jc21/nginx-proxy-manager 0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp
Aquí la aplicación mi_app está publicada directamente en el host:
0.0.0.0:3552->3552/tcp
Eso significa que se puede acceder a ella por:
http://IP_DEL_SERVIDOR:3552
aunque también esté detrás de Nginx Proxy Manager.
El objetivo es que la aplicación aparezca así:
mi_app imagen/mi_app:latest 3552/tcp
Es decir, que el puerto exista dentro del contenedor, pero no esté publicado en el host.
2. Crear una red Docker compartida
Vamos a crear una red Docker llamada proxy, que será compartida por Nginx Proxy Manager, Authelia y las aplicaciones protegidas.
docker network create proxy
Si la red ya existe, Docker mostrará un aviso. No pasa nada.
Podemos comprobar las redes existentes con:
docker network ls
Deberíamos ver algo como:
NETWORK ID NAME DRIVER SCOPExxxxxxxxxxxx proxy bridge local
3. Conectar Nginx Proxy Manager a la red compartida
Nginx Proxy Manager debe estar en la red proxy para poder resolver por nombre interno a Authelia y a las aplicaciones protegidas.
Primero identificamos el nombre real del contenedor:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
Supongamos que el contenedor se llama:
nginx_proxy_manager-app-1
Podemos conectarlo manualmente a la red:
docker network connect proxy nginx_proxy_manager-app-1
Esto sirve para probar, pero no es suficiente para que sea permanente. Si el contenedor se recrea, puede perder esta red. Por eso hay que consolidarlo en el compose.yaml de Nginx Proxy Manager.
Localizar el compose de Nginx Proxy Manager
Podemos usar las etiquetas de Docker Compose:
docker inspect nginx_proxy_manager-app-1 --format 'Working dir: {{ index .Config.Labels "com.docker.compose.project.working_dir" }}Config file: {{ index .Config.Labels "com.docker.compose.project.config_files" }}Project: {{ index .Config.Labels "com.docker.compose.project" }}Service: {{ index .Config.Labels "com.docker.compose.service" }}'
Si el archivo existe, hacemos copia:
cd /opt/nginx-proxy-managercp compose.yaml compose.yaml.bak
Si el archivo no existe, puede reconstruirse siempre que se conserven los volúmenes data y letsencrypt, que son los que guardan la configuración y certificados de Nginx Proxy Manager.
Un ejemplo de compose.yaml válido sería:
name: nginx_proxy_managerservices: app: image: jc21/nginx-proxy-manager:latest restart: unless-stopped ports: - "80:80" - "81:81" - "443:443" volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt - ./snippets:/snippets networks: - default - proxynetworks: proxy: external: true
Nginx Proxy Manager utiliza habitualmente los puertos 80, 443 y 81 para HTTP, HTTPS y la interfaz de administración, respectivamente.
Validamos:
docker compose config
Aplicamos sin hacer down:
docker compose up -d
Comprobamos que NPM está en ambas redes:
docker inspect nginx_proxy_manager-app-1 --format '{{.Name}} -> {{range $net,$v := .NetworkSettings.Networks}}{{println $net}}{{end}}'
Resultado esperado:
/nginx_proxy_manager-app-1 -> nginx_proxy_manager_defaultproxy
4. Quitar la exposición directa de la aplicación Docker
Este paso es fundamental. No tiene sentido poner 2FA delante de Nginx Proxy Manager si la aplicación puede seguir abriéndose por su puerto directo.
Supongamos que tenemos una aplicación Docker con este compose.yaml:
services: mi_app: image: imagen/mi_app:latest container_name: mi_app restart: unless-stopped ports: - "3552:3552"
Ese bloque:
ports: - "3552:3552"
publica el puerto en el host.
Hay que sustituirlo por:
expose: - "3552"
y conectar el servicio a la red proxy:
services: mi_app: image: imagen/mi_app:latest container_name: mi_app restart: unless-stopped expose: - "3552" networks: - proxynetworks: proxy: external: true
La diferencia es importante:
ports → publica un puerto del contenedor en el hostexpose → documenta/expone el puerto internamente para otros contenedores
Docker documenta la publicación de puertos como el mecanismo para hacer accesible un puerto del contenedor desde el host, mientras que las redes de Compose permiten que los servicios se comuniquen internamente por nombre.
Aplicamos:
cd /opt/mi_appdocker compose up -d --force-recreate
Comprobamos:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
Resultado correcto:
mi_app imagen/mi_app:latest 3552/tcp
Resultado incorrecto:
mi_app imagen/mi_app:latest 0.0.0.0:3552->3552/tcp
También es incorrecto esto:
ports: - "3552"
porque Docker puede asignar un puerto aleatorio del host, por ejemplo:
0.0.0.0:32768->3552/tcp
En ese caso la aplicación seguiría publicada, aunque en otro puerto.
5. Configurar el Proxy Host de la aplicación en NPM
En Nginx Proxy Manager vamos a editar o crear el Proxy Host de la aplicación.
Entramos en:
Hosts → Proxy Hosts → Add Proxy Host
o editamos uno existente.
En la pestaña Details:
Domain Names:app.ejemplo.comScheme:httpForward Hostname / IP:mi_appForward Port:3552
Activamos:
Cache Assets: OFFBlock Common Exploits: ONWebsockets Support: ON, si la aplicación usa WebSockets
En la pestaña SSL:
Request a new SSL CertificateForce SSL: ONHTTP/2 Support: ON
En este punto la aplicación debería seguir funcionando por:
https://app.ejemplo.com
pero ya no por:
http://IP_DEL_SERVIDOR:3552
Para probar la comunicación interna:
docker run --rm --network proxy curlimages/curl:latest -I http://mi_app:3552
Si devuelve HTTP/1.1 200, 302, 401 o similar, la red interna funciona.
6. Instalar Authelia
Creamos la estructura:
mkdir -p /opt/authelia/configcd /opt/authelia
Creamos el compose.yaml:
nano compose.yaml
Contenido:
services: authelia: image: authelia/authelia:latest container_name: authelia restart: unless-stopped expose: - "9091" volumes: - ./config:/config networks: - proxynetworks: proxy: external: true
Authelia utiliza ficheros YAML de configuración y puede desplegarse en Docker montando un directorio de configuración en /config.
7. Generar secretos para Authelia
Generamos tres valores secretos:
openssl rand -hex 32openssl rand -hex 32openssl rand -hex 32
No deben publicarse ni compartirse.
Los usaremos para:
jwt_secretsession.secretstorage.encryption_key
También podemos usar la propia CLI de Authelia para generar valores seguros, ya que Authelia documenta comandos específicos para generar valores aleatorios y hashes criptográficos.
8. Crear la configuración principal de Authelia
Creamos:
nano /opt/authelia/config/configuration.yml
Contenido de ejemplo:
server: address: 'tcp://:9091/'log: level: 'info'totp: issuer: 'ejemplo.com'identity_validation: reset_password: jwt_secret: 'PEGAR_AQUI_SECRETO_1'authentication_backend: file: path: '/config/users_database.yml'access_control: default_policy: 'deny' rules: - domain: 'auth.ejemplo.com' policy: 'bypass' - domain: 'app.ejemplo.com' policy: 'two_factor'session: secret: 'PEGAR_AQUI_SECRETO_2' cookies: - domain: 'ejemplo.com' authelia_url: 'https://auth.ejemplo.com' default_redirection_url: 'https://app.ejemplo.com' same_site: 'lax' inactivity: '10m' expiration: '1h' remember_me: '1d'regulation: max_retries: 3 find_time: '2m' ban_time: '10m'storage: encryption_key: 'PEGAR_AQUI_SECRETO_3' local: path: '/config/db.sqlite3'notifier: filesystem: filename: '/config/notification.txt'
La sección access_control permite definir qué dominios requieren una autenticación simple, doble factor, bypass o denegación. En este caso, auth.ejemplo.com queda accesible para poder iniciar sesión, mientras que app.ejemplo.com requiere doble factor.
9. Crear el usuario de Authelia
Authelia no guarda contraseñas en texto claro. Hay que generar un hash.
Ejecutamos:
docker run --rm -it authelia/authelia:latest authelia crypto hash generate argon2
Introducimos la contraseña cuando la pida.
El resultado tendrá un formato parecido a:
Digest: $argon2id$v=19$m=65536,t=3,p=4$...
En el fichero de usuarios hay que pegar solo la parte que empieza por $argon2id$, sin el prefijo Digest:. Authelia documenta el comando authelia crypto hash generate argon2 para generar hashes Argon2.
Creamos:
nano /opt/authelia/config/users_database.yml
Contenido:
users: usuario_admin: disabled: false displayname: 'Administrador' password: '$argon2id$v=19$m=65536,t=3,p=4$HASH_DE_EJEMPLO' email: 'admin@ejemplo.com' groups: - 'admins'
Aplicamos permisos:
chmod 600 /opt/authelia/config/configuration.ymlchmod 600 /opt/authelia/config/users_database.yml
Arrancamos:
cd /opt/autheliadocker compose up -d
Comprobamos logs:
docker logs -f authelia
Resultado esperado:
Startup completeListening for non-TLS connections on '[::]:9091'
Si aparece un error como:
argon2 decode errorillegal base64 data
lo más probable es que se haya pegado mal el hash, por ejemplo incluyendo Digest: o algún carácter adicional.
10. Publicar Authelia en Nginx Proxy Manager
Ahora necesitamos acceder a Authelia desde:
https://auth.ejemplo.com
En Nginx Proxy Manager:
Hosts → Proxy Hosts → Add Proxy Host
Pestaña Details:
Domain Names:auth.ejemplo.comScheme:httpForward Hostname / IP:autheliaForward Port:9091Cache Assets:OFFBlock Common Exploits:ONWebsockets Support:OFF
Pestaña SSL:
Request a new SSL CertificateForce SSL: ONHTTP/2 Support: ON
Guardamos y probamos:
https://auth.ejemplo.com
Debería aparecer la pantalla de Authelia.
11. Preparar snippets en Nginx Proxy Manager
Para que Nginx Proxy Manager pueda consultar a Authelia antes de enviar tráfico a una aplicación, vamos a usar snippets.
La integración oficial de Authelia con Nginx Proxy Manager se basa en incluir bloques de configuración en el campo Advanced de cada Proxy Host protegido.
Creamos la carpeta de snippets si no existe:
mkdir -p /opt/nginx-proxy-manager/snippets
El compose.yaml de NPM debe montar esa carpeta dentro del contenedor:
volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt - ./snippets:/snippets
Aplicamos:
cd /opt/nginx-proxy-managerdocker compose up -d
Comprobamos que el contenedor ve la carpeta:
docker exec -it nginx_proxy_manager-app-1 ls -la /snippets
12. Crear los snippets de Authelia
proxy.conf
nano /opt/nginx-proxy-manager/snippets/proxy.conf
Contenido:
proxy_set_header Host $host;proxy_set_header X-Original-URL $scheme://$http_host$request_uri;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Forwarded-Host $http_host;proxy_set_header X-Forwarded-URI $request_uri;proxy_set_header X-Forwarded-Ssl on;proxy_set_header X-Forwarded-For $remote_addr;proxy_set_header X-Real-IP $remote_addr;client_body_buffer_size 128k;proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;proxy_redirect http:// $scheme://;proxy_http_version 1.1;proxy_cache_bypass $cookie_session;proxy_no_cache $cookie_session;proxy_buffers 64 256k;send_timeout 5m;proxy_read_timeout 360;proxy_send_timeout 360;proxy_connect_timeout 360;
websocket.conf
nano /opt/nginx-proxy-manager/snippets/websocket.conf
Contenido:
proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";
Este snippet es necesario para aplicaciones que usan WebSockets. En el caso de Arcane, por ejemplo, su documentación indica que detrás de un reverse proxy debe configurarse soporte WebSocket para que la interfaz funcione correctamente en tiempo real.
authelia-location.conf
nano /opt/nginx-proxy-manager/snippets/authelia-location.conf
Contenido:
set $upstream_authelia http://authelia:9091/api/authz/auth-request;location /internal/authelia/authz { internal; proxy_pass $upstream_authelia; proxy_set_header X-Original-Method $request_method; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Content-Length ""; proxy_set_header Connection ""; proxy_pass_request_body off; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; proxy_redirect http:// $scheme://; proxy_http_version 1.1; proxy_cache_bypass $cookie_session; proxy_no_cache $cookie_session; proxy_buffers 4 32k; client_body_buffer_size 128k; send_timeout 5m; proxy_read_timeout 240; proxy_send_timeout 240; proxy_connect_timeout 240;}
authelia-authrequest.conf
nano /opt/nginx-proxy-manager/snippets/authelia-authrequest.conf
Contenido:
auth_request /internal/authelia/authz;auth_request_set $user $upstream_http_remote_user;auth_request_set $groups $upstream_http_remote_groups;auth_request_set $name $upstream_http_remote_name;auth_request_set $email $upstream_http_remote_email;proxy_set_header Remote-User $user;proxy_set_header Remote-Groups $groups;proxy_set_header Remote-Email $email;proxy_set_header Remote-Name $name;auth_request_set $redirection_url $upstream_http_location;error_page 401 =302 $redirection_url;
El bloque auth_request hace que nginx consulte a Authelia mediante una subpetición interna antes de dejar pasar la solicitud al backend protegido.
Comprobamos la configuración de nginx:
docker exec -it nginx_proxy_manager-app-1 nginx -t
Si todo está correcto:
docker exec -it nginx_proxy_manager-app-1 nginx -s reload
13. Proteger una aplicación concreta con Authelia
Ahora vamos a proteger app.ejemplo.com.
En Nginx Proxy Manager:
Hosts → Proxy Hosts → app.ejemplo.com → Edit
En Details:
Scheme:httpForward Hostname / IP:mi_appForward Port:3552Cache Assets:OFFBlock Common Exploits:ON
En Advanced, borramos cualquier configuración personalizada previa y añadimos:
include /snippets/authelia-location.conf;location / { include /snippets/proxy.conf; include /snippets/websocket.conf; include /snippets/authelia-authrequest.conf; proxy_pass $forward_scheme://$server:$port;}
Guardamos.
Después validamos nginx:
docker exec -it nginx_proxy_manager-app-1 nginx -t
Si está correcto:
docker exec -it nginx_proxy_manager-app-1 nginx -s reload
14. Registrar el segundo factor
Entramos en ventana privada:
https://app.ejemplo.com
El flujo esperado será:
app.ejemplo.com ↓redirige a auth.ejemplo.com ↓usuario + contraseña ↓registro o validación del segundo factor ↓vuelve a app.ejemplo.com
Como hemos configurado notificación por fichero, el enlace de registro del segundo factor aparecerá en:
cat /opt/authelia/config/notification.txt
Abrimos el enlace, escaneamos el código QR con una aplicación TOTP como Aegis, 2FAS, Google Authenticator, Microsoft Authenticator u otra compatible, y terminamos el registro.
Authelia soporta segundo factor mediante TOTP y también llaves de seguridad WebAuthn, entre otros métodos.
15. Comprobaciones finales
Comprobar que la aplicación no está expuesta directamente
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
Resultado correcto:
mi_app imagen/mi_app:latest 3552/tcp
Resultado incorrecto:
mi_app imagen/mi_app:latest 0.0.0.0:3552->3552/tcp
Comprobar que NPM y la app comparten red
docker inspect mi_app --format '{{.Name}} -> {{range $net,$v := .NetworkSettings.Networks}}{{println $net}}{{end}}'
Debe aparecer:
proxy
Y para NPM:
docker inspect nginx_proxy_manager-app-1 --format '{{.Name}} -> {{range $net,$v := .NetworkSettings.Networks}}{{println $net}}{{end}}'
Debe aparecer también:
proxy
Comprobar resolución interna
docker run --rm --network proxy curlimages/curl:latest -I http://mi_app:3552
Debe responder con una cabecera HTTP.
Comprobar acceso externo
Desde el navegador:
https://app.ejemplo.com
Debe pasar primero por Authelia.
Desde fuera, esto no debería funcionar:
http://IP_DEL_SERVIDOR:3552
16. Añadir más aplicaciones protegidas
Para proteger otra aplicación Docker, se repite el mismo patrón.
Supongamos una nueva aplicación:
monitor.ejemplo.comcontenedor: monitorpuerto interno: 3000
Su compose.yaml debería tener:
services: monitor: image: imagen/monitor:latest container_name: monitor restart: unless-stopped expose: - "3000" networks: - proxynetworks: proxy: external: true
En Authelia añadimos una regla:
access_control: default_policy: 'deny' rules: - domain: 'auth.ejemplo.com' policy: 'bypass' - domain: 'app.ejemplo.com' policy: 'two_factor' - domain: 'monitor.ejemplo.com' policy: 'two_factor'
Reiniciamos Authelia:
cd /opt/autheliadocker compose restart authelia
En Nginx Proxy Manager creamos el Proxy Host:
Domain Names: monitor.ejemplo.comScheme: httpForward Hostname / IP: monitorForward Port: 3000
Y en Advanced:
include /snippets/authelia-location.conf;location / { include /snippets/proxy.conf; include /snippets/websocket.conf; include /snippets/authelia-authrequest.conf; proxy_pass $forward_scheme://$server:$port;}
17. Errores habituales y solución
Error: Could not resolve host: mi_app
Si al probar:
docker run --rm --network proxy curlimages/curl:latest -I http://mi_app:3552
aparece:
Could not resolve host
significa que el contenedor no está en la red proxy o no tiene ese nombre.
Comprobar redes:
docker inspect mi_app --format '{{.Name}} -> {{range $net,$v := .NetworkSettings.Networks}}{{println $net}}{{end}}'
Conectar temporalmente:
docker network connect --alias mi_app proxy mi_app
Y consolidar en el compose.yaml:
networks: - proxy
Error: la aplicación aparece publicada en un puerto aleatorio
Si vemos:
0.0.0.0:32768->3552/tcp
probablemente en el compose se ha puesto:
ports: - "3552"
Eso sigue publicando el servicio. Hay que eliminar ports: y usar:
expose: - "3552"
Error: Authelia no arranca por el hash
Si los logs muestran:
argon2 decode errorillegal base64 data
revisar users_database.yml.
Debe ser:
password: '$argon2id$v=19$m=...'
No debe ser:
password: 'Digest: $argon2id$v=19$m=...'
Error: Failed to fetch al reiniciar desde un panel de Docker
Si se está gestionando desde una aplicación que a su vez depende de Docker, puede fallar al reiniciar su propio proyecto o la red que le da acceso. Para cambios en la infraestructura base —Nginx Proxy Manager, Authelia, redes Docker o el propio gestor Docker— es más seguro usar SSH:
docker compose up -d
Error: nginx no recarga
Comprobar:
docker exec -it nginx_proxy_manager-app-1 nginx -t
Si hay errores, revisar el bloque Advanced del Proxy Host y los snippets.
18. Copia de seguridad recomendada
Después de dejar todo funcionando, conviene guardar una copia de seguridad de las configuraciones:
tar -czf /root/backup-npm-authelia-apps-$(date +%F-%H%M).tgz \ /opt/authelia \ /opt/nginx-proxy-manager \ /opt/mi_app
También es recomendable guardar en un gestor de contraseñas:
Usuario de AutheliaContraseña de AutheliaCódigos de recuperación 2FA, si se generanUbicación de los ficheros configuration.yml y users_database.ymlDominio de AutheliaLista de aplicaciones protegidas
Nunca deben publicarse:
ContraseñasHashes realesSecretos de sesiónClaves de cifradoTokensIPs reales internas si no se desea exponer la arquitectura
19. Resultado final
Tras aplicar esta configuración, cualquier aplicación Docker publicada quedará protegida así:
Usuario externo ↓HTTPS ↓Nginx Proxy Manager ↓Authelia ↓usuario + contraseña + segundo factor ↓aplicación Docker interna
Y la aplicación ya no será accesible por su puerto directo:
http://IP_DEL_SERVIDOR:PUERTO
Esto no convierte el servidor en invulnerable, pero sí mejora mucho la seguridad respecto a una publicación directa con simple usuario y contraseña.
La clave está en combinar varias capas:
1. No publicar puertos internos innecesarios.2. Usar una red Docker interna compartida.3. Publicar solo Nginx Proxy Manager.4. Añadir Authelia como barrera 2FA.5. Mantener el login propio de cada aplicación.6. Revisar logs y copias de seguridad.
20. Referencias y documentación consultada
Para la elaboración de este manual se ha utilizado documentación oficial y asistencia técnica interactiva:
- Documentación oficial de Authelia sobre integración con nginx.
- Documentación oficial de Authelia sobre integración con Nginx Proxy Manager.
- Documentación oficial de Authelia sobre control de acceso y políticas
two_factor. - Documentación oficial de Authelia sobre segundo factor de autenticación.
- Documentación oficial de Authelia sobre configuración mediante ficheros YAML.
- Documentación oficial de Authelia sobre generación de hashes Argon2.
- Documentación oficial de nginx sobre el módulo
auth_request. - Documentación de NGINX sobre autenticación mediante subpeticiones.
- Documentación oficial de Docker Compose sobre redes y comunicación entre servicios.
- Documentación oficial de Docker sobre publicación de puertos.
- Documentación oficial de Nginx Proxy Manager sobre instalación y puertos utilizados.
- Documentación oficial de Arcane sobre instalación, reverse proxy y WebSockets, usada como caso práctico de aplicación Docker sensible protegida detrás de Nginx Proxy Manager.
- ChatGPT, de OpenAI, como asistente técnico utilizado para diseñar, depurar y documentar el procedimiento paso a paso, incluyendo la anonimización de datos privados y la adaptación del proceso a un formato publicable en tecnotizate.es.
