NAT (Network Address Translation) #

Saat container Docker bisa curl google.com, ia tidak punya IP publik sendiri. Saat kamu bisa akses http://localhost:8080 dan sampai ke aplikasi di dalam container, container itu juga tidak punya IP yang bisa di-route. Semua “sihir” itu terjadi karena satu mekanisme yang bekerja di host: NAT (Network Address Translation).

NAT adalah fondasi yang membuat Docker bisa menerapkan prinsip satu host, banyak container, satu IP publik. Ia juga yang membuat container aman secara default — tanpa port mapping, container benar-benar tidak terlihat dari luar. Memahami NAT membantu kamu memecahkan masalah koneksi yang membingungkan, memilih network mode yang tepat, dan mendesain arsitektur yang aman.

Artikel ini membahas NAT di Docker secara mendalam: konsep, implementasi iptables, dua arah NAT (keluar dan masuk), default policy yang Docker pasang, dan implikasinya pada desain aplikasi. Setelah membaca artikel ini, kamu akan paham kenapa curl google.com dari container jalan tanpa konfigurasi manual, dan kenapa container tidak bisa di-ping dari host tanpa port mapping.

Apa Itu NAT? #

Network Address Translation (NAT) adalah teknik untuk menerjemahkan alamat IP dari satu ruang ke ruang lain saat paket melewati router atau gateway. Dalam konteks Docker, NAT dilakukan oleh host yang berfungsi sebagai gateway antara container (IP private) dan jaringan luar (IP host / internet).

NAT punya tiga varian utama:

  • SNAT (Source NAT) — mengubah source address. Biasa dipakai untuk traffic keluar (container → internet).
  • DNAT (Destination NAT) — mengubah destination address. Biasa dipakai untuk traffic masuk (host → container).
  • MASQUERADE — varian SNAT yang otomatis pakai IP dari interface keluar (berguna saat IP host tidak statis).

Docker memakai keduanya: MASQUERADE untuk traffic keluar, DNAT untuk port mapping.

flowchart LR
    CT[Container<br/>172.18.0.2:8000] -->|1. request keluar| N1{MASQUERADE}
    N1 -->|2. source = host IP| NET[Internet]
    NET -->|3. balasan ke host| N2{DNAT}
    N2 -->|4. destination = container IP| CT

    EXT[Client<br/>203.0.113.5] -->|5. request ke host:8080| H[Host:8080]
    H -->|6. DNAT ke container:80| CT
    CT -->|7. response| H
    H -->|8. response ke client| EXT

Pada diagram di atas, ada dua arah utama:

  • Arah keluar (1→4): container request ke internet, source IP-nya di-MASQUERADE jadi IP host, balasan di-DNAT balik.
  • Arah masuk (5→8): client request ke host, DNAT meneruskan ke container, response kembali ke client.
Mengapa NAT begitu penting untuk Docker? Karena container punya IP private (172.x, 192.168.x) yang tidak bisa di-route di internet. Tanpa NAT, container tidak akan pernah bisa bicara ke internet, dan internet tidak akan pernah bisa bicara ke container. NAT menyelesaikan dua arah dengan elegan.

Arsitektur Network Docker untuk NAT #

Untuk memahami NAT di Docker, kamu perlu paham dulu topologi default-nya. Saat Docker Engine berjalan di Linux, ia membuat:

Komponen Peran
docker0 (atau bridge custom) Virtual switch untuk container
Subnet private Misal 172.17.0.0/16 untuk default bridge
iptables rules Implementasi NAT + firewall
IP forwarding Host harus ip_forward=1 (default di Linux modern)
flowchart TB
    subgraph HOST["Docker Host"]
        direction TB
        BR[Bridge: 172.17.0.1]
        FW[iptables NAT rules]
        HETH[Host eth0: 192.168.1.10]
        BR --- FW --- HETH
    end
    subgraph CT["Container"]
        ETH0[eth0: 172.17.0.2]
    end
    BR ---|veth| ETH0
    HETH --> INET((Internet))

Tiga hal yang terjadi:

  1. Container punya IP private di subnet bridge.
  2. iptables di host mengelola aturan routing dan NAT.
  3. Host eth0 adalah satu-satunya interface yang terlihat dari internet.

Untuk traffic keluar, container kirim paket ke bridge → bridge cek routing table → routing bilang “lewat eth0 host” → iptables SNAT/MASQUERADE → keluar ke internet. Untuk traffic masuk (port mapping), paket masuk ke host port → iptables DNAT → bridge → container.


Outbound NAT: MASQUERADE #

