Pembatasan IP #
Salah satu cara paling efektif untuk melindungi aset digital kita dari akses yang tidak sah adalah dengan melakukan kontrol akses di tingkat jaringan menggunakan Pembatasan IP (IP Restriction). Dengan membatasi akses berdasarkan alamat IP, kita dapat memastikan bahwa area sensitif (seperti panel admin, dashboard monitoring, atau API staging internal) hanya dapat dijangkau oleh pihak-pihak yang sah (misalnya dari jaringan kantor atau VPN internal), sekaligus memblokir IP penyerang yang diketahui berbahaya secara langsung di tingkat web server sebelum membebani aplikasi.
Nginx menyediakan modul bawaan ngx_http_access_module yang memungkinkan kita menyusun aturan allow dan deny dengan sangat mudah. Namun, pada arsitektur web modern yang sering kali menggunakan Load Balancer, CDN (seperti Cloudflare), atau Reverse Proxy tambahan di depan Nginx, pembatasan IP menjadi sedikit lebih rumit. Di artikel ini, kita akan membahas dasar-dasar aturan allow dan deny, pentingnya urutan penulisan, penggunaan modul geo untuk pengelolaan IP skala besar, serta solusi krusial menggunakan Real IP Module agar kita tidak salah memblokir IP CDN.
Aturan Dasar: direktif allow dan deny #
Nginx menggunakan dua direktif utama untuk mengontrol akses berbasis IP:
allow: Mengizinkan akses untuk alamat IP atau CIDR block tertentu.deny: Menolak akses untuk alamat IP atau CIDR block tertentu.
Kita dapat menargetkan alamat IP tunggal (IPv4 maupun IPv6), CIDR block (subnet jaringan), atau menggunakan kata kunci all untuk menargetkan seluruh alamat IP di internet.
Berikut adalah contoh konfigurasi dasar di dalam blok location:
location /admin/ {
# 1. Izinkan alamat IP tunggal tertentu (misal IP admin)
allow 203.0.113.15;
# 2. Izinkan range IP subnet kantor (IPv4)
allow 192.168.1.0/24;
# 3. Izinkan range IP subnet IPv6 kustom
allow 2001:db8:1234::/48;
# 4. Tolak semua alamat IP lainnya di luar daftar di atas
deny all;
proxy_pass http://admin_backend;
}
Kritis: Pentingnya Urutan Penulisan (Sequence matters) #
Nginx mengevaluasi aturan allow dan deny secara berurutan dari atas ke bawah. Evaluasi akan langsung dihentikan ketika Nginx menemukan aturan pertama yang cocok (first match). Oleh karena itu, urutan penulisan aturan ini sangatlah krusial.
Mari kita bandingkan perbedaan urutan berikut untuk memahami dampaknya:
1. Pola yang Salah (Anti-Pattern) #
location /api/ {
deny all; # <-- SEMUA IP ditolak di sini!
allow 10.0.0.0/8; # <-- Tidak akan pernah dieksekusi
}
- Akibat: Aturan
deny alldiletakkan di baris pertama. Ketika ada request datang dari IP10.0.0.5, Nginx membaca baris pertama, mendapati bahwa IP tersebut cocok dengan kategoriall, dan langsung memblokir akses (status 403 Forbidden). Nginx tidak pernah membaca baris kedua.
2. Pola yang Benar (Best Practice) #
location /api/ {
allow 10.0.0.0/8; # <-- IP internal 10.x.x.x diizinkan masuk, evaluasi SELESAI
deny all; # <-- IP luar lainnya langsung ditolak
}
- Akibat: Jika request datang dari IP internal
10.0.0.5, request cocok dengan baris pertama (allow), akses langsung diberikan, dan evaluasi dihentikan. Jika request datang dari IP luar180.250.2.1, request dilewatkan oleh baris pertama karena tidak cocok, lalu menabrak baris kedua (deny all) dan diblokir dengan sukses.
[!TIP] Selalu tuliskan aturan spesifik terlebih dahulu (seperti
allow IP_tunggalatauallow subnet), kemudian akhiri dengan aturan umum (deny allatauallow all) di baris paling bawah.
Masalah Kritis di Balik CDN/Reverse Proxy: Siapa Pengunjung Aslinya? #
Pada infrastruktur web modern, Nginx jarang sekali berhadapan langsung dengan browser klien di internet. Biasanya, ada lapisan perantara di depan Nginx seperti Cloudflare CDN, AWS Application Load Balancer (ALB), atau Reverse Proxy internal lainnya.
flowchart TD
Client["Klien Asli<br>(IP: 198.51.100.42)"] -->|1. Kirim Request| CDN["CDN / Cloudflare<br>(IP: 103.21.244.11)"]
CDN -->|2. Teruskan Request + Header X-Forwarded-For| Nginx["Nginx Server"]
subgraph Nginx Internal Processing
NginxCheck{"Apakah Modul real_ip Aktif & Dikonfigurasi?"}
NginxCheck -->|Tidak Aktif| Default["Nginx membaca IP Klien sebagai IP CDN (103.21.244.11)"]
NginxCheck -->|Aktif| Parse["Nginx mengekstrak header & menyetel $remote_addr = 198.51.100.42"]
Default --> Apply1["Evaluasi allow/deny pada IP CDN (Bisa Salah Blokir Seluruh CDN!)"]
Parse --> Apply2["Evaluasi allow/deny pada IP Klien Asli (Keamanan Tepat)"]
end
classDef danger fill:#ef4444,stroke:#dc2626,color:#ffffff;
classDef success fill:#10b981,stroke:#059669,color:#ffffff;
class Apply1 danger;
class Apply2 success;Jika kita tidak mengonfigurasi Nginx untuk menangani skenario ini, kita akan menghadapi dua masalah besar:
- Variabel
$remote_addrSalah: Nginx akan membaca alamat IP klien sebagai alamat IP milik CDN (misalnya103.21.244.11), bukan IP asli pengunjung (198.51.100.42). - Salah Blokir: Jika kita menuliskan aturan
deny 198.51.100.42, aturan tersebut tidak akan bekerja karena Nginx mengira request datang dari IP CDN. Sebaliknya, jika kita menuliskandeny all, Nginx akan menolak koneksi dari CDN, yang berarti kita memblokir seluruh pengunjung situs web kita!
Solusi: Menggunakan Modul real_ip (ngx_http_realip_module)
#
Untuk mengatasi masalah di atas, Nginx menyediakan modul khusus bernama Real IP Module. Modul ini berfungsi untuk memberi tahu Nginx: “Percayai alamat IP dari Load Balancer/CDN kita, lalu cari alamat IP asli klien di dalam header HTTP tertentu, kemudian gantikan nilai variabel $remote_addr dengan IP asli tersebut.”
Header HTTP yang umum digunakan untuk menyimpan IP asli pengunjung adalah:
X-Forwarded-For: Standar industri yang digunakan oleh mayoritas load balancer dan proxy. Berisi daftar IP berantai yang dilalui paket data.CF-Connecting-IP: Header khusus yang disisipkan oleh Cloudflare untuk menyimpan IP asli pengunjung.
Konfigurasi Modul real_ip di Nginx #
Berikut adalah langkah-langkah mengonfigurasi modul real_ip pada Nginx (diletakkan di dalam file konfigurasi /etc/nginx/conf.d/real-ip.conf atau di tingkat http):
# 1. Daftarkan daftar IP server proxy/CDN yang kita percayai
# (Contoh di bawah adalah beberapa range IP publik milik Cloudflare)
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
# Jika menggunakan Load Balancer internal di jaringan AWS/Private Cloud:
set_real_ip_from 10.0.0.0/8; # Percayai IP internal VPC kita
# 2. Tentukan header mana yang berisi IP asli klien
real_ip_header CF-Connecting-IP; # Gunakan CF-Connecting-IP jika pakai Cloudflare
# real_ip_header X-Forwarded-For; # Gunakan X-Forwarded-For untuk ALB/Proxy umum
# 3. Aktifkan recursive search jika ada banyak hop proxy
real_ip_recursive on;
Penjelasan Parameter Poin Penting: #
set_real_ip_from: Nginx hanya akan memproses penggantian IP jika request datang dari alamat IP yang terdaftar di sini. Ini sangat penting untuk keamanan. Jika ada peretas yang mengirimkan request langsung bypass CDN ke server kita dengan memalsukan headerX-Forwarded-For, Nginx akan mengabaikan header palsu tersebut karena IP asal peretas tidak terdaftar sebagai proxy tepercaya.real_ip_header: Menentukan header sumber penarikan IP asli.real_ip_recursive on: Jika disetel keon, Nginx akan menyisir daftar IP berantai di headerX-Forwarded-Fordari kanan ke kiri, mengabaikan seluruh IP yang terdaftar diset_real_ip_from, dan mengambil IP pertama dari kanan yang tidak dipercayai sebagai IP asli klien.
Setelah konfigurasi di atas aktif dan Nginx dimuat ulang, variabel $remote_addr akan otomatis berisi IP asli klien. Aturan allow dan deny kita kini dapat berfungsi kembali dengan akurat menggunakan IP asli pengunjung meskipun server berada di balik CDN.
Modul geo: Pengelolaan IP Range Skala Besar #
Jika kita harus mengelola ratusan aturan pembatasan IP (misalnya memisahkan IP kantor cabang, IP pusat, IP vendor pihak ketiga, dan IP daftar hitam bot), menuliskan direktif allow dan deny secara manual di dalam berkas virtual host akan membuat konfigurasi menjadi sangat panjang, rumit, dan sulit dipelihara.
Solusi terbaik untuk masalah ini adalah menggunakan modul geo. Modul geo memungkinkan kita mendefinisikan variabel kustom yang nilainya bergantung pada alamat IP klien yang terhubung.
Berikut adalah pola implementasi modular geo di Nginx:
Langkah 1: Definisikan Peta IP di dalam blok http
#
# /etc/nginx/conf.d/geo-ip.conf
geo $is_trusted_network {
default 0; # Default: IP tidak dikenal/tidak tepercaya (nilai 0)
127.0.0.1 1; # Localhost tepercaya (nilai 1)
10.0.0.0/8 1; # Subnet VPC tepercaya
192.168.1.0/24 1; # Subnet Wi-Fi Kantor Utama tepercaya
203.0.113.80 1; # IP Publik Kantor Cabang tepercaya
# Kita juga bisa memuat berkas eksternal jika daftarnya sangat banyak:
# include /etc/nginx/trusted_ips.txt;
}
Langkah 2: Gunakan Variabel pada Blok Server/Location #
Sekarang, di dalam berkas virtual host, kita cukup melakukan pengecekan logika variabel menggunakan instruksi sederhana:
server {
listen 443 ssl;
server_name internal-dashboard.example.com;
location / {
# Jika IP klien bukan dari jaringan tepercaya (nilai variabel = 0)
if ($is_trusted_network = 0) {
return 403; # Tolak dengan status Forbidden
}
proxy_pass http://internal_backend;
}
}
Dengan metode ini:
- Berkas virtual host kita tetap bersih, ringkas, dan mudah dibaca.
- Jika ada penambahan atau perubahan alamat IP kantor di masa depan, kita cukup mengedit berkas pemetaan
geo-ip.confdi satu tempat saja, tanpa perlu menyentuh berkas konfigurasi situs web kita yang sensitif.
Studi Kasus: Mengamankan Halaman Sensitif WordPress (wp-admin)
#
Bagi pengguna CMS seperti WordPress, mengamankan halaman /wp-admin/ dan file wp-login.php dari serangan brute-force login global adalah kewajiban. Kita bisa menggunakan teknik pembatasan IP dengan aman di Nginx:
server {
listen 443 ssl;
server_name mywordpress.com;
root /var/www/wordpress;
index index.php;
# Aturan routing umum WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}
# Proteksi khusus wp-admin dan wp-login
location ~ ^/(wp-admin|wp-login\.php) {
# Batasi akses: hanya boleh diakses dari IP kantor statis kita
allow 203.0.113.80;
allow 192.168.10.0/24; # VPN IP Range
deny all;
# Jangan lupa teruskan pemrosesan PHP di dalamnya
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
# Penanganan file PHP umum lainnya
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
}
}
Pembatasan IP Berdasarkan Geografi (Negara) via GeoIP2 #
Dalam beberapa skenario keamanan tingkat lanjut, kita mungkin ingin membatasi akses ke seluruh aplikasi kita berdasarkan asal geografis negara pengguna. Misalnya, jika aplikasi kita hanya melayani pengguna lokal di Indonesia (domain .id), kita dapat memblokir request yang datang dari luar negeri untuk meminimalkan trafik bot pemindai celah keamanan global yang mayoritas berasal dari IP server luar negeri.
Untuk menerapkan ini di Nginx, kita menggunakan modul pihak ketiga ngx_http_geoip2_module bersama database MaxMind GeoIP2.
Langkah 1: Instalasi Modul dan Database GeoIP2 #
# Instal modul GeoIP2 di Ubuntu/Debian
sudo apt install libmaxminddb-dev nginx-module-geoip2 -y
# Unduh database MaxMind Country gratis (GeoLite2-Country.mmdb)
# Pindahkan berkas database ke direktori Nginx
sudo mkdir -p /etc/nginx/geoip/
sudo mv GeoLite2-Country.mmdb /etc/nginx/geoip/
Langkah 2: Konfigurasi Pembacaan Negara di Nginx #
Tambahkan konfigurasi berikut di file /etc/nginx/nginx.conf pada blok http:
http {
# Muat modul GeoIP2 di bagian atas file (jika dinamis)
# load_module modules/ngx_http_geoip2_module.so;
# Konfigurasi pembacaan database GeoIP2 Country
geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
auto_reload 5m; # Muat ulang database setiap 5 menit jika ada pembaruan
$geoip2_data_country_code default=ID source=$remote_addr country iso_code;
}
# Buat logika pemetaan negara yang diizinkan
map $geoip2_data_country_code $allowed_country {
default 0; # Default: Blokir negara lain (nilai 0)
ID 1; # Izinkan Indonesia (nilai 1)
SG 1; # Izinkan Singapura (opsional untuk server proxy/CDN)
}
}
Langkah 3: Terapkan Pemblokiran di Server Block #
Sekarang, kita bisa menolak akses dari negara yang tidak diizinkan di dalam server block:
server {
listen 443 ssl;
server_name example.com;
location / {
# Jika asal negara bukan dari negara yang diizinkan (nilai = 0)
if ($allowed_country = 0) {
# Log aktivitas pemblokiran untuk analisis
access_log /var/log/nginx/blocked_countries.log;
# Kembalikan status 403 Forbidden
return 403 "Akses tidak diizinkan dari wilayah Anda.";
}
proxy_pass http://backend;
}
}
Mencegah Kebocoran & Pemalsuan IP (IP Spoofing Protection) #
Ketika kita menggunakan header X-Forwarded-For untuk mengekstrak IP asli klien, kita harus sangat berhati-hati terhadap serangan IP Spoofing (Pemalsuan IP). Header X-Forwarded-For adalah header HTTP biasa yang dapat ditulis dan dimodifikasi secara bebas oleh penyerang saat mengirimkan request pertama.
Bahaya Menonaktifkan real_ip_recursive
#
Bayangkan skenario berikut jika kita menonaktifkan fitur rekursif (real_ip_recursive off;):
- Penyerang dengan IP asli
198.51.100.99mengirim request dengan menyisipkan header palsu:X-Forwarded-For: 8.8.8.8(IP Google DNS tepercaya). - Request melewati Cloudflare CDN (IP Cloudflare:
172.64.0.5). - Cloudflare menambahkan IP penyerang ke header sehingga menjadi:
X-Forwarded-For: 8.8.8.8, 198.51.100.99. - Request sampai ke Nginx. Nginx melihat request datang dari IP Cloudflare
172.64.0.5yang dipercayai (karena terdaftar diset_real_ip_from). - Jika
real_ip_recursivemati (off), Nginx akan mengambil IP paling kanan dari daftar header, yaitu198.51.100.99sebagai IP asli klien. Ini aman. - Namun, jika ada skenario di balik Load Balancer ganda di mana kita tidak menyaring daftar IP dengan benar, penyerang bisa memanipulasi urutan.
- Dengan menyetel
real_ip_recursive on;, Nginx akan menyisir seluruh IP di header dari kanan ke kiri, mengabaikan seluruh IP yang terdaftar sebagai proxy tepercaya (set_real_ip_from), dan berhenti pada IP pertama dari kanan yang tidak tepercaya. Ini menjamin bahwa IP palsu8.8.8.8yang diletakkan di sebelah kiri oleh penyerang akan diabaikan, dan Nginx tetap mengunci IP penyerang asli198.51.100.99.
[!IMPORTANT] Selalu pastikan direktif
real_ip_recursive on;aktif, dan hanya masukkan IP Load Balancer/CDN resmi kita ke dalam daftarset_real_ip_from. Jangan pernah memasukkan alamat IP wildcard atau IP luar yang tidak kita kendalikan ke dalam daftar tepercaya tersebut.
Ringkasan #
- Urutan Penulisan Kritis: Nginx membaca aturan
allowdandenysecara berurutan dan langsung berhenti di baris pertama yang cocok. Selalu tulis aturanallow IP_spesifiksebelum ditutup dengandeny alldi bagian bawah.- Gunakan Modul real_ip di Balik CDN: Jika web server Nginx berada di balik Load Balancer atau CDN seperti Cloudflare, wajib aktifkan modul
real_ipagar aturan pemblokiran IP asli tidak salah sasaran ke IP CDN.- Terapkan geo untuk IP Skala Besar: Kelola ratusan range IP kantor secara rapi di satu tempat terpusat menggunakan modul
geountuk menghasilkan variabel status akses tepercaya.- Amankan Endpoint Administratif: Batasi akses ke path administratif sensitif (seperti
/admin/atau/wp-login.php) hanya untuk IP VPN/internal kantor demi meminimalkan serangan brute-force massal dari luar.