Repository navigation
Expand file tree
/
Copy pathCaddyfile
More file actions
202 lines (178 loc) · 8 KB
/
Copy pathCaddyfile
File metadata and controls
202 lines (178 loc) · 8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# ─────────────────────────────────────────────────────────────────────────
# Reverse proxy GLOBAL — il sert TOUTES les apps du VPS. Un Caddyfile cassé
# ici tombe TOUS les sites d'un coup. On le modifie par git, jamais à la main
# sur le serveur.
#
# Déployer un changement : éditer → commit → push → sur le VPS : ./proxy-deploy.sh
# (valide le nouveau Caddyfile, PUIS recrée le conteneur caddy)
#
# Pourquoi recréer et pas reload : le Caddyfile est un bind-mount single-file.
# L'éditer crée un nouvel inode que le conteneur en cours ne suit pas — donc
# `caddy reload` (et même `docker compose up -d` seul) continuent de servir
# l'ANCIENNE config. Seul `docker compose up -d --force-recreate caddy` (ce que
# fait proxy-deploy.sh) re-bind le nouveau fichier. Si tu édites quand même sur
# le serveur, lance cette commande — jamais un simple reload.
# ─────────────────────────────────────────────────────────────────────────
{
# Email pour Let's Encrypt (renouvellement automatique)
email {$LETSENCRYPT_EMAIL}
}
# ── Snippet headers de sécurité globaux ────────────────────────────────
(security_headers) {
header {
# HSTS 1 an, inclut sous-domaines, preload-ready
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Anti MIME-sniffing
X-Content-Type-Options "nosniff"
# Anti-clickjacking
X-Frame-Options "SAMEORIGIN"
# Referrer policy raisonnable
Referrer-Policy "strict-origin-when-cross-origin"
# Permissions policy minimaliste (pas de mic/camera/geoloc par défaut)
Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Masque la stack
-Server
}
}
# ── redflagbingo.fun ───────────────────────────────────────────────────
redflagbingo.fun {
import security_headers
encode zstd gzip
reverse_proxy rfb_app:80 {
# X-Real-IP n'est pas posé par Caddy par défaut → override délibéré.
# (X-Forwarded-For/Proto/Host sont ajoutés automatiquement par Caddy.)
header_up X-Real-IP {remote_host}
}
}
# ── humelis.labault.dev ────────────────────────────────────────────
humelis.labault.dev {
import security_headers
encode zstd gzip
reverse_proxy humelis_app:80 {
header_up X-Real-IP {remote_host}
}
}
# ── labault.dev ────────────────────────────────────────────
labault.dev {
import security_headers
root * /srv/portfolio
file_server
# Compression gzip/brotli (perf sur le HTML lourd)
encode gzip
# Cache navigateur (optionnel, utile pour les fonts Google)
header /index.html Cache-Control "no-cache, must-revalidate"
}
# Redirection www → non-www
www.labault.dev {
redir https://labault.dev{uri} permanent
}
# ── hush.labault.dev ────────────────────────────────────────────
hush.labault.dev {
encode gzip zstd
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), camera=(), microphone=()"
-Server
}
reverse_proxy hush-web-prod:80 {
header_up X-Real-IP {remote_host}
}
log {
output file /var/log/caddy/hush.log {
roll_size 10mb
roll_keep 5
}
format console
}
}
# ── devspeak.labault.dev ────────────────────────────────────────────
devspeak.labault.dev {
reverse_proxy devspeak:4321
encode gzip zstd
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
# ── lolv.labault.dev ────────────────────────────────────────────
lolv.labault.dev {
import security_headers
encode zstd gzip
reverse_proxy lolv_wp_prod:80
}
# ── Vlad ───────────────────────────────────────────────────────────────────────
shanna.labault.dev {
import security_headers
encode zstd gzip
@vlad_assets path_regexp vlad_assets ^/wp-content/(?:themes/vlad|plugins/vlad)/.*\.(?:avif|css|gif|jpe?g|js|png|svg|webp|woff2?)$
header @vlad_assets Cache-Control "public, max-age=31536000, immutable"
@vlad_uploads path_regexp vlad_uploads ^/wp-content/uploads/.*\.(?:avif|gif|jpe?g|png|svg|webp)$
header @vlad_uploads Cache-Control "public, max-age=2592000"
reverse_proxy vlad_wp_prod:80 {
header_up X-Real-IP {remote_host}
}
}
# ── tibec.labault.dev ────────────────────────────────────────────
tibec.labault.dev {
import security_headers
reverse_proxy duck_app:80 {
# X-Real-IP n'est pas posé par Caddy par défaut → override délibéré.
# (X-Forwarded-For/Proto/Host sont ajoutés automatiquement par Caddy.)
header_up X-Real-IP {remote_host}
}
}
# ── whykipedia.xyz ────────────────────────────────────────────────────
whykipedia.xyz {
import security_headers
encode zstd gzip
reverse_proxy whykipedia_app:80 {
header_up X-Real-IP {remote_host}
}
}
# Redirection www → non-www
www.whykipedia.xyz {
redir https://whykipedia.xyz{uri} permanent
}
# ── stats.labault.dev ────────────────────────────────────────────
stats.labault.dev {
encode zstd gzip
reverse_proxy umami:3000 {
health_uri /api/heartbeat
health_interval 10s
health_timeout 2s
}
}
# ── deploy.labault.dev (listener de déploiement) ─────────────────────
deploy.labault.dev {
import security_headers
reverse_proxy webhook:9000
}
# ── app.tartory.com, pro.tartory.com ────────────────────────────────────
# PAS d'import security_headers ici, volontairement : l'app envoie déjà sa propre
# politique, plus stricte (CSP comprise). La directive `header` de Caddy REMPLACE,
# donc importer le snippet écraserait camera=(self) par camera=() (scanner de
# code-barres HS), same-origin par strict-origin-when-cross-origin, et DENY par
# SAMEORIGIN. Pas de bloc `log` non plus : les liens de reset portent un token en
# clair dans l'URL, que l'app filtre dans SES logs et que le proxy écrirait en
# clair dans les siens. Voir docs/DEPLOY.md du repo Tartory.
app.tartory.com, pro.tartory.com {
encode zstd gzip
reverse_proxy tartory_web:80 {
header_up X-Real-IP {remote_host}
}
}
# ── tartory.com (domaine nu) ────────────────────────────────────────────
# Le nom résout vers le VPS (A + AAAA). Sans ce bloc, Caddy ne le connaît pas et
# le visiteur se prend une erreur TLS, ce qui est pire qu'une page vide.
# Sans le snippet non plus : son HSTS porte `preload` et `includeSubDomains`, ce que
# Tartory refuse délibérément (l'inscription à la liste des navigateurs est presque
# irréversible, et vaudrait alors pour app. et pro.). Une 301 n'a besoin de rien d'autre.
tartory.com {
redir https://app.tartory.com{uri} permanent
}