Cloudflare Access
A cosa serve
Cloudflare Access autentica identità umane e machine-to-machine prima che raggiungano l'API o l'Editorial MCP. Il public site è pubblico, ma il suo origin contenuti backend è server-to-server e non viene esposto al browser.
Componenti che lo usano
API Worker, Editorial MCP Worker e public-site Vercel server-side usano Access con applicazioni e identità separate per ambiente. Il browser non è un consumer dell'applicazione content.
Dati e artefatti scambiati
Access scambia assertion validate e identità normalizzate verso l'API. Il consumer Vercel invia i due header Service Token solo all'edge Cloudflare. D1 e i payload applicativi non contengono credenziali.
Boundary per ambiente
| Consumer | DEV | PROD | Capacità |
|---|---|---|---|
| Fra | app Admin DEV | app Admin PROD | capability applicative owner |
| Postman/automazione approvata | token e policy Admin DEV | token e policy Admin PROD | capability complete dell'operatore dopo verifica Access |
| Editorial MCP | token MCP DEV | token MCP PROD | capability MCP controllata |
| Public site Vercel | token, Rule Group e due shard content DEV | token, Rule Group e due shard content PROD | sole GET bounded di Issue, Topic, Knowledge Asset e Glossary Term |
| Release API smoke | riuso token public-site DEV | riuso token public-site PROD | sole GET bounded, nessuna capability Admin o mutativa |
Cloudflare rifiuta più di cinque destinazioni in una singola application. La boundary content di ogni ambiente usa quindi due shard provider non semantici:
- Shard A:
/issues,/issues/*,/topicse/topics/*; - Shard B:
/knowledge-assets,/knowledge-assets/*,/glossarye/glossary/*.
Il parent esatto e il relativo wildcard per i discendenti sono destinazioni separate: nessun prefix wildcard può quindi includere parzialmente una route sibling. La partizione mantiene ogni coppia parent/discendenti nello stesso shard e non crea una seconda identità, capability o authority.
Entrambi gli shard referenziano la stessa policy riutilizzabile Service Auth,
che include il Rule Group public-site dello stesso ambiente, e restituiscono
401 quando l'autenticazione fallisce. Non usare Any Access Service Token,
Everyone o Bypass. /admin/*, /f/*, /newsletter/unsubscribe, le route di
sistema e il namespace rimosso /public/v1/* non appartengono a nessuno shard.
Autenticazione e autorizzazione
Vercel invia CF-Access-Client-Id e CF-Access-Client-Secret a ogni fetch
server-side. Non usa il cookie Access come contratto e non inoltra header,
assertion o secret al browser, all'HTML o ai log pubblici.
Il GitHub Environment dello stesso ambiente può custodire una copia cifrata
della medesima coppia con i nomi PUBLIC_CONTENT_ACCESS_CLIENT_ID e
PUBLIC_CONTENT_ACCESS_CLIENT_SECRET. Soltanto lo step smoke API la riceve:
non è disponibile al job scope, alle migration, ai deploy, allo smoke MCP o
all'evidenza. Il canale non crea token, policy, identity o capability ulteriori.
Access autentica e ammette l'identità alla boundary. L'API resta responsabile
di metodo, costo bounded e visibilità PUBLISHED/accepted. La public-site
identity non è ammessa alla application Admin. Dopo la verifica di firma,
issuer e audience Admin, una service identity non-MCP riceve la capability
completa dell'operatore senza una seconda allowlist o mappa permessi nel
Worker. Il solo Client ID applicativo conservato è
AUTH_MCP_SERVICE_CLIENT_ID, necessario a riconoscere e limitare l'identità
MCP prima del default operator. Una configurazione MCP mancante o malformata
non blocca un operatore Admin non-MCP valido: il binding serve soltanto a
riconoscere e limitare l'identità MCP dedicata.
Cosa possiede il provider
Cloudflare possiede Service Token, scadenza e revoca, Rule Group, applicazioni, policy Service Auth, audience, verifica edge e log Access.
Cosa possiede Architecture Coffee
Il repository possiede destinazioni attese, separazione DEV/PROD, contratto dei metodi GET, visibilità editoriale, mapping Admin residuo, naming, sequenza di validazione e rollback. Vercel possiede la custodia server-side della coppia secret consumer; il GitHub Environment corrispondente può custodirne la copia cifrata smoke-only.
Naming operativo
| Risorsa | DEV | PROD |
|---|---|---|
| Service Token | Architecture Coffee Public Site REST - DEV |
Architecture Coffee Public Site REST - PROD |
| Rule Group | Architecture Coffee Public Site REST Consumers - DEV |
Architecture Coffee Public Site REST Consumers - PROD |
| Access application Shard A | Architecture Coffee Public Content REST Shard A - DEV |
Architecture Coffee Public Content REST Shard A - PROD |
| Access application Shard B | Architecture Coffee Public Content REST Shard B - DEV |
Architecture Coffee Public Content REST Shard B - PROD |
| Service Auth policy | Public Site REST Service Auth - DEV |
Public Site REST Service Auth - PROD |
Le coppie consumer sono distinte per ambiente. Il solo Client ID usato dal
resolver Admin è quello MCP, custodito come secret Worker cifrato e non come
variabile vars versionata.
Creazione e verifica
Prima di ogni mutazione registrare candidate SHA, inventario corrente e rollback. Per DEV, creare token, Rule Group, policy e i due shard applicativi; configurare i secret Vercel solo quando il consumer può usarli. Verificare in ordine:
- token DEV corretto su una GET bounded di ogni shard: successo;
- nessun token, token sconosciuto e token PROD sulla boundary DEV:
401; - token public-site DEV su
/admin/*: negato; - metodo mutativo sui path content: nessuna route e nessun effetto;
- entrambi gli shard usano la stessa policy e il medesimo Rule Group;
- log ed evidenza privi di credenziali e payload.
Lo smoke repository-owned esegue sempre il controllo anonimo della protected
front door. Con la coppia configurata esegue le letture e i filtri bounded,
nega /admin/* e prova il fallimento cross-environment; senza la coppia
opzionale registra esplicitamente not-run. Una coppia parziale o richiesta ma
mancante fallisce chiusa. Il target credenziale è fissato all'origin ufficiale
dell'ambiente, senza redirect, e non viene inviato alcun metodo mutativo.
Production usa la stessa sequenza soltanto nella finestra prevista dalle Issue mergiate. ADR-031 rende il merge Fra l'unico gate umano ordinario; una finestra di dipendenza non è un secondo go/no-go.
Operazioni automatiche, manuali e protette
La verifica Access per richiesta è automatica. Creazione, aggiornamento, rotazione, revoca e secret configuration sono operazioni provider distinte, esplicite e protette. Dopo il merge dell'Issue che le descrive, Codex le esegue senza un secondo gate ordinario e valida ogni stage prima del successivo.
Rotazione, revoca e rollback
La rotazione è create-before-revoke:
- creare il replacement token nello stesso ambiente;
- aggiungerlo al Rule Group senza rimuovere il token corrente;
- aggiornare la coppia Vercel e la copia GitHub Environment smoke-only dello stesso ambiente;
- validare letture autorizzate e negazioni;
- rimuovere il token precedente dal gruppo;
- revocarlo solo dopo evidenza deterministica.
Prima della revoca, il rollback ripristina secret e membership precedenti. Dopo una risposta provider ambigua, rileggere lo stato; non ripetere ciecamente la mutazione. La revoca d'emergenza rimuove prima il token dalla boundary e poi lo revoca, accettando che le letture Vercel falliscano chiuse fino al replacement.
Osservabilità sanitizzata
Registrare soltanto ambiente, nome risorsa, scadenza, famiglia route, outcome e correlation ID provider. Non registrare client secret, client ID raw, assertion, cookie, payload contenuto, email o risposta provider privata.
Fallimenti e comportamento fail-closed
Token mancante, scaduto, sconosciuto o cross-environment, Rule Group o policy errata, audience non valida e client Admin non esplicitamente configurato falliscono chiusi. Un effetto provider ambiguo richiede readback e non viene ritentato automaticamente.
Verifiche operative
Verificare sempre successo con il token corretto, 401 senza token e con token
dell'altro ambiente, negazione di /admin/*, assenza di metodi mutativi sui
path content, scadenza nominale, membership del solo Rule Group atteso e log
sanitizzati. Lo smoke repository-owned riconosce separatamente il challenge
Managed OAuth same-origin e la negazione Service Auth vincolata all'host con
evidenza Access deterministica; un 401 generico non è prova del boundary.
Riferimenti canonici
brain/adr/ADR-032-cloudflare-access-service-to-service-content-boundary.json,
brain/adr/ADR-035-reusable-protected-public-content-release-smoke.json,
src/security/, docs/architecture/security.md, il
runbook dello smoke
e ADR-023 per il boundary MCP.