Distribuzioni e cache¶
Una distribuzione espone un bucket (o un suo prefisso) su uno o più domini tuoi, in stile CloudFront:
un sito statico su www.example.com, i download su download.example.it, un endpoint S3 dedicato a un
cliente su s3.cliente.it. Ogni dominio può avere il proprio certificato TLS e le proprie impostazioni di
cache; le modifiche raggiungono tutti i nodi senza riavvii.
Si gestiscono dalla console (Distribuzioni, con un wizard di creazione), con nexoctl dist e
nexoctl cache, o dall'Admin API (/api/v1/distributions, /api/v1/cache). Per un esempio completo dall'inizio
alla fine segui il tutorial del sito statico su due domini.
Modalità¶
--mode |
Cosa serve | Uso |
|---|---|---|
website (default) |
GET/HEAD/OPTIONS con risposte HTML: index document, SPA, pagine d'errore, redirect, header, behavior per percorso, URL firmati | Siti statici, asset, download |
s3 |
L'API S3 completa del bucket sul dominio (firma SigV4 verificata con l'host pubblico) | Endpoint S3 dedicati per cliente o applicazione |
In modalità s3 valgono IAM, bucket policy e regole di accesso del bucket; della distribuzione contano solo
domini, certificato, restrizioni e l'accesso di default (public, authenticated o deny). Le opzioni da
sito web (behavior per percorso, pagine d'errore, redirect, header) vengono rifiutate al salvataggio.
nexoctl dist create local sito-web --bucket web --prefix /prod --mode website \
--domain www.example.com --domain example.com --index index.html --spa /index.html
nexoctl dist create local cliente --bucket cliente --mode s3 --domain s3.cliente.it --access authenticated
nexoctl dist list local
nexoctl dist info local sito-web
nexoctl dist disable local sito-web # i domini rispondono 403
Domini e instradamento¶
Un dominio è esatto (www.example.com) o wildcard (*.preview.example.com) e appartiene a una sola
distribuzione. Per ogni richiesta Nexo guarda l'header Host e sceglie, in ordine:
- un dominio esatto di una distribuzione;
- il dominio wildcard più specifico;
<bucket>.<NEXO_DOMAIN>(bucket in stile virtual host);- i domini principali di S3 e della console (
NEXO_SERVER_URL,NEXO_BROWSER_REDIRECT_URL); - altrimenti l'API S3 path-style, oppure
421 Misdirected Requestcon--unknown-host misdirected(NEXO_UNKNOWN_HOST); i listener web dedicati rispondono sempre421.
Non si possono usare come alias i nomi su cui risponde il deployment: i domini principali, NEXO_DOMAIN e i
virtual host di altri bucket, gli host degli endpoint dei nodi, localhost e il nome della macchina (409). Un
dominio sotto la wildcard di un'altra distribuzione (app.example.com sotto *.example.com) richiede i permessi
anche sul bucket di quella distribuzione (403 altrimenti).
$ nexoctl dist domain add local sito-web '*.preview.example.com'
$ nexoctl dist resolve local www.example.it
www.example.it: distribution `sito` (alias www.example.it, website), bucket sito
Le distribuzioni rispondono su due listener dedicati, se configurati: --web-address (NEXO_WEB_ADDRESS,
HTTP e challenge ACME HTTP-01) e --web-tls-address (NEXO_WEB_TLS_ADDRESS, HTTPS con i certificati delle
distribuzioni scelti per SNI), utili per esporre i siti sulle porte 80/443 senza esporre l'API S3. Sulla porta S3
(9000) rispondono secondo --s3-distributions (NEXO_S3_DISTRIBUTIONS): con auto (default) solo se il
deployment dichiara i nomi dell'endpoint S3 con NEXO_SERVER_URL o NEXO_DOMAIN (come nelle configurazioni di
deploy/proxy), con on sempre, con off mai. Senza
nomi dichiarati la porta S3 risponde a qualunque nome la raggiunga (load balancer, alias DNS, nomi di servizio):
un alias potrebbe coincidere con il nome che i client S3 usano. Sulla porta S3 i percorsi /nexo/ (sonde di
salute, metriche, RPC tra i nodi) restano sempre del server, qualunque sia il dominio.
Se una distribuzione impedisce di usare il deployment, avvia i nodi con --no-distributions
(NEXO_NO_DISTRIBUTIONS=true): nessun dominio viene instradato, l'Admin API funziona e la distribuzione si
corregge o si rimuove (nexoctl dist remove); poi riavvia senza l'opzione.
Behavior e accesso¶
Il behavior di default vale per tutti i percorsi; i behavior aggiuntivi cambiano il comportamento dei
percorsi che corrispondono a un pattern (--prefix, --glob, --regex, il primo che corrisponde vince) e
ereditano il resto dal default.
--access |
Chi legge |
|---|---|
public |
chiunque, salvo i deny delle regole di accesso e il block public access del bucket o del cluster |
signed |
solo con URL o cookie firmati dalle chiavi di un key group (--key-group) |
authenticated |
solo con credenziali S3 (SigV4) o la sessione della console |
deny |
nessuno (403) |
nexoctl dist behavior add local sito-web --glob '/assets/**' --cache-control 'public, max-age=31536000, immutable'
nexoctl dist behavior add local sito-web --regex '^/downloads/.*\.zip$' --access signed --key-group rilasci
nexoctl dist behavior add local sito-web --prefix /internal/ --access deny --position 1
nexoctl dist behavior list local sito-web
nexoctl dist test local sito-web /downloads/v1.zip # quale behavior, accesso e header
Un behavior public di una distribuzione website risponde 403 finché il
block public access del
bucket o del cluster è attivo. Il bucket di origine può restare privato: la distribuzione legge gli oggetti per
conto dei visitatori.
Funzioni da sito web¶
| Funzione | Comando |
|---|---|
| Index document | --index index.html su create, edit o un behavior |
| Single page application | --spa /index.html: serve quel file (200) al posto degli oggetti mancanti |
| Pagine d'errore | nexoctl dist error-page set local sito-web 404 /404.html (--response-status per cambiare lo status) |
| Redirect | dist redirect add con --https-only, --host-from example.com --to-host www.example.com o --from '^/vecchio/(.*)$' --to '/nuovo/$1' (--status 301/302/303/307/308) |
| Header di risposta | dist headers --set 'Name: value', --unset, --security (HSTS, nosniff, frame options, referrer policy) |
| CORS | dist headers --cors-origin https://app.example.com --cors-method GET |
| Log di accesso | logging: {accessLog: true, format: combined} nel YAML della distribuzione (json per default: un record distribution access nel log del server, con event_time l'inizio della richiesta e time l'ora del record) |
URL e cookie firmati¶
Per i behavior --access signed Nexo verifica URL nel formato di CloudFront (?Expires=&Signature=&Key-Pair-Id=,
o ?Policy= per una policy con risorsa wildcard, IP e data di inizio) e i cookie CloudFront-*, con chiavi
Ed25519 o RSA raccolte in key group:
nexoctl dist keygroup create local rilasci # genera la chiave, salva la privata
nexoctl dist keygroup key add local rilasci --public-key mia.pub.pem # oppure carica la tua pubblica
nexoctl dist sign-url local sito-web /downloads/v1.zip --expire 1h --ip 203.0.113.0/24
nexoctl dist sign-url local sito-web /premium/ --expire 12h --cookie --resource 'https://www.example.com/premium/*'
La firma è verificata prima della cache e non entra nella chiave di cache. Più chiavi nello stesso key group
permettono di ruotarle senza interruzioni. La firma lato server è disponibile anche con
POST /api/v1/distributions/{id}/sign (permesso admin:SignDistributionURL) e dalla console.
Certificati¶
| Comando | Certificato |
|---|---|
nexoctl dist cert set local ID --auto |
ottenuto e rinnovato via ACME (Let's Encrypt per default) per tutti i domini esatti |
nexoctl dist cert set local ID --auto --dns-provider NOME |
ottenuto via ACME con la challenge DNS-01: domini esatti e wildcard |
nexoctl dist cert set local ID --cert crt.pem --key key.pem |
caricato da te (catena PEM, foglia per prima) |
nexoctl dist cert set local ID --none |
nessuno: TLS terminato da un proxy |
Configurazione ACME del server: NEXO_ACME_EMAIL, NEXO_ACME_DIRECTORY (default Let's Encrypt),
NEXO_ACME_CHALLENGE (http-01 sul listener web, tls-alpn-01 sul listener web TLS), NEXO_ACME_CA_CERT
per una CA ACME privata. I certificati sono salvati cifrati nel deployment, distribuiti a tutti i nodi e
presentati per SNI; in cluster il rinnovo è eseguito da un solo nodo. nexoctl dist cert list mostra stato e
scadenza; la metrica nexo_tls_certificate_expiry_seconds{certificate} alimenta gli alert. --challenge
sceglie la challenge di una distribuzione (http-01, tls-alpn-01, dns-01) al posto di quella del server.
Domini wildcard: la challenge DNS-01¶
Un dominio wildcard (*.preview.example.com) può ottenere un certificato ACME solo con la challenge
DNS-01: Nexo pubblica un record TXT _acme-challenge.<dominio> nella tua zona DNS, attende che i
nameserver autoritativi della zona lo servano, chiede alla CA di validarlo e lo rimuove. Una distribuzione con
un dominio wildcard e certificato automatico deve usare DNS-01 (altrimenti è rifiutata); resta possibile un
certificato caricato. Dichiara prima un provider DNS (permesso admin:ManageCertificates):
# Cloudflare: un token API con il permesso Zone:DNS:Edit sulla zona
nexoctl admin dns-provider add local cf --type cloudflare --api-token-file cf-token.txt
# AWS Route 53: una access key, o niente per le credenziali dell'ambiente dei nodi (ruolo dell'istanza...)
nexoctl admin dns-provider add local aws --type route53 --access-key-id AKIA... --secret-access-key-file aws.key
# RFC 2136 (BIND, Knot, PowerDNS): aggiornamenti dinamici firmati con una chiave TSIG
nexoctl admin dns-provider add local bind --type rfc2136 --nameserver ns1.example.com \
--tsig-key nexo-acme --tsig-algorithm hmac-sha256 --tsig-secret-file tsig.key
nexoctl admin dns-provider test local cf example.com # pubblica, verifica e rimuove un record di prova
nexoctl dist cert set local previews --auto --dns-provider cf
L'identità Route 53 richiede route53:ListHostedZonesByName, route53:ListResourceRecordSets,
route53:ChangeResourceRecordSets e route53:GetChange; la zona è trovata per nome a meno che
--hosted-zone-id (--zone-id per Cloudflare, --zone per RFC 2136) la indichi. Le credenziali sono salvate
cifrate nel deployment, mai più mostrate né scritte nei log: l'API e la console dicono solo se sono
impostate, e una modifica che le lascia vuote le mantiene. Un provider usato da una distribuzione non può
essere rimosso. --propagation-timeout (default 2 minuti) limita l'attesa del record; con DNS split-horizon o
una zona privata, NEXO_ACME_DNS_NAMESERVERS (--acme-dns-nameservers, host[:porta] separati da virgola)
fa verificare a Nexo questi nameserver al posto di quelli autoritativi. La console gestisce i provider in
Distribuzioni → Certificati e la challenge nella scheda TLS di una distribuzione.
Dietro uno o più proxy¶
NEXO_TRUSTED_PROXIES: solo da questi indirizzi Nexo accettaX-Forwarded-For/Proto(oForwarded, conNEXO_FORWARDED_HEADERS=forwarded) e il PROXY protocol sui listener elencati inNEXO_PROXY_PROTOCOL(s3,console,web). L'IP reale del client alimenta le restrizioni, le regole di accesso e gli URL firmati con--ip.- L'
Hostdeve arrivare invariato;NEXO_TRUST_FORWARDED_HOST=truesolo per i proxy che lo riscrivono. - Origin secret: ogni proxy aggiunge un header segreto; le richieste che arrivano ai nodi senza
(aggirando il proxy) ricevono
403.
nexoctl dist origin-secret rotate local sito-web # stampa il nuovo segreto una volta sola
nexoctl dist restrict local sito-web --origin-secret-header X-Origin-Secret
Con più proxy, un segreto per proxy come riferimento ${secret:proxy-a} nel YAML, risolto su ogni nodo da
NEXO_SECRET_PROXY_A.
- Restrizioni per indirizzo: nexoctl dist restrict local sito-web --deny-ip 203.0.113.0/24 (o
--allow-ip); il limite di richieste: vedi Limite di richieste.
Configurazioni pronte per nginx, Caddy, Traefik, HAProxy e Cloudflare:
deploy/proxy.
Limite di richieste¶
Una distribuzione può limitare le proprie richieste (restrictions.rateLimit, scheda Sicurezza della
console): requestsPerSecond in media, burst tutte insieme (default: il valore del limite), contate per
indirizzo client (per: ip, il default: il client dietro i proxy fidati) o per l'intera distribuzione
(per: distribution). Oltre la soglia un sito risponde 429 Too Many Requests e una distribuzione s3
503 SlowDown (che gli SDK S3 ripetono), entrambe con Retry-After.
nexoctl dist restrict local sito-web --rate-limit 50 --burst 100 # per indirizzo client
nexoctl dist restrict local api --rate-limit 500 --rate-limit-per distribution
nexoctl dist restrict local sito-web --no-rate-limit
Le restrizioni per indirizzo e l'origin secret sono verificati prima: un client che rifiutano non consuma
token. Il limite è per nodo, come i limiti di richieste delle API:
dietro un load balancer su N nodi una distribuzione ammette fino a N volte il suo limite. Sul listener S3 le
richieste di una distribuzione s3 incontrano anche i limiti ratelimit.s3_* dell'API S3. I rifiuti sono contati
in nexo_ratelimit_throttled_total{api="web"|"s3",kind="distribution"} e nell'access log della distribuzione
con il loro stato.
Cache¶
Nexo conserva le risposte delle distribuzioni in una cache in memoria (NEXO_CACHE_MEMORY, default 256 MiB)
e, con NEXO_CACHE_DIR, su disco (NEXO_CACHE_SIZE, meglio un SSD separato dai dischi dei dati).
L'header X-Cache dice come è stata servita ogni risposta:
X-Cache |
Significato |
|---|---|
HIT |
dalla cache, fresca |
MISS |
dall'origine (e messa in cache se possibile) |
STALE |
copia scaduta servita mentre si aggiorna o perché l'origine non risponde |
EXPIRED |
copia scaduta riletta dall'origine |
REVALIDATED |
copia scaduta confermata dall'origine (stesso ETag) |
BYPASS |
cache saltata (cache disattivata, richiesta con credenziali, cookie o header di bypass) |
Livelli e policy¶
Le impostazioni si ereditano: cluster (nexoctl cache settings) → distribuzione → dominio
(--domain) → percorso (--behavior). Ogni livello può scegliere una policy e cambiarne singoli campi.
| Policy predefinita | TTL | Uso |
|---|---|---|
standard |
1h (massimo 7d), rispetta il Cache-Control degli oggetti |
default |
aggressive |
1d (minimo 1h, massimo 30d), finestre stale lunghe | contenuti che cambiano raramente |
immutable-assets |
365d, anche nei browser | asset con hash nel nome |
respect-origin |
solo quello dichiarato dagli oggetti | controllo dal publisher |
disabled |
nessuna cache | contenuti sempre freschi |
nexoctl dist cache set local sito-web --policy standard
nexoctl dist cache set local sito-web --domain www.example.com --policy aggressive --quota 20GiB
nexoctl dist cache set local sito-web --domain '*.preview.example.com' --disable
nexoctl dist cache set local sito-web --behavior '/assets/**' --ttl-default 365d
nexoctl dist cache set local sito-web --behavior '/api/' --behavior-type prefix --disable
nexoctl cache policy create local lunga --ttl-default 1d --ttl-max 30d --query allowlist:v --stale-while-revalidate 5m
nexoctl dist cache show local sito-web
Altri campi utili: --query (parametri della query nella chiave), --key-header/--key-cookie,
--bypass-cookie, --negative-ttl (404/403), --stale-if-error, --compression, --range-caching (video e
file grandi a blocchi), --warm-on-write. Le risposte a richieste autenticate non sono mai condivise tra
utenti diversi.
Invalidazione¶
- Automatica (default): quando un oggetto cambia (PUT, DELETE, copia, metadati, tag) le sue voci vengono scartate su tutti i domini e tutti i nodi.
- Manuale, per percorso esatto, glob, prefisso, regex, tag, dominio o tutto, con
--softper marcare come scadute invece di cancellare:
nexoctl dist invalidate local sito-web '/assets/**' --wait
nexoctl cache purge local sito-web --tag prodotto-42 # oggetti con x-amz-meta-cache-tag: prodotto-42
nexoctl cache purge local sito-web --domain static.example.it --all
nexoctl cache purge list local sito-web
- CDN davanti a Nexo: i webhook di purge (
nexoctl cache webhook add, tipicloudflare,fastly,akamai,nginx,varnish,generic) propagano ogni invalidazione, con retry e coda persistente. Le destinazioni devono essere indirizzi pubblici, salvo le reti inNEXO_CACHE_WEBHOOK_ALLOW.
Le invalidazioni e i warm-up richiedono solo admin:InvalidateCache: una pipeline di deploy non ha bisogno di
altri permessi.
Warm-up, diagnostica, statistiche¶
nexoctl cache warm local sito-web --prefix /assets/ --bandwidth 20MiB --wait
nexoctl cache inspect local https://www.example.com/assets/app.js # chiave, policy e livello, stato su ogni nodo
nexoctl cache stats local # hit ratio, byte, top URL per dominio
nexoctl cache settings local --node-header # aggiunge X-Cache-Node alle risposte
Con nexoctl cache settings local --debug-secret-file debug.secret, le richieste con l'header
X-Nexo-Cache-Debug: <segreto> ricevono anche X-Cache-Key e X-Cache-Policy.
In cluster¶
NEXO_CACHE_MODE sceglie come i nodi condividono la cache:
| Modalità | Comportamento |
|---|---|
local (default) |
ogni nodo ha la sua cache |
shared |
ogni chiave ha un nodo proprietario (consistent hashing): una sola copia nel cluster, capacità sommata |
shared+local |
come shared più una piccola cache locale per gli oggetti più richiesti |
Permessi¶
| Permesso | Per |
|---|---|
admin:ManageDistributions |
creare e modificare distribuzioni e key group |
admin:ManageCertificates |
caricare e rimuovere certificati |
admin:SignDistributionURL |
firmare URL e cookie lato server |
admin:ManageCache |
impostazioni, policy e webhook della cache |
admin:InvalidateCache |
invalidazioni e warm-up |
Metriche¶
nexo_distribution_requests_total{distribution,host,status}, nexo_distribution_bytes_served_total,
nexo_distribution_request_duration_seconds, nexo_cache_requests_total{distribution,host,result},
nexo_cache_hit_ratio, nexo_cache_bytes_served_total{source}, nexo_cache_size_bytes,
nexo_cache_invalidations_total, nexo_cache_webhook_calls_total, nexo_ratelimit_throttled_total{api,kind}.
Vedi Monitoraggio.
GitOps¶
Distribuzioni, impostazioni di cache e policy si esportano e importano in YAML:
nexoctl dist export local > distributions.yaml
nexoctl dist import local distributions.yaml
nexoctl dist cache export local sito-web > sito-web-cache.yaml
nexoctl dist cache import local sito-web-cache.yaml
nexoctl cache policy export local > policies.yaml
nexoctl cache policy import local policies.yaml
I segreti (origin secret, token dei webhook) si scrivono come riferimenti ${secret:nome}, risolti sui nodi
dalle variabili NEXO_SECRET_*: i file YAML non contengono segreti.