Compare commits

...

4 Commits

Author SHA1 Message Date
7a496b235c STABLE: Reconstrucción integral de infraestructura, limpieza de middlewares obsoletos y blindaje de Traefik 2026-01-06 00:57:00 +01:00
3c5976e37f STABLE: Reconstrucción integral de Traefik v3 y Portainer con seguridad reforzada y documentación 2026-01-06 00:55:11 +01:00
4ce8786056 feat(security): añadir whitelist independiente y estructurar middlewares
- Incluye access-control.yml, auth.yml y security-headers.yml
- Añadida whitelist.yml (configuración independiente)
2026-01-05 22:33:13 +01:00
7babf81811 Añadido contenedor de Tailscale para acceso VPN seguro 2026-01-05 21:43:18 +01:00
8 changed files with 138 additions and 71 deletions

View File

@@ -1,8 +1,12 @@
# ============================================================================== # ==============================================================================
# GITEA - CONFIGURACIÓN DE PRODUCCIÓN PARA SYNOLOGY DSM # GITEA - CONFIGURACIÓN DE PRODUCCIÓN (REVISIÓN DE SEGURIDAD)
# ============================================================================== # ==============================================================================
# Este servicio corre bajo el UID 1032 y GID 100 gestionado internamente por s6. # - Identidad: Ejecución bajo UID 1032 / GID 100 (Estándar OgnirNAS).
# Se conecta a Traefik v3 a través de la red interna de servicios. # - Red: Conectado a 'services-internal-net' para aislamiento Bridge.
# - Traefik v3:
# * Se utiliza 'security-headers@file' (Middleware dinámico actualizado).
# * Vinculación explícita router-servicio para evitar estados 'disabled'.
# * Resolución de certificados vía Let's Encrypt.
# ============================================================================== # ==============================================================================
version: "3.9" version: "3.9"
@@ -13,29 +17,35 @@ services:
container_name: gitea container_name: gitea
restart: always restart: always
networks: networks:
- services-internal-net # Red compartida con el Proxy - services-internal-net
volumes: volumes:
- /volume1/docker/data/gitea:/data # Persistencia de datos, SSH y DB - /volume1/docker/data/gitea:/data
environment: environment:
# IDs de usuario confirmados para evitar conflictos de permisos en el NAS
- USER_UID=1032 - USER_UID=1032
- USER_GID=100 - USER_GID=100
- TZ=Europe/Madrid - TZ=Europe/Madrid
# URL externa para evitar el redireccionamiento a localhost:3000
- GITEA__server__ROOT_URL=https://gitea.ognir-server.synology.me/ - GITEA__server__ROOT_URL=https://gitea.ognir-server.synology.me/
- GITEA__database__DB_TYPE=sqlite3 - GITEA__database__DB_TYPE=sqlite3
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
# Enrutamiento mediante Host
# --- Configuración del Router ---
- "traefik.http.routers.gitea.rule=Host(`gitea.ognir-server.synology.me`)" - "traefik.http.routers.gitea.rule=Host(`gitea.ognir-server.synology.me`)"
- "traefik.http.routers.gitea.entrypoints=websecure" - "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls=true" - "traefik.http.routers.gitea.tls=true"
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt" - "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
# Forzamos a Traefik a usar la red interna para evitar errores de gateway
- "traefik.docker.network=services-internal-net" # --- Vinculación Router-Service ---
# Define el destino explícito para habilitar el router en el Dashboard.
- "traefik.http.routers.gitea.service=gitea"
# --- Configuración del Servicio (Backend) ---
- "traefik.http.services.gitea.loadbalancer.server.port=3000" - "traefik.http.services.gitea.loadbalancer.server.port=3000"
# Uso del middleware de seguridad definido en el proveedor de archivos (@file) - "traefik.docker.network=services-internal-net"
- "traefik.http.routers.gitea.middlewares=seguridad-general@file"
# --- Middlewares ---
# Se actualiza de 'seguridad-general' a 'security-headers' según el inventario dinámico.
- "traefik.http.routers.gitea.middlewares=security-headers@file"
networks: networks:
services-internal-net: services-internal-net:

View File

