Cloudflare Access — apps protegidas¶
Estado a 2026-08-01 (re-verificado vía CF API, ver recipe abajo). Conviven dos planos de
identidad: CF Access (edge) con PocketID como Identity Provider, y PocketID OIDC nativo
per-app. El conteo real es 51 apps CF Access self_hosted (subió a 60 desde 54 el
2026-07-19 — se añadieron dj, reorg, rol y un bypass music.monxas.casa/share, más el
registro interno monxas.cloudflareaccess.com del propio IdP callback — bajó a 59 el 2026-08-01
al retirar piso, y a 51 el mismo día al retirar las 8 apps señuelo del honeypot Krawl).
Apps protegidas (CF Access — 51 apps self_hosted, verificado 2026-08-01)¶
Policy default todos: allow { [email protected] OR email_domain=monxas.com }. Sesión 24h.
Enumeración completa — re-verificada 2026-08-01 (51 apps tras retirar piso + Krawl)¶
Auto-generada
Lista viva vía CF API. Para regenerar: ver API recipes. Policy default de las protegidas: allow [email protected] OR email_domain=monxas.com, sesión 24h.
Protegidas (47) — a nivel dominio:
*-preview, airdrop, apis, audio, bazarr, changedetection, code, deploy-trip-dev, deploy-trip, deploy, dj, dockge, dozzle, esp, etmf, finanzas, grafana, japon, jellyseerr, jira, job-hunter, lidarr, md-api, md-demo, md, music, n8n, nas, nopor, ntfy, ocr, okrs, pdf, pinchflat, prowlarr, radarr, reddit, reorg, rol, slicer, slskd, sonarr, soularr, synology, vera, wp-pulse-app.
dj = "DJ (altavoces casa)" (dj.monxas.casa, ver dj_monxas_casa_cf_access.md). reorg =
music-reorg-ui (beets). rol = SillyTavern.
✅
piso(Piso Colón) resuelto 2026-08-01: confirmado decomisionado desde 2026-07-26 (compose enstacks-disabled/piso-retired-20260726). App de CF Access y registro DNS borrados. Queda pendiente borrar a mano el cliente OIDC "Piso Colón" en PocketID (sin API key/sqlite accesible para automatizarlo).✅ Honeypot Krawl resuelto 2026-08-01:
admin,cpanel,dashboard,login,panel,phpmyadmin,webmail,wp-admineran los 8 subdominios señuelo del honeypot Krawl (projects/homelab-stacks/infra.md), decomisionado sin fecha documentada. Confirmado que ninguno aparecía en las rutas "Desired" dehomelab-ctl.py status— no protegían nada real. Borradas las 8 apps de CF Access y sus 8 registros DNS.⚠️ TODO sin verificar (resto):
deploy-trip*,etmf,md*,slicer,vera,wp-pulse-apptampoco aparecen en las rutas "Desired" dehomelab-ctl.py status— puede ser que vivan en un Caddyfile distinto al que gestiona esa herramienta (no confirmado). A diferencia depiso/Krawl, no se encontró evidencia directa (como unstacks-disabled/) de que estas estén realmente decomisionadas — no tocadas.
Bypass (3) — path-specific (API/webhooks, sin login):
music.monxas.casa/rest— music-api-bypassmusic.monxas.casa/share— music-share-bypass (nuevo, no documentado antes de esta auditoría)n8n.monxas.casa/webhook— n8n-webhook-bypass
Protegidas (1) — path-specific dentro de un dominio SIN Access (2026-07-25):
home.monxas.casa/local/tablet— tablet-local-protect (ver gotcha 5 y sección "Añadidas 2026-07-25")
Otro (1): monxas.cloudflareaccess.com — registro interno del callback del IdP (Cloudflare
Access como cliente OIDC de PocketID), no es una "app" en el sentido normal.
Apps deliberately SIN CF Access¶
Auth propio fuerte (no need perimeter)¶
home.monxas.casa,matematico.monxas.casa— Home Assistant (dominio raíz; excepción:/local/tabletsí tiene CF Access desde 2026-07-25, ver más abajo)nas.monxas.casa— Synologyjellyfin.monxas.casa— Jellyfin (Stash sigue vivo,nopor.monxas.casa, en on-demand como el resto del stack — no decomisionado, verprojects/homelab-stacks/tools.md;9999es Dozzle)
OIDC nativo ya integrado (PocketID)¶
Integrados vía PocketID OIDC per-app (login propio de cada app, no gateados por CF Access):
- grafana.monxas.casa — Grafana
- pass.monxas.casa — Vaultwarden
- bookmarks.monxas.casa — Karakeep
- pics.monxas.casa — Immich
- podcasts.monxas.casa — Audiobookshelf
- paperless.monxas.casa — Paperless
Público / bearer-only / IdP itself¶
pocketid.monxas.casa— NUNCA detrás de CF Access (es el IdP)monxas.casa— apex landingcors.monxas.casa— utility público (rate-limited)kiwix.monxas.casa— read-only KBrag.monxas.casa,loki.monxas.casa— APIs con bearer auth
Webhook bypass (special policies)¶
n8n.monxas.casa/webhook— bypass everyone (specific path)music.monxas.casa/rest— bypass everyone (Subsonic API para Amperfy; UImusic.monxas.casaprotegida). Ver Crear un subdominio.deploy.monxas.casa,deploy-trip*.monxas.casa— CI/CD webhooks (review caso a caso)
API recipes¶
Automatizado desde 2026-08-07:
scripts/add-service.sh <host> <backend-ip:puerto>hace el ciclo completo (tunnel ingress + esta Access app/policy + bloque Caddy en ambos nodos HA + reload + smoke test), leyendo credenciales desecrets/cloudflare.sops.yamly resolviendo el IdP PocketID vía API. Ver Crear un subdominio. Las recetas manuales de abajo siguen siendo válidas para casos sueltos o depuración.
# Listar apps + policies
source ~/.env # CF_API_TOKEN
ACC=0e70e9f3f8ffabbdcf9f46b0307a074e
curl -sH "Authorization: Bearer $CF_API_TOKEN" \
"https://api.cloudflare.com/client/v4/accounts/$ACC/access/apps?per_page=100" | \
jq '.result[] | {domain, name}'
# Proteger nueva app (crea + policy default)
curl -X POST -H "Authorization: Bearer $CF_API_TOKEN" -H 'Content-Type: application/json' \
--data '{"name":"X","domain":"x.monxas.casa","type":"self_hosted","session_duration":"24h","app_launcher_visible":false}' \
"https://api.cloudflare.com/client/v4/accounts/$ACC/access/apps"
# luego copiar app_id de la respuesta y:
curl -X POST -H "Authorization: Bearer $CF_API_TOKEN" -H 'Content-Type: application/json' \
--data '{"name":"default","decision":"allow","include":[{"email":{"email":"[email protected]"}},{"email_domain":{"domain":"monxas.com"}}]}' \
"https://api.cloudflare.com/client/v4/accounts/$ACC/access/apps/<APP_ID>/policies"
# Verify external (302 = CF Access OK)
curl -s -o /dev/null -w "%{http_code}\n" https://x.monxas.casa/
Identity Provider¶
PocketID está configurado como OIDC IdP en CF Zero Trust. El cliente Cloudflare Access existe en PocketID DB con callback https://monxas.cloudflareaccess.com/cdn-cgi/access/callback.
PocketID tiene 11 clientes OIDC registrados: Cloudflare Access, Vaultwarden, Karakeep, Audiobookshelf, Immich, Paperless, Grafana, Remote-Pulse Dashboard, wp-pulse, wp-pulse-app, Piso Colón. El cliente Cloudflare Access es el plano edge; el resto son integraciones OIDC nativas per-app. ⚠️ Piso Colón es un cliente huérfano pendiente de borrar a mano (el servicio se decomisionó el 2026-07-26; CF Access y DNS ya limpiados el 2026-08-01, falta solo este cliente en la UI de PocketID).
Cuando hay sesión válida, CF Access pasa el request al origin. Cuando no, redirige a PocketID. Una vez autenticado, regresa a la app. Sesión 24h shared entre todas las apps.
Añadidas 2026-06-17 (1)¶
wp-pulse-app (operator UI) — policy allow SOLO [email protected] + [email protected]
(no email_domain=monxas.com, porque folele es gmail). App f9213006-…, policy 3399346e-….
Por qué: el operator UI estaba SIN gate real — el Caddy forward_auth apuntaba a
pocketid.monxas.casa/forward-auth, endpoint que PocketID no tiene (devuelve la SPA con HTTP 200
para cualquier path/client_id), así que Caddy lo leía como "allow" y dejaba pasar a todos. El backend,
sin X-Forwarded-Email, caía al SUPERADMIN fallback → cualquiera en internet = admin total sobre
todos los sites. Cerrado moviendo el gate a CF Access + Caddy ahora mapea
Cf-Access-Authenticated-User-Email → X-Forwarded-Email para resolución de rol per-user.
Añadidas 2026-07-25 (1)¶
tablet-local-protect — home.monxas.casa/local/tablet, policy allow ([email protected]
OR email_domain=monxas.com). App 38089e97-6e30-4e6b-ab63-eb5334c208c6, policy
f9087407-1c09-4eef-a4da-c3f8331791ae.
Por qué: /local/ en Home Assistant sirve estático sin ninguna auth, por diseño de HA
— es un detalle de la plataforma, no de home.monxas.casa en particular. El dashboard de la
tablet familiar (repo tablet-f1, deployado en /config/www/tablet/) guarda secretos reales
en js/config.local.js (HA_TOKEN de larga duración + credenciales de Navidrome) — ese archivo
era descargable por cualquiera en internet que conociera la ruta, sin ningún login, verificado
con curl externo (200 antes del fix). Se cerró creando una CF Access app scoped al path
/local/tablet (no al dominio completo, que sigue con su auth nativa de HA para todo lo demás).
Por qué no rompe la tablet física en casa: monxas.casa tiene DNS split-horizon en Pi-hole
(/etc/dnsmasq.d/05-local-homelab.conf: address=/monxas.casa/192.168.0.250) — cualquier
dispositivo en la LAN (incluida la tablet) resuelve directo a la VIP de Caddy, sin pasar por
el tunnel de Cloudflare ni por Access en absoluto. Solo un cliente que resuelva por DNS
público (i.e. desde fuera de casa) atraviesa el tunnel y ve el gate de CF Access. Verificado:
curl https://home.monxas.casa/local/tablet/js/config.local.js → 302 (protegido) mientras
curl https://home.monxas.casa/ → 200 (resto de HA intacto).
Gotchas¶
- PocketID NO tiene
/forward-auth— es un OIDC IdP, no un forward-auth proxy. No usesforward_auth pocketid.monxas.casaen Caddy (devuelve 200 SPA = allow-all silencioso). Gatea con CF Access (PocketID como IdP) o un proxy real (oauth2-proxy/tinyauth) con PocketID OIDC detrás. - App sin policy = BLOQUEA TODO. Crear app y policy en transacción rápida o usar CF dashboard que lo hace junto.
- Webhooks/APIs externos: requieren bypass específico por path. Ver patrón
n8n/webhook. - Móviles/CLIs (LunaSea, etc.): no soportan OIDC interactivo. Usar Service Tokens (CF API).
- Subdomain debe estar en tunnel ingress antes de crear CF Access app, si no, queda inaccesible.
/local/de Home Assistant sirve estático SIN auth, siempre (diseño de HA, no de la app que viva ahí) — cualquier secreto en JS servido bajohome.monxas.casa/local/...es público hasta que se gatee explícitamente con una CF Access app scoped a ese path (ver "Añadidas 2026-07-25"). Antes de meter cualquier token nuevo en un dashboard bajo/local/, comprobar primero si ese path concreto ya tiene su propia Access app — el dominio raíz de HA no lo cubre.