Saat container mengirim request ke internet (misal apt-get update), Docker menambahkan aturan iptables MASQUERADE untuk subnet container.

# Lihat rule NAT (simplified)
iptables -t nat -L POSTROUTING -n -v
# Chain POSTROUTING (policy ACCEPT)
#  pkts bytes target     prot opt in     out     source               destination
#     0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16         0.0.0.0/0

Aturan ini artinya: semua paket dari subnet 172.17.0.0/16 yang keluar bukan lewat docker0 akan di-MASQUERADE (source IP-nya diganti jadi IP dari interface keluar).

Alur Detail #

sequenceDiagram
    participant CT as Container
    participant BR as Bridge
    participant RT as Routing Table
    participant IPT as iptables
    participant NET as Internet

    CT->>BR: GET http://google.com (src: 172.17.0.2)
    BR->>RT: Destination bukan lokal
    RT->>IPT: Lewat eth0 host
    IPT->>IPT: MASQUERADE: src jadi 192.168.1.10
    IPT->>NET: Paket keluar
    NET-->>IPT: Response
    IPT-->>CT: Di-reverse-NAT balik ke 172.17.0.2

Container 172.17.0.2 mengirim paket dengan source IP-nya sendiri. iptables “menyembunyikan” IP itu dan menggantinya dengan IP host sebelum paket keluar ke internet. Server Google melihat request datang dari 192.168.1.10 (IP host) dan membalas ke host. Saat balasan sampai, iptables me-reverse NAT dan meneruskan ke container yang asli.

Kenapa pakai MASQUERADE bukan SNAT biasa? Karena MASQUERADE otomatis membaca IP dari interface keluar — cocok saat host punya IP dinamis (DHCP). Untuk server dengan IP statis, SNAT bisa sedikit lebih efisien, tapi MASQUERADE adalah default Docker.

Konfigurasi IP Forwarding #

Agar host bisa meneruskan paket dari container ke luar (dan sebaliknya), kernel Linux butuh ip_forward=1:

# Cek status
sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 1

# Set manual kalau perlu
sudo sysctl -w net.ipv4.ip_forward=1

Docker mensyaratkan ip_forward=1 dan akan throw error kalau belum diaktifkan. Pada distribusi modern, ini default nyala.


Inbound NAT: DNAT dan Port Mapping #

Arah kedua — yang paling sering kamu temui — adalah port mapping. Saat kamu menjalankan docker run -p 8080:80 nginx, Docker membuat aturan DNAT di iptables.

# Lihat rule DNAT
iptables -t nat -L PREROUTING -n
# Chain PREROUTING
# -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80

Artinya: semua paket TCP masuk ke port 8080 host akan di-DNAT ke 172.17.0.2:80 (container).

Alur Detail Port Mapping #

sequenceDiagram
    participant U as User
    participant H as Host:8080
    participant PR as PREROUTING (DNAT)
    participant BR as Bridge
    participant CT as Container:80

    U->>H: TCP ke 192.168.1.10:8080
    H->>PR: Paket masuk
    PR->>PR: DNAT: dst jadi 172.17.0.2:80
    PR->>BR: Forward ke bridge
    BR->>CT: Sampai di container
    CT-->>BR: HTTP response
    BR-->>H: Kembali ke host
    H-->>U: Response sampai di user
Perhatikan: User tidak pernah tahu IP asli container. Ia hanya melihat 192.168.1.10:8080 (host). Container bisa di-restart dengan IP berbeda, dan user tidak akan tahu — aturan DNAT akan di-update otomatis oleh Docker. Inilah kekuatan abstraksi Docker.

Format Port Mapping #

# Format lengkap
-p <host_ip>:<host_port>:<container_port>

# Contoh
docker run -p 8080:80 nginx                # bind ke 0.0.0.0:8080
docker run -p 127.0.0.1:8080:80 nginx      # bind ke localhost saja
docker run -p 192.168.1.10:8080:80 nginx   # bind ke IP LAN tertentu
docker run -p 80 nginx                     # random port host
docker run -p 8080:80/udp nginx            # protokol UDP
Anti-pattern: -p 8080:80 tanpa IP host akan bind ke 0.0.0.0 — artinya semua interface, termasuk IP publik kalau host punya. Untuk database (Postgres, MySQL, MongoDB, Redis) yang tidak boleh diakses publik, selalu bind ke 127.0.0.1 atau IP internal spesifik.

Docker Default Policy: Aman atau Masalah? #

Docker menambahkan banyak aturan iptables secara otomatis. Memahami default policy-nya penting untuk troubleshooting.

FORWARD Chain #

Docker mengubah default policy FORWARD chain dari ACCEPT ke DROP. Ini meningkatkan keamanan (traffic yang tidak dikenal ditolak), tapi bisa menyebabkan kebingungan saat custom rule ditambahkan.

iptables -L FORWARD -n
# Chain FORWARD (policy DROP)
# -A FORWARD -i docker0 -o docker0 -j ACCEPT    # container-to-container
# -A FORWARD -i docker0 ! -o docker0 -j ACCEPT  # container-to-internet (outbound)
# -A FORWARD ... (reverse)                       # inbound

Custom User Chain #

Docker membuat chain khusus bernama DOCKER dan DOCKER-USER agar rule-nya terisolasi dan mudah di-reset.

iptables -L DOCKER -n
# Chain DOCKER
# -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
Best practice: kalau kamu perlu menambah rule firewall custom, tambahkan di chain DOCKER-USER, bukan di chain DOCKER atau langsung di FORWARD. Docker menambah rule custom di awal DOCKER-USER, jadi rule kamu akan dievaluasi pertama — sebelum rule internal Docker.

Double NAT: Risiko yang Jarang Disadari #

Salah satu skenario yang membingungkan pemula: container di belakang Docker, Docker di belakang cloud NAT. Ini disebut double NAT dan punya implikasi penting.

flowchart LR
    U[User] -->|1. request| CSP[Cloud Provider NAT]
    CSP -->|2. dst = public IP VM| VM[VM Public IP]
    VM -->|3. DNAT ke container| CT[Container]
    CT -->|4. response src = container IP| VM
    VM -->|5. SNAT ke public IP| CSP
    CSP -->|6. balik ke user| U

Masalah yang muncul:

  • Source IP asli hilang — aplikasi di container tidak tahu IP asli user (sudah di-SNAT jadi IP VM).
  • WebSocket / long-lived connection kadang bermasalah karena state NAT bisa timeout.
  • Port forwarding ke container harus eksplisit — cloud NAT tidak otomatis.

Solusi: pakai Host network atau mode proxy protocol jika kamu butuh IP asli user. Untuk kebanyakan kasus, informasi IP asli tidak krusial karena ada reverse proxy (Nginx, Traefik) yang menambah header X-Forwarded-For.

Aplikasi yang butuh IP asli user (rate limiting, geo-blocking, fraud detection) akan kehilangan informasi itu karena double NAT. Solusinya: aktifkan proxy protocol di reverse proxy dan baca X-Forwarded-For dengan benar di aplikasi.

iptables: Teman Debugging yang Penting #

Saat container tidak bisa konek ke internet, atau port mapping tidak jalan, iptables adalah tempat pertama yang harus kamu cek. Berikut perintah yang berguna.

# Lihat semua rule NAT
sudo iptables -t nat -L -n -v

# Lihat rule spesifik untuk satu container/network
sudo iptables -t nat -L -n -v | grep 172.18.0

# Lihat rule FORWARD
sudo iptables -L FORWARD -n -v

# Trace satu paket (debugging advanced)
sudo iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
sudo iptables -t raw -A OUTPUT -p tcp --dport 80 -j TRACE
# Lihat di dmesg

# Lihat counter rule (apakah rule benar-benar matched)
sudo iptables -t nat -L POSTROUTING -n -v
# Kolom "pkts" dan "bytes" menunjukkan berapa kali rule kena
# Reset semua rule Docker (hati-hati!)
sudo iptables -t nat -F
sudo iptables -t filter -F
sudo systemctl restart docker
# Docker akan rebuild rule-nya saat restart
Jangan pernah flush iptables di production tanpa backup. iptables -F menghapus semua rule. Kalau host ini punya rule firewall penting, koneksi bisa terganggu. Selalu backup dulu: iptables-save > backup.rules.

NAT vs Network Mode Lainnya #

NAT bukan satu-satunya cara Docker menghubungkan container ke jaringan. Setiap network mode punya perilaku NAT yang berbeda.

Network Mode NAT Keluar? NAT Masuk? Isolasi
bridge ✅ MASQUERADE ✅ DNAT (port mapping) Per bridge
host ❌ Tidak ❌ Tidak Tidak ada
none ❌ Tidak ❌ Tidak Maksimal
overlay ✅ (tergantung) Per network
macvlan ❌ (IP langsung di LAN) ❌ (IP LAN) Layer-2

Bridge pakai NAT dua arah karena container punya IP private. Host tanpa NAT karena container share network host. None tanpa NAT karena tidak ada network. Macvlan tanpa NAT karena container punya IP LAN sendiri.