@@ -1,41 +1,38 @@
# ============================================================================== # ==============================================================================
# PORTAINER CE - CONFIGURACIÓN SEGURA PARA OGNIRNAS # OGNIRNAS - PORTAINER CE (GESTIÓN DE CONTENEDORES)
# ============================================================================== # ==============================================================================
# - Usuario: 1032 (docker-manager) # Última revisión: 2026-01-06
# - Acceso Socket: GID 65538 (Synology Docker Group) # Propietario: Ognir (UID 1032 / GID 100)
# - Red: services-internal-net # NOTA: Sin Auth de Traefik (usa su propio login interno).
# - Middleware: seguridad-general@file
# ============================================================================== # ==============================================================================
version: '3.8' version: "3.9"
services: services:
portainer: portainer:
image: portainer/portainer-ce:latest image: portainer/portainer-ce:latest
container_name: portainer container_name: portainer
restart: always restart: always
user: "1032:100" security_opt:
group_add: - no-new-privileges:true
- "65538" # Permite al usuario 1032 leer el socket de root
networks: networks:
- services-internal-net services-internal-net: {}
# Puertos de emergencia (puedes comentarlos si solo usas Traefik)
ports:
- "8000:8000"
- "9443:9443"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /etc/localtime:/etc/localtime:ro
- /volume1/docker/data/portainer:/data - /var/run/docker.sock:/var/run/docker.sock:ro # Conexión al socket para gestión
- /volume1/docker/data/portainer:/data # Datos persistentes
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.ognir-server.synology.me`)" - "traefik.http.routers.portainer.rule=Host(`portainer.ognir-server.synology.me`)"
- "traefik.http.routers.portainer.entrypoints=websecure" - "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls=true" - "traefik.http.routers.portainer.tls=true"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt" - "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.docker.network=services-internal-net"
- "traefik.http.services.portainer.loadbalancer.server.port=9000" - "traefik.http.services.portainer.loadbalancer.server.port=9000"
# Importante: El middleware que definimos en la config dinámica # --- MIDDLEWARES (SOLO CABECERAS, SIN AUTH) ---
- "traefik.http.routers.portainer.middlewares=seguridad-general@file" - "traefik.http.routers.portainer.middlewares=security-headers@file"
networks: networks:
services-internal-net: services-internal-net:

23
tailscale/docker-compose.yml Executable file
View File

@@ -0,0 +1,23 @@
# ==============================================================================
# TAILSCALE - NODO DE RED DE GESTIÓN (REVISADO)
# ==============================================================================
version: "3.9"
services:
tailscale:
image: tailscale/tailscale:latest
container_name: tailscale
hostname: ognir-nas-tailnet
restart: always
# Usamos network host para integrarnos en la pila de red del NAS
network_mode: host
privileged: true # Esto otorga acceso al TUN del kernel sin mapeos manuales
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /volume1/docker/data/tailscale:/var/lib/tailscale
# Eliminamos el mapeo de /dev/net/tun que daba error
environment:
- TS_USERSPACE=false
- TS_STATE_DIR=/var/lib/tailscale

View File

@@ -1,9 +1,18 @@
# ============================================================================== # ==============================================================================
# TRAEFIK V3 - INFRAESTRUCTURA COMPLETA (OGNIRNAS) # OGNIRNAS - INFRAESTRUCTURA CORE: TRAEFIK V3 & DOCKER-SOCKET-PROXY
# ============================================================================== # ==============================================================================
# Última revisión: 2026-01-06
# Propietario: Ognir (UID 1032 / GID 100)
# Objetivo: Reverse Proxy con autenticación reforzada y aislamiento del socket.
# ==============================================================================
version: "3.9" version: "3.9"
services: services:
# ----------------------------------------------------------------------------
# SERVICE: traefik-socket-proxy
# Capa de seguridad que evita el acceso directo de Traefik al socket de Docker.
# ----------------------------------------------------------------------------
traefik-socket-proxy: traefik-socket-proxy:
image: tecnativa/docker-socket-proxy:latest image: tecnativa/docker-socket-proxy:latest
container_name: traefik-socket-proxy container_name: traefik-socket-proxy
@@ -18,45 +27,54 @@ services:
- SERVICES=1 - SERVICES=1
- VERSION=1 - VERSION=1
- EVENTS=1 - EVENTS=1
- CONNECT_TIMEOUT=30
- SERVER_TIMEOUT=30
- CLIENT_TIMEOUT=30
# ----------------------------------------------------------------------------
# SERVICE: traefik (v3.0)
# Orquestador principal. Gestiona TLS y Dashboard seguro.
# ----------------------------------------------------------------------------
traefik: traefik:
image: traefik:v3.0 image: traefik:v3.0
container_name: traefik container_name: traefik
restart: always restart: always
user: "1032:100" user: "1032:100" # Ejecución bajo el usuario Ognir para permisos de archivos
depends_on: depends_on:
traefik-socket-proxy: traefik-socket-proxy:
condition: service_started condition: service_started
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-dash.rule=Host(`traefik.ognir-server.synology.me`)"
- "traefik.http.routers.traefik-dash.entrypoints=websecure"
- "traefik.http.routers.traefik-dash.tls=true"
- "traefik.http.routers.traefik-dash.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik-dash.service=api@internal"
# Mantenemos el middleware pero asegúrate de limpiar caché del navegador
- "traefik.http.routers.traefik-dash.middlewares=seguridad-general@file"
- "traefik.docker.network=services-internal-net"
networks: networks:
proxy-macvlan-net: proxy-macvlan-net:
ipv4_address: 192.168.178.25 ipv4_address: 192.168.178.25 # IP estática en red local
services-internal-net: services-internal-net: {}
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8080/ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
volumes: volumes:
- /volume1/docker/configs/traefik:/etc/traefik:ro - /volume1/docker/configs/traefik:/etc/traefik:ro
- /volume1/docker/data/traefik:/letsencrypt - /volume1/docker/data/traefik:/letsencrypt
- /volume1/docker/data/traefik/logs:/var/log/traefik - /volume1/docker/data/traefik/logs:/var/log/traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=services-internal-net"
# --- CONFIGURACIÓN DEL ROUTER (DASHBOARD) ---
- "traefik.http.routers.traefik-dash.rule=Host(`traefik.ognir-server.synology.me`)"
- "traefik.http.routers.traefik-dash.entrypoints=websecure"
- "traefik.http.routers.traefik-dash.tls=true"
- "traefik.http.routers.traefik-dash.tls.certresolver=letsencrypt"
# --- SERVICIO INTERNO (API V3) ---
- "traefik.http.routers.traefik-dash.service=api@internal"
# --- MIDDLEWARES (ORDEN PRIORITARIO: LOGIN > SEGURIDAD) ---
# Colocamos auth-dashboard primero para asegurar que el prompt salte antes
# de aplicar las cabeceras de seguridad que podrían bloquearlo en caché.
- "traefik.http.routers.traefik-dash.middlewares=auth-dashboard@file,security-headers@file"
command: command:
- "--configFile=/etc/traefik/traefik.yml" - "--configFile=/etc/traefik/traefik.yml"
# ------------------------------------------------------------------------------
# REDES EXTERNAS PRE-EXISTENTES
# ------------------------------------------------------------------------------
networks: networks:
proxy-macvlan-net: proxy-macvlan-net:
external: true external: true

9
traefik/dynamic/auth.yml Executable file
View File

@@ -0,0 +1,9 @@
# ==============================================================================
# AUTENTICACIÓN PARA DASHBOARD Y SERVICIOS CRÍTICOS
# ==============================================================================
http:
middlewares:
auth-dashboard:
basicAuth:
users:
- "Ognir:$apr1$0.TPOwbF$mHoZOQE2xcdWDBN4VhcBe/"

View File

@@ -1,17 +0,0 @@
# ==============================================================================
# MIDDLEWARES DE SEGURIDAD (CONFIGURACIÓN DINÁMICA)
# ==============================================================================
http:
middlewares:
seguridad-general:
headers:
# Cabeceras de seguridad recomendadas (HSTS, XSS, etc.)
forceSTSHeader: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
contentTypeNosniff: true
browserXssFilter: true
frameDeny: true # Evita que tu sitio sea cargado en un iframe (protección clickjacking)
referrerPolicy: "same-origin"

View File

@@ -0,0 +1,15 @@
# ################################################################# #
# MIDDLEWARE: SECURITY-HEADERS #
# ################################################################# #
http:
middlewares:
security-headers:
headers:
forceSTSHeader: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
contentTypeNosniff: true
browserXssFilter: true
frameDeny: true
referrerPolicy: "same-origin"

12
traefik/dynamic/whitelist.yml Executable file
View File

@@ -0,0 +1,12 @@
# ################################################################# #
# MIDDLEWARE DE WHITELIST - ACCESO RESTRINGIDO OGNIRNAS #
# ################################################################# #
http:
middlewares:
muro-seguro:
ipAllowList:
sourceRange:
- "127.0.0.1/32" # Localhost (interno)
- "192.168.178.0/24" # Tu red local de casa
- "100.64.0.0/10" # Todo el rango de Tailscale
- "172.16.0.0/12" # Redes internas de Docker