Caddy: Geografisch Filter
In mijn vorige post Vaultwarden hosting en beveiligen schreef ik over de beveiliging van Vaultwarden. Op basis van een IP filter en Fail2Ban beperk ik de toegang tot de wachtwoordkluis Vaultwarden.
Uitgangspunt
Vaultwarden draait standaard in een Docker container. Ik maak geen gebruik van Docker - maar van de Vaultwarden packages onder Arch Linux. Er zijn ook packages beschikbaar voor andere Linux distributies. Ik heb gekozen voor Arch Linux omdat de packages goed worden onderhouden. Informatie over de Arch Linux packages staan op de volgende pagina: https://archlinux.org/packages/community/x86_64/vaultwarden/.
Vereisten
De vereisten voor Geo Filtering met Caddy zijn:
- GeoIPUpdate: /usr/bin/geoipupdate
- Maxmind GeoLite account + license key: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
- Caddy inclusief caddy-maxmind-geolocation
GeoIPUpdate kan geïnstalleerd worden met de standaard package manager onder Linux. Ik gebruik de Vaultwarden packages (vaultwarden + vaultwarden-web) direct onder Arch Linux. Ik heb geoipupdate
geïnstalleerd met het commando pacman -Sy geoipupdate
.
Een Maxmind GeoLite account en licentiesleutel kan verkregen worden via de hierboven weergegeven link. De directe URL naar het inschrijfformulier is: https://www.maxmind.com/en/geolite2/signup.
De module caddy-maxmind-geolocation kan toegevoegd worden aan caddy door gebruik te maken van xcaddy.
Toevoegen module(s) aan Caddy
Ik heb een map src
aangemaakt onder mijn $HOME directory. Vanuit de map src
heb ik het volgende commando uitgevoerd:
$ ../go/bin/xcaddy build \
--with github.com/caddyserver/transform-encoder \
--with github.com/porech/caddy-maxmind-geolocation
Ik heb als eerste de module transform-encoder
ingevoerd - omdat ik die module nodig heb naast caddy-maxmind-geolocation
.
Vervolgens heb ik de onderstaande commando's uitgevoerd:
- overschakelen naar root - gebruik
sudo
indien je geen wachtwoord op userroot
hebt gezet (zoals onder Ubuntu) - kopieer originele caddy file (reservekopie) - let op: vul voor
username
de eigen gebruikersnaam in - stop caddy service
- overschrijf caddy file met nieuwe caddy file - let op: vul voor
username
de eigen gebruikersnaam in - start caddy
$ su -
# cp /usr/bin/caddy /home/username/caddy_org
# systemctl stop caddy
# cp /home/username/src/caddy /usr/bin/caddy
# systemctl start caddy
Configuratie
GeoIP
Vergeet niet om de geografische databases op te halen. Het commando /usr/bin/geoipupdate
maakt gebruik van de config file /etc/GeoIP.conf
en plaatst de databases in de DatabaseDirectory
. Onder Arch Linux gaat het om de map /var/lib/GeoIP
.
Ik heb onder Arch Linux cronie
geïnstalleerd zodat ik conjobs kan gebruiken. Vervolgens heb ik een cronjob aangemaakt met het commando crontab -e
:
# pacman -Sy cronie
# export EDITOR=nano
# crontab -e
Het gaat om de volgende regel:
0 3 * * 3 /usr/bin/geoipupdate >/dev/nul 2>&1
Het is handig om /usr/bin/geoipupdate -v
als root user uit te voeren. Hiermee wordt handmatig een update uitgevoerd met geoipupdate met uitgebreide uitvoer (verbose).
caddy
De configuratie van Caddy staat in de map /etc/caddy
. Voor Vaultwarden heb ik drie config files: Caddyfile
, caddy_security.conf
en conf.d/psw01.domeinnaam.nl
. Uiteraard is domeinnaam.nl
niet de echte domeinnaam. Ik kan in dit voorbeeld niet de echte domeinnaam gebruiken.
File: /etc/caddy/Caddyfile
# The Caddyfile is an easy way to configure your Caddy web server.
#
# https://caddyserver.com/docs/caddyfile
#
# The configuration below serves a welcome page over HTTP on port 80.
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace the line below with your
# domain name.
#
# https://caddyserver.com/docs/caddyfile/concepts#addresses
{
# Restrict the admin interface to a local unix file socket whose directory
# is restricted to caddy:caddy. By default the TCP socket allows arbitrary
# modification for any process and user that has access to the local
# interface. If admin over TCP is turned on one should make sure
# implications are well understood.
admin "unix//run/caddy/admin.socket"
}
# Import additional caddy config files in /etc/caddy/conf.d/
import /etc/caddy/conf.d/*
File: /etc/caddy/caddy_security.conf
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Xss-Protection "1; mode=block"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Content-Security-Policy "upgrade-insecure-requests"
Referrer-Policy "strict-origin-when-cross-origin"
Cache-Control "public, max-age=15, must-revalidate"
Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'self'; camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture *; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'"
}
File: /etc/caddy/conf.d/psw01.domeinnaam.nl
(doRevProx) {
# The negotiation endpoint is also proxied to Rocket
reverse_proxy /notifications/hub/negotiate localhost:8000
# Notifications redirected to the websockets server
reverse_proxy /notifications/hub localhost:3012
### Credits: https://community.hetzner.com/tutorials/how-to-set-up-vaultwarden
# Send all other traffic to the regular Vaultwarden endpoint
reverse_proxy * localhost:8000 {
header_up X-Real-IP {remote_host}
}
# Remove server response header
header /* {
-Server
}
}
psw01.domeinnaam.nl {
# General settings
encode gzip
file_server
# Security
import /etc/caddy/caddy_security.conf
### Geo Filtering ################
@geofilter {
maxmind_geolocation {
db_path "/var/lib/GeoIP/GeoLite2-Country.mmdb"
allow_countries NL
}
}
handle @geofilter {
import doRevProx
}
### White Listing ################
@allowed {
remote_ip forwarded 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
}
handle @allowed {
import doRevProx
}
respond "Access denied - 403" 403
# Logging
log {
format transform "{common_log}"
# access logs
output file /var/log/caddy/psw01.domeinnaam.nl/access.log {
roll_size 100MiB
roll_keep 90
roll_keep_for 2160h
level INFO
}
# error logs
output file /var/log/caddy/psw01.domeinnaam.nl/errors.log {
roll_size 100MiB
roll_keep 90
roll_keep_for 2160h
level ERROR
}
}
}
Met allow_countries NL
in het blok @geofilter
wordt er voor gezorgd dat alleen vanaf Nederlandse IP adressen toegang wordt verleend.
Met remote_ip forwarded 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
in het blok @allowed
wordt er voor gezorgd dat private IPv4 adressen (RFC1918) en CGNAT IPv4 adressen (RFC6598) toe worden gestaan. In de meeste IPv4 LAN's zal RFC1918 voldoende zijn - maar in sommige omgevingen wordt RFC6598 gebruikt (dit is een uitzondering).
Overige notities
Hieronder staan nog een aantal notities.
Log Files
De logbestanden voor Caddy moeten bestaan. Het gaat om errors.log
en access.log
. Bijvoorbeeld /var/log/caddy/psw01.domeinnaam.nl/errors.log
voor de error log die wordt gebruikt door Fail2Ban. Het bestand access.log
wordt in principe niet gebruikt.
Remove server response header
Ik heb het gedeelte Remove server response header
toegevoegd aan de Caddy configuratie om er voor te zorgen dat niet zichtbaar is dat Caddy wordt gebruikt. Ik vind het niet nodig dat dergelijke gegevens zichtbaar zijn in scans. Laat een eventuele hacker maar gissen naar wat er gebruikt wordt.
Firewall
Het is onverantwoord om geen firewall te gebruiken! Ik gebruik UFW (Uncomplicated Firewall) onder Arch Linux. Daarnaast zit de VM met Vaultwarden achter een hardwarematige firewall. Ik heb (nog) geen beschrijving gemaakt voor UFW maar in een notendop komt het erop neer dat ik UFW actief gemaakt heb en de benodigde poorten open heb gezet. In /etc/ufw/ufw.conf
moet achter ENABLED=
de waarde yes
ingesteld staan. Vervolgens kan UFW ingeschakeld worden door middel van systemctl enable --now ufw
en gestart worden door middel van systemctl start ufw
. Werk je via SSH, dan kan SSH open gezet worden met het commando ufw prepend allow ssh
of ufw prepend allow 22/tcp
. Ik gebruik prepend
om er voor te zorgen dat de regel bovenaan de lijst komt te staan. Met het commando ufw status numbered
kun je de toegevoegde regels zien. Heb je spijt van een regel en wil je die verwijderen, dan kan dat doen met het commando ufw delete
gevolgd door het regelnummer (wat je vindt met het commando ufw status numbered
).
Denk voor Caddy/Vaultwarden aan poort 443 voor https en poort 22 voor SSH. Poort 22 wordt in mijn opzet beperkt door de hardwarematige firewall tot bepaalde source IP's.
Fail2Ban
Ik gebruik Fail2Ban om brute force pogingen tegen te werken. Zie: Vaultwarden hosting en beveiligen voor meer informatie. Om brute force pogingen op Caddy en SSH tegen te gaan, kan de volgende jail.local
gebruikt worden.
File: jail.local
(deze file staat op mijn Arch Linux VM in de map /etc/fail2ban/jail.d/
).
[DEFAULT]
enabled = false
ignoreip = 127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
[caddy]
enabled = true
port = http,https,8000
logpath = /var/log/caddy/*/errors.log
maxretry = 3
findtime = 28800
bantime = 7200
[sshd]
bantime = 31104000
findtime = 86400
maxentry = 1
De variabel ignoreip
kan aangevuld worden met meerdere IP's. Bijvoorbeeld publieke IP's waarmee je verbinding maakt via SSH. Het eigen publieke IP wordt dan niet geblokkeerd bij een foutje tijdens het inloggen.
Het is verstandig om Fail2Ban te gebruiken maar mijn advies is om niet alleen op Fail2Ban te vertrouwen. De Fail2Ban service kan ook eens falen - of door een configuratiefout niet actief zijn. Een optie is om met UFW de toegang tot de SSH poort te beperken. Bijvoorbeeld met het commando ufw prepend allow from 178.22.85.148 proto tcp to any port 22
. Het IP is een voorbeeld - maar met het commando wordt verkeer naar poort 22/tcp (SSH) toegestaan vanaf 178.22.85.148. Het is dan wel verstandig om de globale regel te verwijderen, indien toegang tot SSH reeds open stond...