Concetti del Caddyfile

Questo documento vi aiuterà a conoscere in dettaglio l'HTTP Caddyfile.

  1. Struttura
  2. Opzioni globali
  3. Indirizzi
  4. Matcher
  5. Placeholder
  6. Snippet
  7. Rotte nominate
  8. Commenti
  9. Variabili d'ambiente

Struttura

La struttura del Caddyfile può essere descritta visivamente:

{
email voi@vostro.com
servers {
trusted_proxies static private_ranges
}
}
(snippet) {
# questo è uno snippet riutilizzabile
log {
output file /var/log/access.log
}
}
example.com {
@post {
method POST
}
reverse_proxy @post localhost:9001 localhost:9002 {
lb_policy first
}
file_server /static
import snippet
}
www.example.com {
redir https://example.com{uri}
import snippet
}
Legenda
Blocco opzioni globali
Snippet
Blocco sito
Definizione matcher
Nome opzione
Valore opzione
Commento
Indirizzo del sito
Direttiva
Token matcher
Argomento
Sottodirettiva

Punti chiave:

  • Un blocco opzioni globali opzionale può essere la primissima cosa nel file.

  • Gli snippet o le rotte nominate possono apparire opzionalmente subito dopo.

  • Altrimenti, la prima riga del Caddyfile è sempre l'indirizzo (o gli indirizzi) del sito da servire.

  • Tutte le direttive e i matcher devono essere inseriti in un blocco sito. Non esiste un ambito globale o ereditarietà tra i blocchi sito.

  • If esiste un solo blocco sito, le sue parentesi graffe { } sono opzionali.

Un Caddyfile consiste in almeno uno o più blocchi sito, ciascuno dei quali inizia sempre con uno o più indirizzi per il sito. Qualsiasi direttiva che appaia prima dell'indirizzo confonderà il parser.

Blocchi

L'apertura e la chiusura di un blocco si effettuano con le parentesi graffe:

... {
	...
}
  • La parentesi graffa di apertura { deve trovarsi alla fine della sua riga ed essere preceduta da uno spazio.

  • La parentesi graffa di chiusura } deve trovarsi su una riga a sé stante.

Quando c'è un solo blocco sito, le parentesi graffe (e l'indentazione) sono opzionali. Questo serve per comodità per definire velocemente un singolo sito, ad esempio questo:

localhost

