Nginx di Balik NAT / Docker (Real IP Problem)

Nginx di Balik NAT / Docker (Real IP Problem)

Saat Nginx dijalankan di balik NAT, reverse proxy, atau container (Docker), sering muncul masalah:

Semua request terlihat datang dari IP yang sama (proxy / NAT IP)
Seolah semua user punya IP yang sama

Ini menyebabkan masalah nyata:

  • Rate limiting salah target
  • Logging IP asli salah
  • GeoIP & analytics rusak
  • Security rules tidak efektif

Masalah ini dikenal sebagai Real IP Problem.


1. Apa yang Terjadi Sebenarnya?

Contoh arsitektur Docker:

Internet → Load Balancer → Docker Nginx → Backend
  • Semua request ke Nginx berasal dari LB internal
  • $remote_addr Nginx = LB IP, bukan client asli

Jika tidak ditangani:

  • limit_req / limit_conn → salah target
  • Log → semua user sama IP
  • GeoIP → semua traffic di satu kota (LB)

2. Header Penting: X-Forwarded-For dan X-Real-IP

  • Proxy modern (LB, CDN, Nginx) menyisipkan header IP asli:
X-Forwarded-For: 203.0.113.25, 10.0.0.1
X-Real-IP: 203.0.113.25
  • Client asli = IP pertama (203.0.113.25)
  • IP proxy / NAT = IP berikutnya (10.0.0.1)

Nginx perlu tahu agar $remote_addr merefleksikan client asli.


3. Module Nginx Penting

ngx_http_realip_module

Directive utama:

set_real_ip_from <trusted_proxy_ip>;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
  • set_real_ip_from → hanya trust proxy tertentu
  • real_ip_header → baca header
  • real_ip_recursive → ambil IP pertama dari list

4. Contoh Konfigurasi di Docker

http {
    real_ip_header X-Forwarded-For;
    set_real_ip_from 172.18.0.0/16;  # subnet Docker internal
    real_ip_recursive on;

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
            # Rate limiting berdasarkan $remote_addr sekarang valid
        }
    }
}

Efek:

  • $remote_addr = client asli
  • Rate limit → tidak “semua user satu IP”
  • Logs → akurat

5. Kesalahan Fatal yang Sering Terjadi

  1. Tidak menambahkan set_real_ip_from
    → Nginx tetap mencatat IP proxy
  2. real_ip_recursive off
    $remote_addr = IP terakhir (proxy) bukan client
  3. Menaruh subnet salah
    → IP internal yang tidak trusted bisa memalsukan IP client
  4. Menggunakan $http_x_forwarded_for langsung di config
    → Bisa di-spoof
    → Tidak aman untuk rate limiting / security

6. Real Case: Rate Limit Salah Target

limit_req_zone $remote_addr zone=api:10m rate=5r/s;
  • Semua request Docker LB → $remote_addr = 10.0.0.5
  • 1000 user → Nginx pikir 1 user → throttle → banyak 429
  • Solusi: real_ip_module$remote_addr = client asli

7. GeoIP & Analytics

Tanpa real_ip_module:

  • Semua traffic terlihat dari server / LB IP
  • Analitik & GeoIP rusak
  • Security tools → false alert

8. Multi-layer Proxy

Jika ada:

Client → CDN → LB → Nginx → Backend
  • Gunakan real_ip_recursive on
  • set_real_ip_from → trust semua layer yang sah
  • Ambil IP pertama yang sah sebagai $remote_addr

9. Logging yang Akurat

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
  • Masih simpan header asli untuk debugging
  • $remote_addr → bisa dipakai rate limit & analytics

10. Checklist Aman Real IP di Docker/NAT

  1. Gunakan ngx_http_realip_module
  2. Tentukan subnet trusted (set_real_ip_from)
  3. Aktifkan real_ip_recursive on
  4. Jangan gunakan $http_x_forwarded_for mentah-mentah untuk security
  5. Uji dengan curl --header "X-Forwarded-For: 1.2.3.4" → pastikan $remote_addr benar

11. Kesimpulan

Jika Nginx dijalankan di balik NAT atau container, real IP tidak otomatis.

Kalimat kuncinya:

Tanpa real_ip_module, rate limit, logging, dan security bisa salah kaprah, meski semua traffic berjalan normal.