Kapan NAT Docker Jadi Masalah? #

NAT umumnya invisible dan bekerja dengan baik. Tapi ada beberapa situasi di mana NAT Docker bisa menjadi batasan:

1. UDP hole punching — beberapa aplikasi P2P butuh koneksi UDP langsung. NAT membuat ini sulit karena state di iptables bisa expire.

2. Real-time multiplayer gaming server — latency tambahan dari NAT dan bridge bisa terasa. Solusi: network_mode: host.

3. VoIP / video calling — beberapa protokol real-time struggle dengan NAT. STUN/TURN server bisa dibutuhkan.

4. High-throughput database replication — untuk replikasi dengan bandwidth tinggi, overhead NAT bisa signifikan. Solusi: dedicated network atau host mode.

5. ICMP / ping dari host ke container — tanpa port mapping, kamu tidak bisa ping container dari host lewat IP host. Solusi: ping lewat IP container langsung dari dalam host namespace.

# Ping container dari host (cara yang bekerja)
docker inspect <container> --format '{{.NetworkSettings.IPAddress}}'
ping 172.17.0.2
Untuk debugging, cara paling efektif: masuk ke namespace container dan test dari dalam. docker exec -it <container> sh lalu ping, curl, atau wget ke target. Ini menghindari NAT outbound sama sekali.

Perintah Penting untuk NAT #

# Lihat rule NAT lengkap
sudo iptables -t nat -L -n -v --line-numbers

# Lihat hanya yang relevan dengan Docker
sudo iptables -t nat -S | grep -E 'DOCKER|MASQUERADE'

# Lihat apakah IP forwarding aktif
cat /proc/sys/net/ipv4/ip_forward
# atau
sysctl net.ipv4.ip_forward

# Monitor traffic NAT real-time (butuh iptraf atau iftop)
sudo apt install iptraf-ng
sudo iptraf-ng

Sumber Belajar NAT Lebih Dalam #

Untuk yang ingin mendalami NAT dan iptables lebih jauh, beberapa referensi klasik yang layak dibaca:

  • Linux man pagesman iptables, man iptables-extensions. Dokumentasi resmi yang sangat lengkap.
  • “Linux Firewalls” by Steve Suehring — buku yang membahas iptables dan NAT secara sistematis.
  • Docker networking documentation — bagian “Understand Docker networking” di docs.docker.com.
  • Cilium eBPF documentation — untuk yang sudah advance dan ingin lihat bagaimana networking modern menggantikan iptables dengan eBPF.
Tren industri: eBPF semakin banyak dipakai di production sebagai pengganti iptables. Tool seperti Cilium, Calico, dan Flannel memungkinkan networking container yang lebih cepat dan observable. Konsep NAT-nya mirip, tapi implementasinya di kernel level dan jauh lebih fleksibel.

Ringkasan #

  • NAT adalah mekanisme di mana host menerjemahkan IP container (private) ke IP host (public/unik) dan sebaliknya. Tanpa NAT, container tidak bisa akses internet dan tidak bisa diakses dari luar.
  • Outbound NAT memakai MASQUERADE (varian SNAT) — saat container request keluar, source IP-nya di-replace jadi IP host, lalu di-reverse saat balasan datang.
  • Inbound NAT memakai DNAT lewat port mapping (-p 8080:80). Paket masuk ke port host di-DNAT ke IP container, response kembali lewat host.
  • Docker mengelola iptables secara otomatis — chain DOCKER, DOCKER-USER, dan rule MASQUERADE/DNAT ditambahkan saat container atau network dibuat.
  • IP forwarding (ip_forward=1) wajib aktif. Docker mensyaratkannya dan akan error kalau tidak.
  • Default policy FORWARD Docker adalah DROP (bukan ACCEPT) untuk keamanan. Rule untuk container traffic ditambahkan secara eksplisit.
  • Double NAT (container di belakang cloud NAT) umum di cloud. IP asli user hilang; untuk aplikasi yang butuh IP asli, gunakan proxy protocol.
  • Anti-pattern: -p 8080:80 tanpa IP host akan bind ke 0.0.0.0 — database jadi terekspos publik. Selalu bind ke 127.0.0.1 untuk service internal.
  • Untuk debugging, iptables -t nat -L -n -v adalah teman terbaik. pkts dan bytes menunjukkan apakah rule benar-benar matched.
  • NAT cocok untuk aplikasi web biasa, API, dan workload yang tidak butuh IP asli user. Kurang cocok untuk: P2P, real-time gaming, high-throughput replication, VoIP.

← Sebelumnya: Bridge Network   Berikutnya: Host Network →

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