reverse_proxy /api/* localhost:9001
file_server

è equivalente a:

localhost {
	reverse_proxy /api/* localhost:9001
	file_server
}

quando si ha un solo blocco sito; è una questione di preferenza.

Per configurare più siti con lo stesso Caddyfile, dovete usare le parentesi graffe attorno a ciascuno di essi per separarne le configurazioni:

example1.com {
	root /www/example.com
	file_server
}

example2.com {
	reverse_proxy localhost:9000
}

Se una richiesta corrisponde a più blocchi sito, viene scelto il blocco sito con l'indirizzo corrispondente più specifico. Le richieste non "cascano" in altri blocchi sito.

Direttive

Le direttive sono parole chiave funzionali che personalizzano il modo in cui il sito viene servito. Devono apparire all'interno dei blocchi sito. Ad esempio, una configurazione completa del server di file potrebbe apparire così:

localhost {
	file_server
}

Or a reverse proxy:

localhost {
	reverse_proxy localhost:9000
}

In questi esempi, file_server e reverse_proxy sono direttive. Le direttive sono la prima parola di una riga in un blocco sito.

Nel secondo esempio, localhost:9000 è un argomento perché appare sulla stessa riga dopo la direttiva.

A volte le direttive possono aprire i propri blocchi. Le sottodirettive appaiono all'inizio di ogni riga all'interno dei blocchi delle direttive:

localhost {
	reverse_proxy localhost:9000 localhost:9001 {
		lb_policy first
	}
}

Qui, lb_policy è una sottodirettiva di reverse_proxy (imposta la policy di bilanciamento del carico da usare tra i backend).

Salvo diversa documentazione, le direttive non possono essere usate all'interno di altri blocchi direttiva. Ad esempio, basic_auth non può essere usata all'interno di file_server perché il file server non sa come gestire l'autenticazione; ma potete usare le direttive all'interno dei blocchi route, handle e handle_path perché sono specificamente progettati per raggruppare le direttive.

Si noti che quando l'HTTP Caddyfile viene adattato, le direttive degli handler HTTP vengono ordinate secondo uno specifico ordine delle direttive predefinito, a meno che non si trovino in un blocco route; pertanto, l'ordine di apparizione delle direttive non ha importanza, tranne che nei blocchi route.

Token e virgolette

Il Caddyfile viene analizzato lessicalmente in token prima di essere processato. Lo spazio bianco è significativo nel Caddyfile, perché i token sono separati proprio dagli spazi bianchi.

Spesso le direttive si aspettano un certo numero di argomenti; se un singolo argomento ha un valore contenente spazi bianchi, verrebbe interpretato come due token separati:

directive abc def

Ciò potrebbe essere problematico e restituire errori o comportamenti imprevisti.

Se abc def deve essere il valore di un singolo argomento, deve essere racchiuso tra virgolette:

directive "abc def"

Le virgolette possono essere precedute da un carattere di escape se avete bisogno di usare virgolette all'interno di token già virgolettati:

directive "\"abc def\""

Per evitare l'escape delle virgolette, potete invece usare gli accenti gravi per racchiudere i token; per esempio:

directive `{"foo": "bar"}`

All'interno dei token virgolettati, tutti gli altri caratteri sono trattati letteralmente, inclusi spazi, tabulazioni e ritorni a capo. Sono quindi possibili token su più righe:

directive "prima riga
	seconda riga"

Sono supportati anche gli heredoc:

example.com {
	respond <<HTML
		<html>
		  <head><title>Foo</title></head>
		  <body>Foo</body>
		</html>
		HTML 200
}

Il marcatore di apertura dell'heredoc deve iniziare con <<, seguito da qualsiasi testo (consigliate le lettere maiuscole). Il marcatore di chiusura dell'heredoc deve essere lo stesso testo (nell'esempio sopra, HTML). Il marcatore di apertura può essere preceduto da un escape \<< per impedire l'interpretazione dell'heredoc, se necessario.

Il marcatore di chiusura può essere indentato, il che fa sì che ogni riga di testo venga privata di quella quantità di indentazione (ispirato da PHP), il che è ottimo per la leggibilità all'interno dei blocchi garantendo al contempo un ottimo controllo degli spazi bianchi nel testo del token. Anche il ritorno a capo finale viene rimosso, ma può essere mantenuto aggiungendo una riga vuota extra prima del marcatore di chiusura.

Ulteriori token possono seguire il marcatore di chiusura come argomenti della direttiva (come nell'esempio sopra, il codice di stato 200).

Opzioni globali

Un Caddyfile può opzionalmente iniziare con uno speciale blocco senza chiavi, chiamato blocco opzioni globali:

{
	...
}

Se presente, deve essere il primissimo blocco nella configurazione.

Viene usato per impostare opzioni che si applicano globalmente, o non a un particolare sito. Al suo interno possono essere impostate solo le opzioni globali; non è possibile usare le normali direttive dei siti.

Ad esempio, per abilitare l'opzione globale debug, che viene comunemente usata per produrre log dettagliati per la risoluzione dei problemi:

{
	debug
}

Leggete la pagina delle Opzioni Globali per saperne di più.

Indirizzi

Un indirizzo appare sempre all'inizio del blocco sito, ed è solitamente la prima cosa nel Caddyfile.

Questi sono esempi di indirizzi validi:

Indirizzo Effetto
example.com HTTPS con certificato pubblicamente affidabile gestito
*.example.com HTTPS con certificato wildcard pubblicamente affidabile gestito
localhost HTTPS con certificato affidabile localmente gestito
http:// HTTP catch-all, influenzato da http_port
https:// HTTPS catch-all, influenzato da https_port
http://example.com HTTP esplicitamente, con un matcher Host
example.com:443 HTTPS dovuto alla corrispondenza con il valore predefinito di https_port
:443 HTTPS catch-all dovuto alla corrispondenza con il valore predefinito di https_port
:8080 HTTP su porta non standard, nessun matcher Host
localhost:8080 HTTPS su porta non standard, dovuto alla presenza di un dominio valido
https://example.com:443 HTTPS, ma avere sia https:// che :443 è ridondante
127.0.0.1 HTTPS, con un certificato IP affidabile localmente
http://127.0.0.1 HTTP, con un matcher Host dell'indirizzo IP (rifiuta localhost)

Dall'indirizzo, Caddy può potenzialmente dedurre lo schema, l'host e la porta del vostro sito. Se l'indirizzo è senza porta, il Caddyfile sceglierà la porta corrispondente allo schema se specificato, oppure verrà assunta la porta predefinita 443.

Se specificate un hostname, verranno accettate solo le richieste con un header Host corrispondente. In altre parole, se l'indirizzo del sito è localhost, Caddy non accetterà le richieste verso 127.0.0.1.

Le wildcard (*) possono essere utilizzate, ma solo per rappresentare precisamente una etichetta dell'hostname. Ad esempio, *.example.com corrisponde a foo.example.com ma non a foo.bar.example.com, e * corrisponde a localhost ma non a example.com. Consultate il pattern dei certificati wildcard per un esempio pratico.

Per intercettare tutti gli host, omettete la porzione dell'host nell'indirizzo, ad esempio semplicemente https://. Questo è utile quando si usa il TLS on-demand, ovvero quando non conoscete i domini in anticipo.

Se più siti condividono la stessa definizione, potete elencarli tutti insieme, separati da spazi e virgole (è necessario almeno uno spazio). I seguenti tre esempi sono equivalenti:

# Indirizzi sito separati da virgola
localhost:8080, example.com, www.example.com {
	...
}

oppure

# Indirizzi sito separati da spazio
localhost:8080 example.com www.example.com {
	...
}

oppure

# Indirizzi sito separati da virgola e nuova riga
localhost:8080,
example.com,
www.example.com {
	...
}

Un indirizzo deve essere unico; non potete specificare lo stesso indirizzo più di una volta.

I placeholder non possono essere usati negli indirizzi, ma potete usare al loro interno le variabili d'ambiente in stile Caddyfile:

{$DOMAIN:localhost} {
	...
}

Per impostazione predefinita, i siti si associano (bind) a tutte le interfacce di rete. Se desiderate sovrascrivere questo comportamento, usate la direttiva bind o l'opzione globale default_bind.

Matcher

Le direttive degli handler HTTP si applicano a tutte le richieste per impostazione predefinita (salvo diversa documentazione).

I matcher di richiesta possono essere usati per classificare le richieste in base a determinati criteri. Con i matcher, potete specificare esattamente a quali richieste si applica una determinata direttiva.

Per le direttive che supportano i matcher, il primo argomento dopo la direttiva è il token matcher. Ecco alcuni esempi:

root *           /var/www  # token matcher: *
root /index.html /var/www  # token matcher: /index.html
root @post       /var/www  # token matcher: @post

I token matcher possono essere omessi del tutto per far corrispondere tutte le richieste; ad esempio, * non deve essere fornito se l'argomento successivo non assomiglia a un matcher di percorso.

Leggete la pagina dei Matcher di richiesta per saperne di più.

Placeholder

I placeholder sono un modo semplice per iniettare valori dinamici nella vostra configurazione statica. Possono essere usati come argomenti di direttive e sottodirettive.

I placeholder sono delimitati su entrambi i lati da parentesi graffe { } e contengono l'identificatore all'interno, ad esempio: {foo.bar}. La parentesi graffa di apertura del placeholder può essere preceduta da un escape \{come.questo} per evitarne la sostituzione. Gli identificatori dei placeholder sono tipicamente organizzati in namespace con punti per evitare collisioni tra i moduli.

Quali placeholder siano disponibili dipende dal contesto. Non tutti i placeholder sono disponibili in tutte le parti della configurazione. Ad esempio, l'app HTTP imposta dei placeholder che sono disponibili solo nelle aree della configurazione relative alla gestione delle richieste HTTP (ovvero nelle direttive e nei matcher degli handler HTTP, ma non nella configurazione tls). Alcune direttive o matcher possono impostare i propri placeholder che possono essere usati da tutto ciò che li segue. Alcuni placeholder sono disponibili globalmente.

Potete usare qualsiasi placeholder nel Caddyfile, ma per comodità potete anche usare alcune di queste scorciatoie equivalenti che vengono espanse quando il Caddyfile viene analizzato:

Caddyfile Sostituisce
{cookie.*} {http.request.cookie.*}
{client_ip} {http.vars.client_ip}
{dir} {http.request.uri.path.dir}
{err.*} {http.error.*}
{file_match.*} {http.matchers.file.*}
{file.base} {http.request.uri.path.file.base}
{file.ext} {http.request.uri.path.file.ext}
{file} {http.request.uri.path.file}
{header.*} {http.request.header.*}
{host} {http.request.host}
{hostport} {http.request.hostport}
{labels.*} {http.request.host.labels.*}
{method} {http.request.method}
{orig_method} {http.request.orig_method}
{orig_uri} {http.request.orig_uri}
{orig_path} {http.request.orig_uri.path}
{orig_dir} {http.request.orig_uri.path.dir}
{orig_file} {http.request.orig_uri.path.file}
{orig_query} {http.request.orig_uri.query}
{orig_?query} {http.request.orig_uri.prefixed_query}
{path.*} {http.request.uri.path.*}
{path} {http.request.uri.path}
{%path} {http.request.uri.path_escaped}
{port} {http.request.port}
{query.*} {http.request.uri.query.*}
{query} {http.request.uri.query}
{%query} {http.request.uri.query_escaped}
{?query} {http.request.uri.prefixed_query}
{re.*} {http.regexp.*}
{remote_host} {http.request.remote.host}
{remote_port} {http.request.remote.port}
{remote} {http.request.remote}
{rp.*} {http.reverse_proxy.*}
{resp.*} {http.intercept.*}
{scheme} {http.request.scheme}
{tls_cipher} {http.request.tls.cipher_suite}
{tls_client_certificate_der_base64} {http.request.tls.client.certificate_der_base64}
{tls_client_certificate_pem} {http.request.tls.client.certificate_pem}
{tls_client_fingerprint} {http.request.tls.client.fingerprint}
{tls_client_issuer} {http.request.tls.client.issuer}
{tls_client_serial} {http.request.tls.client.serial}
{tls_client_subject} {http.request.tls.client.subject}
{tls_version} {http.request.tls.version}
{upstream_hostport} {http.reverse_proxy.upstream.hostport}
{uri} {http.request.uri}
{%uri} {http.request.uri_escaped}
{vars.*} {http.vars.*}

Non tutti i campi della configurazione supportano i placeholder, ma la maggior parte lo fa dove ci si aspetterebbe. Il supporto per i placeholder deve essere stato aggiunto esplicitamente a quei campi. Gli autori di plugin possono leggere questo articolo per imparare come aggiungere il supporto per i placeholder nei propri moduli.

Snippet

Potete definire dei blocchi speciali chiamati snippet dando loro un nome racchiuso tra parentesi:

(logging) {
	log {
		output file /var/log/caddy.log
		format json
	}
}

Potete poi riutilizzarli ovunque ne abbiate bisogno, usando la speciale direttiva import:

example.com {
	import logging
}

www.example.com {
	import logging
}

La direttiva import può essere usata anche per includere altri file al suo posto. Se l'argomento non corrisponde a uno snippet definito, verrà provato come file. Supporta anche i glob per importare più file. Come caso speciale, può apparire in qualsiasi punto del Caddyfile (tranne che come argomento di un'altra direttiva), inclusi i punti all'esterno dei blocchi sito:

{
	email admin@example.com
}

import sites/*

Potete passare argomenti a una configurazione importata (snippet o file) e usarli in questo modo:

(snippet) {
	respond "Yahaha! Hai trovato {args[0]}!"
}

a.example.com {
	import snippet "Esempio A"
}

b.example.com {
	import snippet "Esempio B"
}

⚠️ Sperimentale | v2.9.x+

Potete anche passare un blocco opzionale a uno snippet importato, e usarli come segue:

(snippet) {
	{block}
	respond "OK"
}

a.example.com {
	import snippet {
		header +foo bar
	}
}

b.example.com {
	import snippet {
		header +bar foo
	}
}

Leggete la pagina della direttiva import per saperne di più.

Rotte nominate

⚠️ Sperimentale

Le rotte nominate usano una sintassi simile agli snippet; sono dei blocchi speciali definiti all'esterno dei blocchi sito, preceduti da &( e terminanti con ), con il nome nel mezzo.

&(app-proxy) {
	reverse_proxy app-01:8080 app-02:8080 app-03:8080
}

Potete poi riutilizzare questa rotta nominata all'interno di qualsiasi sito:

example.com {
	invoke app-proxy
}

www.example.com {
	invoke app-proxy
}

Questo è particolarmente utile per ridurre l'uso della memoria se la stessa rotta è necessaria in molti siti diversi, o se sono necessarie più condizioni di matcher diverse per invocare la stessa rotta.

Leggete la pagina della direttiva invoke per saperne di più.

Commenti

I commenti iniziano con # e proseguono fino alla fine della riga:

# I commenti possono iniziare una riga
directive  # oppure andare alla fine

Il carattere cancelletto # per un commento non può apparire nel mezzo di un token (ovvero deve essere preceduto da uno spazio o apparire all'inizio di una riga). Ciò consente l'uso dei cancelletti all'interno degli URI o di altri valori senza richiedere la virgolettatura.

Variabili d'ambiente

Se la vostra configurazione si affida alle variabili d'ambiente, potete usarle nel Caddyfile:

{$ENV}

Le variabili d'ambiente in questa forma vengono sostituite prima che inizi l'analisi del Caddyfile, quindi possono espandersi in valori vuoti (ovvero ""), token parziali, token completi o persino più token e righe.

Ad esempio, una variabile d'ambiente UPSTREAMS="app1:8080 app2:8080 app3:8080" si espanderebbe in più token:

example.com {
	reverse_proxy {$UPSTREAMS}
}

È possibile specificare un valore predefinito per quando la variabile d'ambiente non viene trovata, usando : come delimitatore tra il nome della variabile e il valore predefinito:

{$DOMAIN:localhost} {

}

Se volete posticipare la sostituzione di una variabile d'ambiente fino al runtime, potete usare i placeholder standard {env.*}. Si noti che non tutti i parametri della configurazione supportano questi placeholder, poiché gli sviluppatori di moduli devono aggiungere una riga di codice per eseguire la sostituzione. Se sembra non funzionare, aprite una issue per richiederne il supporto.

Ad esempio, se avete installato il plugin caddy-dns/cloudflare e desiderate configurare la sfida DNS, potete passare la variabile d'ambiente CLOUDFLARE_API_TOKEN al plugin in questo modo:

{
	acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

Se state eseguendo Caddy come servizio systemd, consultate queste istruzioni per impostare gli override del servizio al fine di definire le vostre variabili d'ambiente.