Pembatasan IP

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
}
Tanpa trusted_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_ip untuk 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_proxies agar remote_ip matcher menggunakan IP klien asli dari X-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.

← Sebelumnya: Rate Limiting   Berikutnya: Security Headers →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact