Pare-feu : exemple de VPS avec Caddy
Configuration de bout en bout d’un VPS Ubuntu (24.04 ou 26.04) qui héberge une application web derrière Caddy. Noms génériques : domaine exemple.com, utilisateur deploy, application monapp sur le port 3000, poste d’administration 203.0.113.10.
Architecture
Section intitulée « Architecture »Internet ──443/80──▶ Caddy (HTTPS, en-têtes, journaux) ──127.0.0.1:3000──▶ monapp (service systemd) ▲ UFW (22, 80, 443) + Fail2ban (sshd, caddy-auth, caddy-sondes)- Seuls SSH, 80 et 443 sont ouverts.
- L’application écoute sur
127.0.0.1. - Caddy gère HTTPS, les en-têtes de sécurité et les journaux.
- Fail2ban bannit les adresses qui échouent ou qui scannent.
Prérequis
Section intitulée « Prérequis »- Un VPS Ubuntu, une adresse IP publique, un accès initial
rootousudo. - Un domaine dont l’enregistrement
A(etAAAA) pointe vers le VPS. - Une clé SSH sur le poste d’administration (voir SSH : clés).
1. Mises à jour et compte d’administration
Section intitulée « 1. Mises à jour et compte d’administration »sudo apt update && sudo apt full-upgrade -ysudo adduser deploysudo usermod -aG sudo deployVoir Ubuntu Server : actions après l’installation.
2. SSH par clés
Section intitulée « 2. SSH par clés »Depuis le poste d’administration :
ssh-copy-id deploy@exemple.comssh deploy@exemple.comSur le VPS, en gardant une session ouverte :
sudo tee /etc/ssh/sshd_config.d/00-durcissement.conf <<'CONF'PermitRootLogin noPasswordAuthentication noKbdInteractiveAuthentication noAuthenticationMethods publickeyMaxAuthTries 3AllowUsers deployCONFsudo sshd -t && sudo systemctl reload sshTester une nouvelle connexion avant de fermer la session. Voir SSH : durcissement du serveur.
3. Pare-feu UFW
Section intitulée « 3. Pare-feu UFW »sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw limit OpenSSHsudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw allow 443/udpsudo ufw enableVoir UFW : prise en main. Si l’application tourne dans Docker, la publier sur 127.0.0.1 (UFW et Docker).
4. L’application en service systemd
Section intitulée « 4. L’application en service systemd »[Unit]Description=Application monappAfter=network.target
[Service]User=monappGroup=monappWorkingDirectory=/srv/monapp/appEnvironmentFile=/etc/monapp.envExecStart=/usr/bin/node src/serveur.jsRestart=on-failureRestartSec=3NoNewPrivileges=truePrivateTmp=trueProtectSystem=strictProtectHome=trueReadWritePaths=/srv/monapp
[Install]WantedBy=multi-user.targetsudo systemctl daemon-reloadsudo systemctl enable --now monappcurl -I http://127.0.0.1:3000/sudo ss -tlnp | grep ':3000'Voir systemd : écrire un service et durcissement.
5. Caddy
Section intitulée « 5. Caddy »Installation depuis le dépôt officiel (voir Caddy : installation) :
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curlcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.listsudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.listsudo apt update && sudo apt install caddysudo mkdir -p /var/log/caddy && sudo chown caddy:caddy /var/log/caddy/etc/caddy/Caddyfile :
{ email admin@exemple.com}
(securite) { header { Strict-Transport-Security "max-age=31536000" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" -Server }}
exemple.com { import securite encode zstd gzip
log { output file /var/log/caddy/acces.log { roll_size 10MiB roll_keep 5 } format json }
basic_auth /admin/* { admin HASH_GENERE_PAR_CADDY_HASH_PASSWORD }
reverse_proxy 127.0.0.1:3000}
www.exemple.com { redir https://exemple.com{uri} permanent}Générer le hash du mot de passe (saisie masquée) et le coller dans le Caddyfile :
caddy hash-passwordValider et appliquer :
sudo -u caddy caddy validate --config /etc/caddy/Caddyfilesudo systemctl reload caddyCaddy obtient le certificat HTTPS au premier accès, si le DNS pointe vers le VPS et si les ports 80 et 443 sont joignables. Plus d’exemples : Caddy : exemples réutilisables.
6. Fail2ban
Section intitulée « 6. Fail2ban »sudo apt install fail2banFiltres Caddy (voir Fail2ban : jails et filtres) :
sudo tee /etc/fail2ban/filter.d/caddy-auth.conf <<'CONF'[Definition]failregex = ^.*"client_ip":"<ADDR>".*"status":(?:401|403),datepattern = "ts":{EPOCH}ignoreregex =CONF
sudo tee /etc/fail2ban/filter.d/caddy-sondes.conf <<'CONF'[Definition]failregex = ^.*"client_ip":"<ADDR>".*"uri":"[^"]*(?:/\.env|/\.git|wp-login\.php|xmlrpc\.php|phpmyadmin|/admin\.php|/cgi-bin)[^"]*".*"status":404,datepattern = "ts":{EPOCH}ignoreregex =CONFJails (remplacer 203.0.113.10 par l’adresse du poste d’administration) :
sudo tee /etc/fail2ban/jail.local <<'CONF'[DEFAULT]bantime = 1hfindtime = 10mmaxretry = 5ignoreip = 127.0.0.1/8 ::1 203.0.113.10bantime.increment = true
[sshd]enabled = truemaxretry = 3mode = aggressive
[caddy-auth]enabled = trueport = http,httpsfilter = caddy-authlogpath = /var/log/caddy/acces.logmaxretry = 5
[caddy-sondes]enabled = trueport = http,httpsfilter = caddy-sondeslogpath = /var/log/caddy/acces.logmaxretry = 3bantime = 1dCONFsudo systemctl restart fail2bansudo fail2ban-client status7. Mises à jour de sécurité automatiques
Section intitulée « 7. Mises à jour de sécurité automatiques »sudo apt install unattended-upgradessudo dpkg-reconfigure -plow unattended-upgrades8. Liste de contrôle
Section intitulée « 8. Liste de contrôle »Sur le VPS :
sudo ss -tlnpsudo ufw status verbosesudo fail2ban-client statussudo systemctl is-active caddy monapp fail2bansudo journalctl -u caddy -n 20 --no-pagerSeuls 22, 80 et 443 doivent écouter publiquement ; le port 3000 reste sur 127.0.0.1.
Depuis une autre machine :
curl -I https://exemple.com/ # 200 et en-têtes de sécuritécurl -I http://exemple.com/ # redirection vers HTTPScurl -u admin:mauvais -I https://exemple.com/admin/ # 401nmap -Pn -p 22,80,443,3000,3306,5432,6379 exemple.com9. Sauvegardes
Section intitulée « 9. Sauvegardes »Sauvegarder la base de données (PostgreSQL, MariaDB, SQLite), les fichiers de l’application et /etc, tester la restauration et copier hors du VPS.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
| Pas de certificat HTTPS | DNS non propagé, ports 80/443 fermés (UFW, hébergeur) : sudo journalctl -u caddy -n 50 |
Caddy échoue avec permission denied |
sudo chown caddy:caddy /var/log/caddy/* |
502 Bad Gateway |
Application arrêtée ou mauvais port : systemctl status monapp |
| Plus d’accès SSH après le durcissement | Console : retirer /etc/ssh/sshd_config.d/00-durcissement.conf (voir SSH : dépannage) |
| Fail2ban ne bannit pas depuis le serveur | ignoreself : essayer depuis une autre machine |
| Même adresse pour tous les visiteurs | Caddy derrière un proxy : trusted_proxies |
| Port 3000 joignable de l’extérieur | L’application écoute sur 0.0.0.0 : la faire écouter sur 127.0.0.1 |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Les étapes sont identiques sur les deux versions. À noter sur 26.04 : OpenSSH 10 journalise sous sshd-session, PerSourcePenalties est actif par défaut et /tmp est en mémoire.