From b59ece3b5811fc3933f87a7bed3d1e5696d7245f Mon Sep 17 00:00:00 2001 From: groales Date: Sat, 29 Nov 2025 17:16:45 +0100 Subject: [PATCH] =?UTF-8?q?Documentaci=C3=B3n=20completa=20de=20NGINX=20Pr?= =?UTF-8?q?oxy=20Manager=20en=20espa=C3=B1ol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backup.md | 464 +++++++++++++++++++++++++++++++++++++++++++++++ Configuracion.md | 341 ++++++++++++++++++++++++++++++++++ Home.md | 244 +++++++++++++++++++++++++ SSL.md | 344 +++++++++++++++++++++++++++++++++++ 4 files changed, 1393 insertions(+) create mode 100644 Backup.md create mode 100644 Configuracion.md create mode 100644 Home.md create mode 100644 SSL.md diff --git a/Backup.md b/Backup.md new file mode 100644 index 0000000..4eecf6b --- /dev/null +++ b/Backup.md @@ -0,0 +1,464 @@ +# Backup y Restauración + +## ¿Qué se Respalda? + +NGINX Proxy Manager almacena datos en dos volúmenes Docker: + +### `npm_data` (`/data`) +- 📊 **Base de datos SQLite** (o configuración de DB externa) +- ⚙️ **Configuración de proxy hosts, redirects, streams** +- 🔑 **Access lists, usuarios, permisos** +- 📝 **Logs de acceso y errores** +- 🔧 **Configuraciones NGINX custom** + +### `npm_letsencrypt` (`/etc/letsencrypt`) +- 🔒 **Certificados SSL de Let's Encrypt** +- 🔑 **Claves privadas** +- 📜 **Configuración de renovación automática** + +⚠️ **Crítico**: Ambos volúmenes contienen información sensible. Protege los backups adecuadamente. + +## Backup Manual + +### Método 1: Backup de Volúmenes (Recomendado) + +```bash +# Detener NPM (opcional, para consistencia) +docker compose down + +# Backup de npm_data +docker run --rm \ + -v npm_data:/data:ro \ + -v $(pwd):/backup \ + alpine \ + tar czf /backup/npm_data-$(date +%Y%m%d-%H%M%S).tar.gz -C /data . + +# Backup de npm_letsencrypt +docker run --rm \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v $(pwd):/backup \ + alpine \ + tar czf /backup/npm_letsencrypt-$(date +%Y%m%d-%H%M%S).tar.gz -C /letsencrypt . + +# Reiniciar NPM +docker compose up -d +``` + +### Método 2: Backup en Caliente + +Sin detener el servicio: + +```bash +# Backup de npm_data (servicio corriendo) +docker run --rm \ + -v npm_data:/data:ro \ + -v $(pwd):/backup \ + alpine \ + tar czf /backup/npm_data-$(date +%Y%m%d-%H%M%S).tar.gz -C /data . + +# Backup de npm_letsencrypt +docker run --rm \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v $(pwd):/backup \ + alpine \ + tar czf /backup/npm_letsencrypt-$(date +%Y%m%d-%H%M%S).tar.gz -C /letsencrypt . +``` + +⚠️ **Nota**: Backups en caliente pueden tener inconsistencias en la base de datos si hay escrituras durante la copia. + +### Método 3: Backup de Carpetas Locales + +Si usas bind mounts en lugar de volúmenes: + +```yaml +# docker-compose.yaml con bind mounts +volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt +``` + +```bash +# Backup simple +tar czf npm-backup-$(date +%Y%m%d-%H%M%S).tar.gz data/ letsencrypt/ + +# O con rsync +rsync -av data/ /ruta/backup/npm/data/ +rsync -av letsencrypt/ /ruta/backup/npm/letsencrypt/ +``` + +## Restauración + +### Restaurar desde Backup + +```bash +# 1. Detener NPM si está corriendo +docker compose down + +# 2. Eliminar volúmenes existentes (⚠️ cuidado!) +docker volume rm npm_data npm_letsencrypt + +# 3. Crear volúmenes nuevos +docker volume create npm_data +docker volume create npm_letsencrypt + +# 4. Restaurar npm_data +docker run --rm \ + -v npm_data:/data \ + -v $(pwd):/backup \ + alpine \ + tar xzf /backup/npm_data-YYYYMMDD-HHMMSS.tar.gz -C /data + +# 5. Restaurar npm_letsencrypt +docker run --rm \ + -v npm_letsencrypt:/letsencrypt \ + -v $(pwd):/backup \ + alpine \ + tar xzf /backup/npm_letsencrypt-YYYYMMDD-HHMMSS.tar.gz -C /letsencrypt + +# 6. Verificar permisos +docker run --rm \ + -v npm_data:/data \ + alpine \ + chown -R root:root /data + +# 7. Reiniciar NPM +docker compose up -d +``` + +### Verificar Restauración + +```bash +# Ver logs +docker logs -f nginx-proxy-manager + +# Debería ver: +# [setup ] Starting backend +# [nginx ] Starting nginx + +# Acceder a UI +# http://IP:81 +``` + +## Backup Automatizado + +### Script de Backup + +Crear `backup-npm.sh`: + +```bash +#!/bin/bash + +# Configuración +BACKUP_DIR="/var/backups/nginx-proxy-manager" +RETENTION_DAYS=30 +TIMESTAMP=$(date +%Y%m%d-%H%M%S) + +# Crear directorio si no existe +mkdir -p "$BACKUP_DIR" + +echo "[$(date)] Iniciando backup de NGINX Proxy Manager..." + +# Backup de volúmenes +docker run --rm \ + -v npm_data:/data:ro \ + -v "$BACKUP_DIR":/backup \ + alpine \ + tar czf "/backup/npm_data-$TIMESTAMP.tar.gz" -C /data . + +docker run --rm \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v "$BACKUP_DIR":/backup \ + alpine \ + tar czf "/backup/npm_letsencrypt-$TIMESTAMP.tar.gz" -C /letsencrypt . + +# Verificar backups +if [ -f "$BACKUP_DIR/npm_data-$TIMESTAMP.tar.gz" ] && [ -f "$BACKUP_DIR/npm_letsencrypt-$TIMESTAMP.tar.gz" ]; then + SIZE_DATA=$(du -h "$BACKUP_DIR/npm_data-$TIMESTAMP.tar.gz" | cut -f1) + SIZE_LE=$(du -h "$BACKUP_DIR/npm_letsencrypt-$TIMESTAMP.tar.gz" | cut -f1) + echo "[$(date)] Backup completado:" + echo " - npm_data: $SIZE_DATA" + echo " - npm_letsencrypt: $SIZE_LE" +else + echo "[$(date)] ERROR: Backup falló" + exit 1 +fi + +# Limpiar backups antiguos +echo "[$(date)] Limpiando backups antiguos (>$RETENTION_DAYS días)..." +find "$BACKUP_DIR" -name "npm_*.tar.gz" -mtime +$RETENTION_DAYS -delete + +echo "[$(date)] Proceso completado" +``` + +Hacer ejecutable: + +```bash +chmod +x backup-npm.sh +./backup-npm.sh +``` + +### Programar con Cron + +Backup diario a las 3:00 AM: + +```bash +crontab -e + +# Añadir línea: +0 3 * * * /ruta/a/backup-npm.sh >> /var/log/npm-backup.log 2>&1 +``` + +Ver logs: + +```bash +tail -f /var/log/npm-backup.log +``` + +## Backup a Almacenamiento Remoto + +### rsync a Servidor Remoto + +```bash +#!/bin/bash +BACKUP_FILE="npm-backup-$(date +%Y%m%d-%H%M%S).tar.gz" + +# Crear backup local +docker run --rm \ + -v npm_data:/data:ro \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v $(pwd):/backup \ + alpine \ + sh -c "tar czf /backup/$BACKUP_FILE -C / data letsencrypt" + +# Copiar a servidor remoto +rsync -avz --progress "$BACKUP_FILE" usuario@servidor-backup:/backups/npm/ + +# Opcional: eliminar backup local +rm "$BACKUP_FILE" +``` + +### S3 / MinIO con rclone + +```bash +# Configurar rclone (una vez) +rclone config + +# Script de backup a S3 +BACKUP_FILE="npm-backup-$(date +%Y%m%d-%H%M%S).tar.gz" + +docker run --rm \ + -v npm_data:/data:ro \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v $(pwd):/backup \ + alpine \ + sh -c "tar czf /backup/$BACKUP_FILE -C / data letsencrypt" + +rclone copy "$BACKUP_FILE" s3-remote:bucket-name/npm-backups/ +rm "$BACKUP_FILE" +``` + +### Backup a NFS/CIFS + +```bash +# Montar share NFS +mount -t nfs servidor.local:/backups /mnt/backups + +# Backup directo a share +docker run --rm \ + -v npm_data:/data:ro \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v /mnt/backups:/backup \ + alpine \ + tar czf /backup/npm-backup-$(date +%Y%m%d-%H%M%S).tar.gz -C / data letsencrypt +``` + +## Migración a Nuevo Servidor + +### En el Servidor Antiguo + +```bash +# Crear backup completo +docker run --rm \ + -v npm_data:/data:ro \ + -v npm_letsencrypt:/letsencrypt:ro \ + -v $(pwd):/backup \ + alpine \ + tar czf /backup/npm-migration.tar.gz -C / data letsencrypt + +# Copiar a nuevo servidor +scp npm-migration.tar.gz usuario@nuevo-servidor:/tmp/ +``` + +### En el Servidor Nuevo + +```bash +# 1. Clonar repositorio +git clone https://git.ictiberia.com/groales/npm +cd npm + +# 2. Crear volúmenes +docker volume create npm_data +docker volume create npm_letsencrypt + +# 3. Restaurar backup +docker run --rm \ + -v npm_data:/data \ + -v npm_letsencrypt:/letsencrypt \ + -v /tmp:/backup \ + alpine \ + tar xzf /backup/npm-migration.tar.gz -C / + +# 4. Iniciar NPM +docker compose up -d + +# 5. Verificar +docker logs -f nginx-proxy-manager +``` + +## Exportar/Importar Configuración + +### Solo Base de Datos (SQLite) + +```bash +# Exportar solo DB +docker exec nginx-proxy-manager \ + sqlite3 /data/database.sqlite .dump > npm-database-$(date +%Y%m%d).sql + +# Importar DB +cat npm-database-YYYYMMDD.sql | docker exec -i nginx-proxy-manager \ + sqlite3 /data/database.sqlite +``` + +## Reset de Contraseña Admin + +Si olvidaste la contraseña: + +```bash +# Método 1: Resetear a credenciales por defecto +docker compose down + +# Ejecutar container temporal con script reset +docker run --rm \ + -v npm_data:/data \ + jc21/nginx-proxy-manager:latest \ + npx knex migrate:latest --env production && npx knex seed:run --env production + +docker compose up -d + +# Login con: +# Email: admin@example.com +# Password: changeme +``` + +Método 2: SQL directo (solo SQLite): + +```bash +docker exec -it nginx-proxy-manager /bin/bash + +# Dentro del contenedor +sqlite3 /data/database.sqlite + +-- Resetear password a 'changeme' +UPDATE auth SET secret = '$2a$10$YSwA4rB7M/xE8N1n8YCfCuYZ.9cNrwMr/L/PkZ.qXHfEqNkFN7XCy' WHERE id = 1; + +.exit +exit + +# Reiniciar +docker restart nginx-proxy-manager +``` + +## Verificación de Integridad + +### Test de Restauración + +```bash +# 1. Crear volúmenes de test +docker volume create npm_data_test +docker volume create npm_letsencrypt_test + +# 2. Restaurar en volúmenes test +docker run --rm \ + -v npm_data_test:/data \ + -v npm_letsencrypt_test:/letsencrypt \ + -v $(pwd):/backup \ + alpine \ + sh -c "tar xzf /backup/npm_data-BACKUP.tar.gz -C /data && tar xzf /backup/npm_letsencrypt-BACKUP.tar.gz -C /letsencrypt" + +# 3. Iniciar NPM temporal +docker run -d \ + -p 18081:81 \ + --name npm-test \ + -v npm_data_test:/data \ + -v npm_letsencrypt_test:/etc/letsencrypt \ + jc21/nginx-proxy-manager:latest + +# 4. Verificar en http://localhost:18081 + +# 5. Limpiar +docker stop npm-test +docker rm npm-test +docker volume rm npm_data_test npm_letsencrypt_test +``` + +### Validar Tar + +```bash +# Verificar integridad +tar tzf npm_data-YYYYMMDD-HHMMSS.tar.gz > /dev/null +echo $? # Debe devolver 0 + +# Ver contenido +tar tzf npm_data-YYYYMMDD-HHMMSS.tar.gz | head -20 +``` + +## Best Practices + +### Estrategia 3-2-1 + +- ✅ **3 copias** de datos (original + 2 backups) +- ✅ **2 tipos de medios** (local + remoto) +- ✅ **1 copia offsite** (cloud, otro datacenter) + +### Frecuencia Recomendada + +| Uso | Frecuencia | +|-----|------------| +| Producción crítica | Cada 6 horas | +| Producción | Diario | +| Personal/Home | Semanal | + +### Retención + +Ejemplo: +- Diarios: 7 días +- Semanales: 4 semanas +- Mensuales: 12 meses + +### Encriptación + +Para backups sensibles: + +```bash +# Backup encriptado con GPG +BACKUP_FILE="npm-backup-$(date +%Y%m%d-%H%M%S).tar.gz" + +# Crear backup +docker run --rm ... tar czf /backup/$BACKUP_FILE ... + +# Encriptar +gpg --symmetric --cipher-algo AES256 "$BACKUP_FILE" + +# Resultado: npm-backup-YYYYMMDD-HHMMSS.tar.gz.gpg +rm "$BACKUP_FILE" +``` + +Desencriptar: + +```bash +gpg --decrypt npm-backup-YYYYMMDD-HHMMSS.tar.gz.gpg > npm-backup.tar.gz +``` + +--- + +**Volver a**: [Página Principal](Home) | [Configuración Avanzada](Configuracion) diff --git a/Configuracion.md b/Configuracion.md new file mode 100644 index 0000000..ec34d28 --- /dev/null +++ b/Configuracion.md @@ -0,0 +1,341 @@ +# Configuración Avanzada + +## Base de Datos Externa + +### MySQL / MariaDB + +Para mayor rendimiento y escalabilidad, usa MySQL/MariaDB en lugar de SQLite: + +```yaml +services: + app: + image: jc21/nginx-proxy-manager:latest + environment: + TZ: "Europe/Madrid" + DB_MYSQL_HOST: "db" + DB_MYSQL_PORT: 3306 + DB_MYSQL_USER: "npm" + DB_MYSQL_PASSWORD: "npm_password" + DB_MYSQL_NAME: "npm" + depends_on: + - db + + db: + image: jc21/mariadb-aria:latest + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: "root_password" + MYSQL_DATABASE: "npm" + MYSQL_USER: "npm" + MYSQL_PASSWORD: "npm_password" + MARIADB_AUTO_UPGRADE: "1" + volumes: + - mysql_data:/var/lib/mysql + +volumes: + npm_data: + npm_letsencrypt: + mysql_data: +``` + +### PostgreSQL + +```yaml +services: + app: + environment: + DB_POSTGRES_HOST: "db" + DB_POSTGRES_PORT: "5432" + DB_POSTGRES_USER: "npm" + DB_POSTGRES_PASSWORD: "npm_password" + DB_POSTGRES_NAME: "npm" + depends_on: + - db + + db: + image: postgres:17 + environment: + POSTGRES_USER: "npm" + POSTGRES_PASSWORD: "npm_password" + POSTGRES_DB: "npm" + volumes: + - postgres_data:/var/lib/postgresql + +volumes: + npm_data: + npm_letsencrypt: + postgres_data: +``` + +## Docker Secrets + +Para mayor seguridad, usa secrets en lugar de variables de entorno en texto plano: + +```yaml +secrets: + mysql_pwd: + file: ./secrets/mysql_password.txt + +services: + app: + environment: + DB_MYSQL_HOST: "db" + DB_MYSQL_PORT: 3306 + DB_MYSQL_USER: "npm" + DB_MYSQL_PASSWORD__FILE: /run/secrets/mysql_pwd + DB_MYSQL_NAME: "npm" + secrets: + - mysql_pwd +``` + +## Redes Docker + +### Conectar NPM con Otros Servicios + +Crear red compartida: + +```bash +docker network create proxy_network +``` + +NPM: +```yaml +services: + app: + networks: + - proxy_network + - npm_network + +networks: + npm_network: + name: npm_network + proxy_network: + external: true +``` + +Otros servicios: +```yaml +services: + mi_app: + networks: + - proxy_network + +networks: + proxy_network: + external: true +``` + +Configurar Proxy Host: +- **Forward Hostname**: `mi_app` (nombre del contenedor) +- **Forward Port**: Puerto interno (no necesitas publicarlo con `-p`) + +## Configuraciones NGINX Personalizadas + +NPM permite insertar configuración NGINX personalizada en diferentes ubicaciones. + +### Ubicaciones de Archivos Custom + +Crear directorio: `./data/nginx/custom/` (se monta desde volumen `npm_data`) + +Archivos disponibles: +- `root_top.conf` - Top de nginx.conf +- `root.conf` - Final de nginx.conf +- `http_top.conf` - Top del bloque http +- `http.conf` - Final del bloque http +- `events.conf` - Final del bloque events +- `stream.conf` - Final del bloque stream +- `server_proxy.conf` - Final de cada servidor proxy +- `server_redirect.conf` - Final de cada servidor de redirección +- `server_stream.conf` - Final de cada stream +- `server_dead.conf` - Final de cada servidor 404 + +### Ejemplo: Rate Limiting Global + +```bash +# Acceder al contenedor +docker exec -it nginx-proxy-manager /bin/bash + +# Crear archivo custom +cat > /data/nginx/custom/http_top.conf << 'EOF' +# Rate limiting +limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; +limit_req_status 429; +EOF + +# Salir y recargar NGINX +exit +docker exec nginx-proxy-manager nginx -s reload +``` + +Aplicar en Proxy Host (pestaña Advanced): +```nginx +limit_req zone=general burst=20 nodelay; +``` + +### Ejemplo: Módulo GeoIP2 + +Habilitar módulo: + +```bash +docker exec -it nginx-proxy-manager /bin/bash +cat > /data/nginx/custom/root_top.conf << 'EOF' +load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so; +load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so; +EOF +exit +docker exec nginx-proxy-manager nginx -s reload +``` + +## Variables de Entorno Avanzadas + +### Ejecutar como Usuario No-Root + +```yaml +environment: + PUID: 1000 + PGID: 1000 +``` + +⚠️ Puede causar problemas con puertos < 1024. Solución: usar `--cap-add=NET_BIND_SERVICE`. + +### Deshabilitar IPv6 + +```yaml +environment: + DISABLE_IPV6: 'true' +``` + +### Deshabilitar IP Ranges Fetch + +Para entornos con acceso limitado a Internet: + +```yaml +environment: + IP_RANGES_FETCH_ENABLED: 'false' +``` + +### Header X-FRAME-OPTIONS + +```yaml +environment: + X_FRAME_OPTIONS: "sameorigin" # o "deny", "allow-from https://example.com" +``` + +### Usuario Admin Automático + +Saltarse el setup inicial: + +```yaml +environment: + INITIAL_ADMIN_EMAIL: admin@tudominio.com + INITIAL_ADMIN_PASSWORD: tu_password_segura +``` + +⚠️ Cambiar la contraseña inmediatamente después del primer login. + +## Healthcheck + +Añadir healthcheck al servicio: + +```yaml +services: + app: + healthcheck: + test: ["CMD", "/usr/bin/check-health"] + interval: 10s + timeout: 3s + retries: 3 +``` + +## Logrotate Personalizado + +Por defecto NPM rota logs semanalmente (access: 4 archivos, error: 10 archivos). + +Personalizar: + +```bash +# logrotate.custom +/data/logs/*.log { + daily + rotate 7 + missingok + notifempty + compress + delaycompress + sharedscripts + postrotate + docker exec nginx-proxy-manager nginx -s reopen + endscript +} +``` + +Montar en compose: + +```yaml +volumes: + - npm_data:/data + - npm_letsencrypt:/etc/letsencrypt + - ./logrotate.custom:/etc/logrotate.d/nginx-proxy-manager +``` + +## Migración desde Otra Instancia + +### Exportar desde origen + +```bash +# Backup volúmenes +docker run --rm \ + -v npm_data:/data \ + -v $(pwd):/backup \ + alpine tar czf /backup/npm_data_backup.tar.gz -C /data . + +docker run --rm \ + -v npm_letsencrypt:/letsencrypt \ + -v $(pwd):/backup \ + alpine tar czf /backup/npm_letsencrypt_backup.tar.gz -C /letsencrypt . +``` + +### Importar en destino + +```bash +# Crear volúmenes +docker volume create npm_data +docker volume create npm_letsencrypt + +# Restaurar +docker run --rm \ + -v npm_data:/data \ + -v $(pwd):/backup \ + alpine tar xzf /backup/npm_data_backup.tar.gz -C /data + +docker run --rm \ + -v npm_letsencrypt:/letsencrypt \ + -v $(pwd):/backup \ + alpine tar xzf /backup/npm_letsencrypt_backup.tar.gz -C /letsencrypt + +# Levantar NPM +docker compose up -d +``` + +## Arquitecturas Soportadas + +NPM soporta múltiples arquitecturas (manifest multi-arch): + +- `amd64` - Intel/AMD 64-bit +- `arm64` - ARM 64-bit (Raspberry Pi 4, Apple Silicon) +- `armv7` - ARM 32-bit (Raspberry Pi 3) + +No necesitas hacer nada especial, Docker pull descarga la imagen correcta automáticamente. + +### Raspberry Pi Consideraciones + +Para MariaDB en ARM, usar imagen alternativa: + +```yaml +db: + image: yobasystems/alpine-mariadb:latest # en lugar de jc21/mariadb-aria +``` + +--- + +**Volver a**: [Página Principal](Home) | [Certificados SSL](SSL) diff --git a/Home.md b/Home.md new file mode 100644 index 0000000..60b155c --- /dev/null +++ b/Home.md @@ -0,0 +1,244 @@ +# NGINX Proxy Manager - Documentación + +Bienvenido a la documentación del despliegue de **NGINX Proxy Manager** usando Docker Compose. + +## ¿Qué es NGINX Proxy Manager? + +NGINX Proxy Manager (NPM) es una interfaz de gestión intuitiva para configurar y administrar proxy inversos NGINX sin necesidad de conocimientos profundos de NGINX. Ideal para: + +- 🏠 **Self-hosting casero** - Exponer servicios locales a Internet de forma segura +- 🔒 **Certificados SSL automáticos** - Let's Encrypt integrado con renovación automática +- 🌐 **Múltiples dominios** - Gestionar decenas o cientos de proxy hosts desde una UI +- 📱 **Interfaz moderna** - Administración web responsive y fácil de usar +- 🔐 **Control de acceso** - Listas de acceso y autenticación básica HTTP + +## Características Principales + +### Gestión de Proxy Hosts + +- **Proxy Hosts**: Redirigir dominios a servicios backend (HTTP/HTTPS) +- **Redirection Hosts**: Redirecciones 301/302 permanentes o temporales +- **404 Hosts**: Páginas personalizadas para dominios no configurados +- **Streams**: Proxy TCP/UDP para servicios no-HTTP (SSH, bases de datos, etc.) + +### SSL/TLS + +- ✅ **Let's Encrypt gratuito** con renovación automática +- ✅ **Certificados personalizados** (upload de cert + key) +- ✅ **Wildcard certificates** vía DNS challenge +- ✅ **HTTP/2 y HTTP/3** (QUIC) support +- ✅ **Force SSL** (redirección automática HTTP → HTTPS) +- ✅ **HSTS** (HTTP Strict Transport Security) + +### Seguridad + +- 🔐 **Access Lists** - Restricción por IP, username/password +- 🛡️ **Headers personalizados** - X-Frame-Options, CSP, CORS +- 🚫 **Bloqueo de exploits comunes** - Configuración NGINX hardening +- 🔑 **Gestión de usuarios** - Multi-usuario con roles y permisos + +## Componentes del Stack + +Este repositorio despliega NPM con la siguiente configuración: + +### Servicio Principal +- **Imagen**: `jc21/nginx-proxy-manager:latest` +- **Contenedor**: `nginx-proxy-manager` +- **Reinicio**: `unless-stopped` + +### Puertos Expuestos +- **80**: HTTP público (redirige automáticamente a HTTPS) +- **81**: Interfaz web de administración +- **443**: HTTPS público + +### Volúmenes +- `npm_data` → `/data` - Base de datos SQLite, configuraciones, logs +- `npm_letsencrypt` → `/etc/letsencrypt` - Certificados SSL de Let's Encrypt + +### Red +- `npm_network` - Red dedicada para el stack + +## Requisitos Previos + +Antes de desplegar NPM, asegúrate de tener: + +- ✅ Docker Engine y Docker Compose instalados +- ✅ Puertos 80, 443 y 81 disponibles (o configurar puertos alternativos) +- ✅ Dominio(s) apuntando al servidor (para certificados SSL) +- ✅ Acceso a Internet (para validación de Let's Encrypt) +- ✅ Firewall configurado para permitir tráfico en puertos 80/443 + +## Despliegue Rápido + +### 1. Clonar el Repositorio + +```bash +git clone https://git.ictiberia.com/groales/npm +cd npm +``` + +### 2. Levantar el Stack + +```bash +docker compose up -d +``` + +### 3. Verificar el Estado + +```bash +docker ps --filter name=nginx-proxy-manager +docker logs nginx-proxy-manager +``` + +### 4. Acceder a la Interfaz Web + +Abre tu navegador en: **http://IP-del-servidor:81** + +⏱️ **Primera vez**: El inicio puede tardar 1-2 minutos. Espera a ver en los logs: +``` +[setup ] Starting backend +``` + +### 5. Login Inicial + +**Credenciales por defecto**: +``` +Email: admin@example.com +Password: changeme +``` + +🔒 **Obligatorio**: Al primer login, se te pedirá: +1. Cambiar el email +2. Cambiar el nombre de usuario +3. Establecer una nueva contraseña + +## Primer Proxy Host (Guía Rápida) + +### Ejemplo: Exponer Portainer con SSL + +Supongamos que tienes Portainer corriendo en `192.168.1.100:9443` y quieres acceder via `https://portainer.tudominio.com`. + +1. **Ir a "Hosts" → "Proxy Hosts" → "Add Proxy Host"** + +2. **Pestaña "Details"**: + - **Domain Names**: `portainer.tudominio.com` + - **Scheme**: `https` (Portainer usa HTTPS) + - **Forward Hostname / IP**: `192.168.1.100` + - **Forward Port**: `9443` + - **Cache Assets**: ✅ (opcional) + - **Block Common Exploits**: ✅ + - **Websockets Support**: ✅ (necesario para Portainer) + +3. **Pestaña "SSL"**: + - ✅ **Request a new SSL Certificate with Let's Encrypt** + - ✅ **Force SSL** (redirigir HTTP → HTTPS) + - ✅ **HTTP/2 Support** + - ✅ **HSTS Enabled** (opcional, recomendado) + - Email: `tu@email.com` (para notificaciones de Let's Encrypt) + - ✅ **I Agree to the Let's Encrypt Terms of Service** + +4. **Save** + +✅ En 10-30 segundos, el certificado SSL se generará y el proxy estará activo. + +Accede a: `https://portainer.tudominio.com` 🎉 + +## Casos de Uso Comunes + +### 1. Proxy a Contenedores Docker en la Misma Red + +Si tienes servicios corriendo en Docker en el mismo host: + +```yaml +# En el docker-compose.yaml de tu servicio +networks: + - npm_network + +networks: + npm_network: + external: true +``` + +Luego en NPM: +- **Forward Hostname**: Nombre del contenedor (ej: `portainer`) +- **Forward Port**: Puerto interno del contenedor (no el publicado) + +### 2. Redirección Permanente + +**Ejemplo**: Redirigir `www.tudominio.com` → `tudominio.com` + +1. **"Hosts" → "Redirection Hosts" → "Add Redirection Host"** +2. **Domain Names**: `www.tudominio.com` +3. **Scheme**: `https` +4. **Forward Domain Name**: `tudominio.com` +5. **Preserve Path**: ✅ +6. **HTTP Code**: `301` (permanente) +7. **SSL**: Solicitar certificado para `www.tudominio.com` + +### 3. Stream TCP (Ejemplo: SSH) + +**Objetivo**: Acceder a SSH del servidor via puerto 2222 externo. + +1. **"Streams" → "Add Stream"** +2. **Incoming Port**: `2222` +3. **Forwarding Host**: `127.0.0.1` (localhost) +4. **Forwarding Port**: `22` (SSH) +5. **TCP Forwarding**: ✅ +6. **Save** + +Añadir puerto al `docker-compose.yaml`: + +```yaml +ports: + - "80:80" + - "81:81" + - "443:443" + - "2222:2222" # Stream SSH +``` + +Reiniciar: `docker compose up -d` + +Conectar: `ssh usuario@IP-servidor -p 2222` + +### 4. Access List (Restringir por IP) + +1. **"Access Lists" → "Add Access List"** +2. **Name**: `Solo oficina` +3. **Pass Auth**: ✅ (si quieres autenticación adicional) +4. **Authorization** → **Add** → **Username**: `admin`, **Password**: `***` +5. **Access** → **Allow** → `192.168.1.0/24` (red local) +6. **Access** → **Deny** → `0.0.0.0/0` (todo lo demás) +7. **Save** + +Aplicar a Proxy Host: +- Editar Proxy Host → **Access List**: Seleccionar "Solo oficina" + +## Próximos Pasos + +- 📖 [Configuración Avanzada](Configuracion) - MySQL, secrets, custom NGINX configs +- 🔒 [Certificados SSL/TLS](SSL) - Wildcard, DNS challenge, certificados propios +- 💾 [Backup y Restauración](Backup) - Cómo respaldar y restaurar NPM +- ⚙️ [Configuración Avanzada de NGINX](Avanzado) - Custom configs, geoIP, rate limiting + +## Troubleshooting Rápido + +| Problema | Solución | +|----------|----------| +| Puerto 80/443 ocupado | Ver proceso: `sudo netstat -tulpn \| grep :80`, cambiar puertos en compose | +| No se genera certificado SSL | Verificar DNS apunta al servidor, puertos 80/443 abiertos en firewall | +| Olvido de contraseña | Ver [Backup](Backup#reset-password) para resetear | +| Error "Address family not supported" | Añadir `DISABLE_IPV6: 'true'` en environment | +| 502 Bad Gateway | Verificar que el backend esté corriendo y el puerto correcto | + +## Recursos Oficiales + +- 📘 [Documentación Oficial](https://nginxproxymanager.com/) +- 🐛 [GitHub Issues](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) +- 💬 [Reddit r/nginxproxymanager](https://reddit.com/r/nginxproxymanager) +- 🐳 [Docker Hub](https://hub.docker.com/r/jc21/nginx-proxy-manager) +- 📺 [Video Tutoriales](https://www.youtube.com/results?search_query=nginx+proxy+manager) + +--- + +**Versión**: Latest (rolling release) +**Última actualización**: Noviembre 2025 diff --git a/SSL.md b/SSL.md new file mode 100644 index 0000000..3d8ab3f --- /dev/null +++ b/SSL.md @@ -0,0 +1,344 @@ +# Certificados SSL/TLS + +## Let's Encrypt (Recomendado) + +NPM integra Let's Encrypt para certificados SSL gratuitos y automáticos. + +### Requisitos + +- ✅ Dominio apuntando al servidor (registro A/AAAA en DNS) +- ✅ Puertos 80 y 443 accesibles desde Internet +- ✅ Firewall configurado para permitir tráfico entrante + +### Solicitar Certificado (HTTP Challenge) + +1. **Crear/Editar Proxy Host** +2. **Pestaña SSL**: + - ✅ **Request a new SSL Certificate with Let's Encrypt** + - Email: `tu@email.com` (para notificaciones de expiración) + - ✅ **Force SSL** (redirigir HTTP → HTTPS automáticamente) + - ✅ **HTTP/2 Support** + - ✅ **HSTS Enabled** (opcional, recomendado para seguridad) + - ✅ **I Agree to the Let's Encrypt Terms of Service** +3. **Save** + +✅ El certificado se generará en 10-30 segundos. + +### Renovación Automática + +NPM verifica certificados diariamente y renueva automáticamente los que están por expirar (< 30 días). + +No requiere configuración adicional. + +### Certificados Wildcard (DNS Challenge) + +Para certificados wildcard (`*.tudominio.com`) se requiere DNS challenge. + +#### Proveedores DNS Soportados + +NPM soporta +100 proveedores via Certbot plugins. Los más comunes: + +- Cloudflare +- Amazon Route53 +- Google Cloud DNS +- DigitalOcean +- OVH +- Namecheap +- GoDaddy +- etc. + +#### Configurar DNS Challenge + +**Ejemplo: Cloudflare** + +1. **Obtener API Token de Cloudflare**: + - Login en Cloudflare Dashboard + - Profile → API Tokens → Create Token + - Template: "Edit zone DNS" + - Zone Resources: Include → Specific zone → `tudominio.com` + - Continue → Create Token + - Copiar token + +2. **En NPM: SSL Certificates → Add SSL Certificate**: + - **Domain Names**: `*.tudominio.com`, `tudominio.com` (ambos) + - ✅ **Use a DNS Challenge** + - **DNS Provider**: `Cloudflare` + - **Credentials File Content**: + ``` + dns_cloudflare_api_token = TU_TOKEN_AQUI + ``` + - Email: `tu@email.com` + - ✅ **I Agree to the Let's Encrypt Terms of Service** + - **Save** + +3. **Usar el certificado**: + - Al crear Proxy Hosts, en pestaña SSL: + - Seleccionar certificado wildcard existente + - No marcar "Request a new SSL Certificate" + +#### Otros Proveedores DNS + +**Amazon Route53**: +``` +dns_route53_access_key_id = YOUR_ACCESS_KEY +dns_route53_secret_access_key = YOUR_SECRET_KEY +``` + +**Google Cloud DNS**: +``` +dns_google_credentials = /path/to/credentials.json +``` + +**DigitalOcean**: +``` +dns_digitalocean_token = YOUR_TOKEN +``` + +**OVH**: +``` +dns_ovh_endpoint = ovh-eu +dns_ovh_application_key = YOUR_APP_KEY +dns_ovh_application_secret = YOUR_APP_SECRET +dns_ovh_consumer_key = YOUR_CONSUMER_KEY +``` + +Consulta [Certbot DNS plugins](https://eff-certbot.readthedocs.io/en/stable/using.html#dns-plugins) para más proveedores. + +## Certificados Propios + +### Subir Certificado y Clave Privada + +1. **SSL Certificates → Add SSL Certificate** +2. **Custom**: + - **Name**: Nombre descriptivo (ej: "MiEmpresa Wildcard") + - **Certificate Key**: Pegar contenido del archivo `.key` + - **Certificate**: Pegar contenido del archivo `.crt` o `.pem` + - **Intermediate Certificate**: (opcional) Cadena de certificados intermedios + - **Save** + +3. **Usar en Proxy Host**: + - Pestaña SSL → Seleccionar certificado custom + - ✅ **Force SSL** + - ✅ **HTTP/2 Support** + +### Formato de Certificados + +#### Certificado (Certificate) + +``` +-----BEGIN CERTIFICATE----- +MIIFXzCCBEegAwIBAgISBGP... +... (contenido base64) ... +-----END CERTIFICATE----- +``` + +#### Clave Privada (Certificate Key) + +``` +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQ... +... (contenido base64) ... +-----END PRIVATE KEY----- +``` + +#### Certificado Intermedio (Intermediate Certificate) + +Si tu CA proporciona cadena de certificados: + +``` +-----BEGIN CERTIFICATE----- +(Certificado Intermedio 1) +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +(Certificado Intermedio 2) +-----END CERTIFICATE----- +``` + +### Convertir Formatos + +**PFX/P12 a PEM**: +```bash +# Extraer clave privada +openssl pkcs12 -in certificate.pfx -nocerts -out key.pem -nodes + +# Extraer certificado +openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out cert.pem +``` + +**DER a PEM**: +```bash +openssl x509 -inform der -in certificate.cer -out certificate.pem +``` + +## HTTP/2 y HTTP/3 + +### HTTP/2 + +Habilitado por defecto al activar SSL en Proxy Host. + +✅ **HTTP/2 Support** en pestaña SSL. + +### HTTP/3 (QUIC) + +NPM soporta HTTP/3 pero requiere configuración adicional: + +```bash +docker exec -it nginx-proxy-manager /bin/bash + +cat > /data/nginx/custom/server_proxy.conf << 'EOF' +# HTTP/3 +listen 443 quic reuseport; +add_header Alt-Svc 'h3=":443"; ma=86400'; +EOF + +exit +docker exec nginx-proxy-manager nginx -s reload +``` + +Abrir puerto UDP 443: + +```yaml +ports: + - "80:80" + - "81:81" + - "443:443" + - "443:443/udp" # HTTP/3 +``` + +## HSTS (HTTP Strict Transport Security) + +HSTS fuerza a los navegadores a usar HTTPS siempre. + +### Habilitar en Proxy Host + +Pestaña SSL: +- ✅ **HSTS Enabled** +- **HSTS Subdomains** (opcional, incluye subdominios) + +Esto añade el header: +``` +Strict-Transport-Security: max-age=31536000; includeSubDomains +``` + +### HSTS Preload + +Para máxima seguridad, añadir tu dominio al [HSTS Preload List](https://hstspreload.org/). + +Configuración custom: + +```nginx +# En Proxy Host → Advanced → Custom Nginx Configuration +add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; +``` + +Luego enviar dominio a hstspreload.org. + +## Configuración Avanzada de SSL + +### Cipher Suites Personalizados + +Para mayor compatibilidad o seguridad: + +```nginx +# En /data/nginx/custom/server_proxy.conf +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; +ssl_prefer_server_ciphers off; +``` + +### OCSP Stapling + +Mejora rendimiento SSL: + +```nginx +ssl_stapling on; +ssl_stapling_verify on; +ssl_trusted_certificate /etc/letsencrypt/live/tudominio.com/chain.pem; +resolver 1.1.1.1 8.8.8.8 valid=300s; +resolver_timeout 5s; +``` + +### Session Cache + +Reducir overhead de handshakes SSL: + +```nginx +ssl_session_cache shared:SSL:10m; +ssl_session_timeout 10m; +``` + +## Troubleshooting + +### Error: DNS Challenge Failed + +**Síntomas**: Certificado wildcard no se genera. + +**Causas**: +- API token/credenciales incorrectas +- Permisos insuficientes del token +- Propagación DNS lenta + +**Soluciones**: +```bash +# Ver logs detallados +docker logs nginx-proxy-manager | grep certbot + +# Verificar permisos de token (Cloudflare) +# Token debe tener: Zone:DNS:Edit + +# Esperar propagación DNS (puede tardar minutos) +``` + +### Error: Rate Limit Exceeded + +Let's Encrypt tiene límites: +- **5 certificados por dominio/semana** +- **50 certificados por cuenta/semana** + +**Solución**: +- Esperar 7 días para reset +- Usar certificados wildcard (cubre múltiples subdominios con 1 cert) +- Durante desarrollo, usar [staging environment](https://letsencrypt.org/docs/staging-environment/) + +### Certificado No Se Renueva + +**Verificar**: +```bash +# Ver logs de renovación +docker logs nginx-proxy-manager | grep renew + +# Listar certificados +docker exec nginx-proxy-manager certbot certificates +``` + +**Forzar renovación manual**: +```bash +docker exec nginx-proxy-manager certbot renew --force-renewal +docker exec nginx-proxy-manager nginx -s reload +``` + +### Error: Port 80 Not Available + +Let's Encrypt HTTP challenge requiere puerto 80. + +**Si puerto 80 no está disponible**: +1. Usa DNS challenge (wildcard) +2. O libera puerto 80 temporalmente + +### Verificar Certificado SSL + +**Desde línea de comandos**: +```bash +# Ver detalles del certificado +echo | openssl s_client -servername tudominio.com -connect tudominio.com:443 2>/dev/null | openssl x509 -noout -dates + +# Test desde exterior +curl -vI https://tudominio.com 2>&1 | grep -i ssl + +# SSL Labs test (rating A-F) +# https://www.ssllabs.com/ssltest/analyze.html?d=tudominio.com +``` + +--- + +**Volver a**: [Página Principal](Home) | [Configuración Avanzada](Configuracion)