El día 1 convierte la base Debian en una plataforma completa: OpenCloud sobre Docker Compose con overlays oficiales, Nginx terminando TLS y encaminando sólo al loopback, políticas de firewall coherentes y la landing Astro del proyecto publicada como segundo backend detrás del mismo frontal.
También se introducen mejoras posteriores al primer corte — Fail2ban y el subdominio dedicado del cloud — porque forman parte del relato operativo real del despliegue.
OpenCloud “core” sin Collabora/WOPI
El modo elegido arranca OpenCloud como servicio único desde la composición oficial (opencloud-eu/opencloud-compose), usando una imagen rodante etiquetada (opencloud-rolling con tag fijado en despliegue) para poder actualizar de forma deliberada (docker compose pull + ventana de mantenimiento).
- Overlay
external-proxy/opencloud.yml: adapta variables comoPROXY_HTTP_ADDRpara escuchar dentro del contenedor y publicar el puerto HTTP del proxy sólo como127.0.0.1:<puerto-app>en el host. COMPOSE_PROJECT_NAME=opencloud: ancla los nombres de volumen Docker (opencloud_opencloud-data,opencloud_opencloud-config) sin depender del cwd.- Fichero
.env: fuente única de variables de despliegue; permisos estrictos en disco y fuera del control de versiones. COMPOSE_FILE: lista los overlays necesarios (docker-compose.ymlbase más el overlay de proxy externo).
Dentro del contenedor coexisten microservicios que conversan por gRPC/HTTP en localhost interno del propio contenedor; ese rango no se expone directamente al host salvo por los endpoints previstos por el chart upstream.
Diagrama detallado OpenCloud tras el proxy
Browser │ HTTPS :443 ▼ Nginx (Debian, site dedicado OpenCloud) │ HTTP http://127.0.0.1:9200 (sólo loopback — no sale del host) ▼ Contenedor OpenCloud (usuario UID/GID fijo en despliegue) │ microservicios internos en 127.0.0.1 (rango ~9140–9300 dentro del contenedor) ▼ Volúmenes Docker: • opencloud-data → ficheros de usuarios, índices, NATS, IDM... • opencloud-config → opencloud.yaml autogenerado, CSP, listas de política...
PROXY_TLS=false indica que la terminación TLS ocurre fuera del contenedor (en Nginx). OpenCloud genera URLs coherentes cuando recibe cabeceras X-Forwarded-* correctas.
Mapa de puertos y superficie expuesta
| Puerto | Listener | Alcance | Rol |
|---|---|---|---|
| 22 | sshd | Internet (según política) | Administración SSH. |
| 80 | Nginx | Internet | HTTP para redirección a HTTPS y retos ACME. |
| 443 | Nginx | Internet | TLS + virtual hosts (KM0 + OpenCloud). |
| 9200 | Docker → OpenCloud proxy | Sólo 127.0.0.1 | Backend HTTP que ve Nginx. |
| 9140–9300 | Microservicios OpenCloud | Interior del contenedor | gRPC/HTTP internos — no publicados en el host. |
UFW refuerza la política permitiendo desde Internet sólo lo necesario (SSH + HTTP/S típicamente). La regla mental es: si no debe conocerlo el navegador externo, no escucha en todas las interfaces.
Nginx ↔ OpenCloud: por qué importan ciertas directivas
| Directiva | Valor típico | Motivo |
|---|---|---|
proxy_buffering | off | SSE (Server-Sent Events) para actualizaciones en tiempo real del cliente web. |
proxy_request_buffering | off | Subidas resumibles TUS — hay que streamear sin bufferizar todo el cuerpo. |
proxy_pass | http://127.0.0.1:9200 | HTTP local; TLS ya resuelto en el borde. |
X-Forwarded-Proto | $scheme | OpenCloud genera redirects/cookies coherentes para HTTPS. |
Upgrade / Connection | passthrough | WebSockets para componentes interactivos del UI. |
proxy_read_timeout / proxy_send_timeout | 3600s | Sesiones largas de sincronización y subidas grandes. |
client_max_body_size | 10G | Tope útil para ficheros grandes vía cliente. |
http2 | on | Multiplexación HTTP/2 en Nginx moderno. |
Árbol de despliegue en /opt/opencloud
/opt/opencloud/
├── opencloud-compose/ # clon upstream opencloud-eu/opencloud-compose (git pull para overlays base)
│ ├── docker-compose.yml
│ ├── external-proxy/opencloud.yml # overlay loopback + PROXY_HTTP_ADDR
│ ├── .env # activo — fuera de git, chmod 600
│ └── .env.debian-core-external-proxy.example
├── nginx/
│ ├── sites-available/opencloud # plantilla TLS + proxy (Let's Encrypt en prod)
│ └── certbot-challenge.site-example # HTTP temporal para primera emisión
├── scripts/
│ └── backup-volumes.sh # backup .tar.gz de volúmenes (sin pg_dump en modo core)
└── docs/
└── runbook.md # operación diaria (compose, upgrades, backups)
Los snippets en repo sirven como referencia; los ficheros activos bajo /etc/nginx/sites-available/ en el host deben revisarse siempre con nginx -t antes de systemctl reload nginx.
Variables de entorno relevantes (concepto, sin valores secretos)
COMPOSE_FILE/COMPOSE_PROJECT_NAME: definen overlays activivos y nombre estable del proyecto Docker.OC_DOMAIN: debe coincidir con el fqdn público; durante una fase inicial puede configurarse como dirección IP, pero algunos enlaces del UI se comportan mejor con nombre DNS definitivo.INSECURE: en laboratorio puede relajar validación TLS entre microservicios cuando los certificados aún no son de una AC reconocida por todos los runtime; en producción debe alinearse con una cadena TLS limpia.OC_DOCKER_IMAGE/OC_DOCKER_TAG: pinchan versión reproducible antes de upgrades deliberados.LOG_DRIVER/LOG_LEVEL: compatibilidad con política de logs del daemon Docker.DEMO_USERS: control explícito sobre usuarios de demostración.
La primera configuración administrativa debe completarse desde la interfaz y cualquier detalle sensible queda fuera de canales públicos como este blog.
Dónde viven los datos (volúmenes Docker)
OpenCloud centraliza persistencia en dos volúmenes nombrados. El contenido relevante incluye:
idm/— directorio LDAP interno embebido y material criptográfico asociado.idp/— estado del proveedor OIDC.nats/— bus de eventos JetStream entre microservicios.search/— índice full-text (Bleve).storage/metadata— metadatos CS3 (espacios, bloqueos, comparticiones).storage/users/.../.oc-nodes/— nodos/blobs del driver “decomposed”: los ficheros lógicos no son un árbol clásico en disco sino nodos referenciados por metadatos.web/— activos/cache estáticos que usa el frontal integrado.- Volumen de config runtime —
opencloud.yamlgenerado automáticamente, CSP auxiliar y políticas auxiliares.
Cifrado en reposo: por defecto los blobs son ficheros ordinarios dentro del volumen. Opciones típicas de endurecimiento: cifrado de disco del host (LUKS), backend objeto con SSE en proveedor compatible (overlay S3), o cifrado extremo-a-extremo en clientes de escritorio/móvil donde las llaves no salen del dispositivo.
Cifrado en tránsito: TLS cliente↔Nginx; LDAP interno puede usar TLS con certificados generados para servicios IDM según la configuración automática.
Flujo HTTPS del sitio KM0 (km0-web)
Internet :443 ─► Nginx host (TLS, km0.amvara.de)
│
└──► http://127.0.0.1:9180 (contenedor km0-web — sólo loopback)
Astro estático servido por nginx Alpine dentro del contenedor
- Stack: Astro 5 + Tailwind 3, salida estática (
build.formatorientado a directorios). - i18n: JSON en
src/i18n/+ rutas dedicadas (/ca/,/en/), español por defecto en raíz. - Build: Node 22 Alpine multi-stage (
npm ci,astro build). - Repo servidor: típicamente
/opt/km0-webcondocker compose build && docker compose up -dtras cambios. - SEO:
@astrojs/sitemap, alternativas hreflang.
Comando rápido de verificación tras despliegue: cabeceras HTTP a las tres rutas principales desde localhost para aislar fallos del proxy TLS (curl -sI http://127.0.0.1:9180/ etc.).
Fail2ban y subdominio del cloud
Tras el primer corte estable se añadió Fail2ban como red complementaria al firewall: observa journals/patrón de logs de servicios expuestos y aplica bans temporales ante comportamiento agresivo repetido.
El cloud quedó publicado en cloud.km0.amvara.de, separado explícitamente de la marca de marketing en km0.amvara.de. Ventajas:
- certificados y políticas CSP pueden divergir;
- los usuarios entienden qué URL usar para trabajo vs comunicación;
- los equipos pueden delegar DNS/TLS sin mezclar configuraciones del Astro estático.
Operación rutinaria (extracto)
cd /opt/opencloud/opencloud-compose docker compose ps docker compose logs -f opencloud docker compose pull && docker compose up -d # upgrade imagen pinchaada git -C /opt/opencloud/opencloud-compose pull # overlays upstream ss -tulpn | grep -E ':22|:80|:443|:9200' # sanity listeners ufw status verbose bash /opt/opencloud/scripts/backup-volumes.sh # hasta automatizar cron
Los backups en modo core pueden centrarse en archivar los volúmenes Docker comprimidos; no depende de dumps SQL PostgreSQL porque la persistencia principal está integrada en los volúmenes del stack elegido.
Modo laboratorio vs producción
- TLS provisional: certificado autofirmado útil para validar proxy y overlays — los navegadores mostrarán alertas hasta instalarse Let's Encrypt cuando exista DNS estable.
- Dominio: pasar de IP cruda a FQDN mejora enlaces internos y cookies.
INSECURErelajado: sólo coherente mientras los certificados internos no forman parte de una PKI confiable para todos los procesos.- Backups automáticos: script manual hasta cron supervisado.
- Renovación Certbot: vigilar
certbot.timeren hosts productivos.
Nada de lo anterior sustituye revisiones periódicas de configuración fuera del control de versiones ni los procesos internos de rotación acordados por quien administra la plataforma.
Riesgos y siguientes pasos típicos
- Mantener sano el ciclo de renovación TLS para cada hostname público.
- Verificar tras cada cambio de Astro que el español por defecto sigue sirviendo correctamente según configuración i18n.
- Toda edición del Nginx del host debe pasar por test + reload controlado.
- Automatizar backups de volúmenes y ensayar restauraciones parciales.
- Extender políticas Fail2ban con jails específicos cuando el patrón de ataques sea conocido.
Este texto amplía la narrativa técnica sin incluir secretos: cualquier valor sensible debe permanecer en el vault/runbook privado del equipo.