Pembatasan IP #
Pembatasan akses berdasarkan IP address adalah salah satu mekanisme keamanan yang paling langsung dan efektif. Caddy menyediakan matcher remote_ip yang memungkinkan kamu membatasi atau mengizinkan akses berdasarkan IP atau range IP klien — dapat diterapkan untuk seluruh site maupun path tertentu.
Matcher remote_ip
#
Matcher remote_ip mencocokkan IP address klien dengan daftar yang kamu tentukan, mendukung notasi CIDR untuk range IP:
example.com {
# Blokir akses dari IP atau range tertentu
@blocked remote_ip 203.0.113.0/24 198.51.100.5
respond @blocked 403
file_server {
root /var/www/html
}
}
Notasi yang didukung:
Single IP: 192.168.1.1
IPv6: 2001:db8::1
CIDR range: 192.168.0.0/16 (semua IP 192.168.0.0 – 192.168.255.255)
Private nets: 10.0.0.0/8 (semua IP 10.x.x.x)
172.16.0.0/12
192.168.0.0/16
Loopback: 127.0.0.0/8
::1
Allowlist — Hanya IP Tertentu yang Bisa Akses #
Pola paling umum: izinkan hanya IP yang diketahui, tolak semua yang lain.
# Batasi akses hanya dari IP tertentu
admin.example.com {
@allowed remote_ip 203.0.113.10 203.0.113.11 203.0.113.12
@blocked not remote_ip 203.0.113.10 203.0.113.11 203.0.113.12
respond @blocked 403
reverse_proxy localhost:9000
}
# Cara alternatif yang lebih bersih dengan handle
admin.example.com {
@notAllowed {
not remote_ip 203.0.113.10/32 203.0.113.0/24
}
respond @notAllowed "Access Denied" 403
reverse_proxy localhost:9000
}
Izinkan Hanya Jaringan Internal #
internal-tools.company.com {
# Hanya izinkan akses dari jaringan internal
@external {
not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.1/32
}
respond @external "Access restricted to internal network" 403
reverse_proxy internal-app:8080
}
Blocklist — Blokir IP Tertentu #
example.com {
# Blokir IP yang diketahui bermasalah
@blocked {
remote_ip 198.51.100.0/24 # Known bad actor range
remote_ip 203.0.113.50 # Specific malicious IP
}
respond @blocked 403
# Traffic normal dilanjutkan
file_server {
root /var/www/html
}
}
Pembatasan IP per Path #
Sangat berguna untuk melindungi endpoint tertentu (admin, metrics, status) tanpa mempengaruhi akses publik:
example.com {
# Admin area: hanya kantor
@admin-external {
path /admin/*
not remote_ip 203.0.113.0/24 10.0.0.0/8
}
respond @admin-external 403
# Metrics endpoint: hanya monitoring server
@metrics-external {
path /metrics /status /health-detailed
not remote_ip 10.10.0.5/32 # IP monitoring server
}
respond @metrics-external 403
# API endpoint: bisa dari mana saja (tapi rate limited)
# (tidak ada pembatasan IP untuk /api/*)
# Semua lainnya: publik
root * /var/www/html
file_server
}
Proteksi Admin Panel dengan Berlapis #
Kombinasikan IP restriction dengan basicauth untuk keamanan berlapis:
admin.example.com {
# Layer 1: Hanya dari IP yang diizinkan
@notOffice {
not remote_ip 203.0.113.0/24 10.0.0.0/8
}
respond @notOffice 403
# Layer 2: Basic auth untuk semua yang lolos IP check
basicauth {
alice $2a$14$aliceHashHere
bob $2a$14$bobHashHere
}
# Layer 3: Logging semua akses untuk audit
log {
output file /var/log/caddy/admin-access.log
format json
}
reverse_proxy localhost:9000
}
IP Restriction dengan Trusted Proxies #
Ketika Caddy berada di belakang load balancer atau CDN (Cloudflare, AWS ALB), IP yang terlihat Caddy adalah IP proxy, bukan IP klien asli. Kamu perlu mengkonfigurasi trusted proxies agar remote_ip matcher menggunakan IP klien asli dari header X-Forwarded-For:
{
servers {
# IP-IP yang dipercaya untuk header forwarding
# AWS ALB internal IP range (contoh):
trusted_proxies static 10.0.0.0/8
# Cloudflare IP ranges (cek dokumentasi Cloudflare untuk list terbaru):
# trusted_proxies static 103.21.244.0/22 103.22.200.0/22 ...
}
}
example.com {
# Setelah trusted_proxies dikonfigurasi,
# {remote_host} dan remote_ip matcher
# menggunakan IP klien asli dari X-Forwarded-For
@blocked remote_ip 198.51.100.0/24
respond @blocked 403
reverse_proxy backend:3000
}
Tanpatrusted_proxies,{remote_host}akan selalu menunjukkan IP proxy (misalnya IP Cloudflare), bukan IP klien asli. Ini membuat IP restriction tidak bekerja seperti yang diharapkan — semua request terlihat berasal dari IP proxy yang sama.
Geo-blocking dengan Pendekatan Berbeda #
Caddy tidak memiliki geo-blocking bawaan berdasarkan negara. Tapi ada beberapa pendekatan:
Menggunakan Cloudflare (Termudah) #
Jika kamu menggunakan Cloudflare sebagai CDN/proxy, geo-blocking bisa dikonfigurasi di level Cloudflare tanpa menyentuh Caddy sama sekali. Cloudflare Firewall Rules mendukung penolakan traffic dari negara tertentu.
Header dari Cloudflare #
Cloudflare menambahkan header CF-IPCountry ke setiap request:
example.com {
# Blokir request dari negara tertentu berdasarkan header Cloudflare
@blocked_countries {
header CF-IPCountry "RU"
header CF-IPCountry "CN"
}
respond @blocked_countries 403
file_server {
root /var/www/html
}
}
Menggunakan MaxMind GeoIP (Self-hosted) #
Untuk geo-blocking tanpa Cloudflare, plugin MaxMind GeoIP untuk Caddy bisa digunakan:
# Build dengan plugin MaxMind GeoIP
xcaddy build --with github.com/porech/caddy-maxmind-geolocation
example.com {
@not_id remote_ip 10.0.0.0/8 # Izinkan internal
@blocked_country {
maxmind_geolocation {
db_path /etc/caddy/GeoLite2-Country.mmdb
allow_countries ID SG MY # Izinkan Indonesia, Singapura, Malaysia
}
}
# respond @blocked_country 403
file_server
}
Logging Akses yang Diblokir #
Untuk audit dan monitoring, log semua request yang diblokir:
example.com {
@blocked remote_ip 198.51.100.0/24
handle @blocked {
# Log sebelum blokir
log {
output file /var/log/caddy/blocked.log
format json
}
respond 403
}
file_server
}
# Analisis IP yang paling banyak diblokir
cat /var/log/caddy/blocked.log | jq -r '.request.remote_addr' | \
cut -d: -f1 | sort | uniq -c | sort -rn | head -20
# Monitor real-time
tail -f /var/log/caddy/blocked.log | jq -r \
'"\(.ts) BLOCKED \(.request.remote_addr) \(.request.method) \(.request.uri)"'
Dynamic IP Blocking dengan Script #
Untuk blocking dinamis berdasarkan log analysis:
#!/bin/bash
# auto-block.sh — Analisis log dan update blocklist Caddy
LOG_FILE="/var/log/caddy/access.log"
THRESHOLD=100 # Blokir jika > 100 error dalam 10 menit
WINDOW=600 # 10 menit dalam detik
# Ambil IP dengan terlalu banyak 4xx/5xx dalam window waktu
SUSPICIOUS_IPS=$(cat "$LOG_FILE" | \
jq -r 'select(.ts > (now - '"$WINDOW"') and .status >= 400) | .request.remote_addr' | \
cut -d: -f1 | sort | uniq -c | \
awk -v t="$THRESHOLD" '$1 > t {print $2}')
if [ -n "$SUSPICIOUS_IPS" ]; then
echo "Suspicious IPs detected: $SUSPICIOUS_IPS"
# Update Caddyfile blocklist dan reload
# (implementasi tergantung struktur konfigurasi kamu)
for ip in $SUSPICIOUS_IPS; do
echo "Blocking $ip..."
# Tambahkan ke file blocklist yang di-import Caddyfile
done
sudo systemctl reload caddy
fi
Allowlist untuk CI/CD dan Deploy Webhook #
example.com {
# Endpoint webhook deploy: hanya dari GitHub Actions IP range
# (Cek IP range GitHub di docs.github.com/en/authentication/...)
@webhook-external {
path /webhooks/deploy
not remote_ip 192.30.252.0/22 185.199.108.0/22
}
respond @webhook-external 403
# Endpoint normal tetap publik
file_server { root /var/www/html }
}
Ini memastikan endpoint webhook deploy hanya bisa dipanggil dari infrastruktur GitHub, bukan dari pihak luar.
Memblokir Private Network dari Akses Publik #
Ketika kamu mengekspos layanan ke internet, penting untuk mencegah request yang mencoba menjangkau layanan internal melalui SSRF (Server-Side Request Forgery):
# Contoh: Open redirect protection
example.com {
# Jangan izinkan redirect ke IP private
# (ini lebih ke logika aplikasi, tapi Caddy bisa membantu di layer proxy)
# Blokir akses ke path yang mengarah ke metadata endpoint cloud
@metadata {
path /proxy/*
remote_ip 169.254.0.0/16 # AWS metadata service range
}
respond @metadata 403
}
Ringkasan #
- Gunakan matcher
remote_ipuntuk allowlist (hanya IP ini yang boleh) atau blocklist (IP ini diblokir).- Untuk proteksi admin panel, kombinasikan IP restriction + basicauth untuk keamanan berlapis.
- Saat Caddy berada di belakang CDN atau load balancer, konfigurasi
trusted_proxiesagarremote_ipmatcher menggunakan IP klien asli dariX-Forwarded-For.- Geo-blocking paling mudah dilakukan di level Cloudflare; untuk self-hosted gunakan plugin MaxMind GeoIP.
- Selalu log request yang diblokir untuk audit trail dan deteksi pola serangan.
- IP restriction adalah defense in depth — gunakan bersama rate limiting dan basicauth, bukan sebagai satu-satunya lapisan keamanan.