Día 1 — OpenCloud, proxy y web del proyecto

OpenCloud en Docker Compose, Nginx con TLS en loopback, Fail2ban, subdominio cloud y la landing Astro KM0 publicada como segundo backend.

Introducción

El día 1 convierte la base Debian en una plataforma completa: OpenCloud sobre Docker Compose con overlays oficiales, Nginx terminando TLS y encaminando solo 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: adapta variables como PROXY_HTTP_ADDR para escuchar dentro del contenedor y publicar el puerto HTTP del proxy solo como 127.0.0.1:<puerto> en el host.
  • COMPOSE_PROJECT_NAME=opencloud: ancla los nombres de volumen Docker 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 (base más overlay de proxy externo).

Dentro del contenedor coexisten microservicios que conversan por gRPC/HTTP en localhost interno; ese rango no se expone directamente al host salvo por los endpoints previstos por el chart upstream.

Arquitectura

OpenCloud tras el proxy

Browser
   │  HTTPS :443
   ▼
Nginx (Debian, site dedicado OpenCloud)
   │  HTTP http://127.0.0.1:9200  (solo loopback)
   ▼
Contenedor OpenCloud (UID/GID fijo)
   │  microservicios internos ~9140–9300
   ▼
Volúmenes Docker:
   • opencloud-data   → ficheros, índices, NATS, IDM...
   • opencloud-config → opencloud.yaml, CSP, políticas...

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.

Puertos

Mapa de superficie expuesta

  • 22 (sshd): administración SSH — Internet según política.
  • 80/443 (Nginx): HTTP/S público — redirección ACME y virtual hosts KM0 + OpenCloud.
  • 9200 (Docker → OpenCloud): solo 127.0.0.1 — backend HTTP que ve Nginx.
  • 9140–9300: microservicios internos del contenedor — no publicados en el host.
UFW refuerza la política permitiendo desde Internet solo lo necesario. Si no debe conocerlo el navegador externo, no escucha en todas las interfaces.

Nginx

Directivas clave hacia OpenCloud

  • proxy_buffering off: SSE para actualizaciones en tiempo real del cliente web.
  • proxy_request_buffering off: subidas resumibles TUS sin bufferizar todo el cuerpo.
  • proxy_pass http://127.0.0.1:9200: TLS ya resuelto en el borde.
  • X-Forwarded-Proto $scheme: redirects y cookies coherentes para HTTPS.
  • Upgrade/Connection passthrough: WebSockets para el UI interactivo.
  • Timeouts 3600s y client_max_body_size 10G: sesiones largas y ficheros grandes.

Despliegue

Árbol en /opt/opencloud

/opt/opencloud/
├── opencloud-compose/     # clon upstream + overlays
│   ├── docker-compose.yml
│   ├── external-proxy/opencloud.yml
│   └── .env                 # activo — fuera de git, chmod 600
├── nginx/                   # plantillas TLS + proxy
├── scripts/backup-volumes.sh
└── docs/runbook.md

Los snippets en repo sirven como referencia; los ficheros activos bajo /etc/nginx/sites-available/ deben revisarse siempre con nginx -t antes de systemctl reload nginx.

Datos

Volúmenes Docker y persistencia

OpenCloud centraliza persistencia en dos volúmenes nombrados. El contenido relevante incluye:

  • idm/ e idp/: directorio LDAP interno y estado del proveedor OIDC.
  • nats/: bus de eventos JetStream entre microservicios.
  • search/: índice full-text (Bleve).
  • storage/: metadatos CS3 y nodos del driver decomposed.
  • web/: activos estáticos del frontal integrado.

Cifrado en reposo: blobs ordinarios dentro del volumen; opciones de endurecimiento incluyen LUKS, SSE en backend objeto o cifrado E2E en clientes. Cifrado en tránsito: TLS cliente↔Nginx.

Web KM0

Flujo HTTPS del sitio corporativo

Internet :443 ─► Nginx host (TLS, km0digital.com)
                     └──► http://127.0.0.1:9180  (km0-web — solo loopback)
                            Astro estático + nginx Alpine
  • Stack: Astro 5 + Tailwind 3, salida estática.
  • i18n: JSON en src/i18n/ + rutas /ca/, /en/, /de/; español por defecto en raíz.
  • Build: Node 22 Alpine multi-stage; repo en /opt/km0-web.
  • SEO: @astrojs/sitemap con alternativas hreflang.

Perímetro

Fail2ban y subdominio del cloud

Tras el primer corte estable se añadió Fail2ban como red complementaria al firewall. El cloud quedó publicado en cloud.km0digital.com, separado de la marca de marketing en km0digital.com:

  • 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

Comandos rutinarios

cd /opt/opencloud/opencloud-compose
docker compose ps
docker compose logs -f opencloud
docker compose pull && docker compose up -d
git -C /opt/opencloud/opencloud-compose pull

ss -tulpn | grep -E ‘:22|:80|:443|:9200’ ufw status verbose bash /opt/opencloud/scripts/backup-volumes.sh

Laboratorio vs producción

Fases del despliegue

  • TLS provisional: certificado autofirmado útil para validar proxy — alertas en navegador hasta Let's Encrypt con DNS estable.
  • Dominio: pasar de IP cruda a FQDN mejora enlaces internos y cookies.
  • INSECURE relajado: solo coherente mientras los certificados internos no forman PKI confiable.
  • Backups: script manual hasta cron supervisado; vigilar certbot.timer en producción.

Siguiente paso

Día 2

El día 2 madura autenticación OIDC con Dex, actualiza OpenCloud 7.x y establece el primer backup integral. Mientras tanto, explora los servicios o el relato del día 2.