Lewati ke isi

Keamanan/Cyber Security

Cara Mengamankan Docker di VPS agar Port Tidak Bocor

Port container Docker bisa terbuka ke internet walau UFW aktif. Ini penyebabnya dan cara menguncinya dengan bind 127.0.0.1 dan DOCKER-USER.

Terbit 5 menit baca

Jawaban singkat

Port yang dipublikasikan Docker melewati aturan UFW karena Docker mengatur iptables sendiri. Solusinya, petakan port ke 127.0.0.1, buka ke publik hanya lewat reverse proxy, dan pakai rantai DOCKER-USER bila perlu membatasi IP.

Daftar isi
  1. Kenapa UFW tidak melindungi container?
  2. Langkah mengunci port Docker
  3. Langkah 1: Inventaris port yang sedang terbuka
  4. Langkah 2: Ikat port internal ke 127.0.0.1
  5. Langkah 3: Jadikan 127.0.0.1 sebagai bawaan
  6. Langkah 4: Buka ke publik hanya lewat reverse proxy
  7. Langkah 5: Batasi sumber IP dengan DOCKER-USER
  8. Langkah 6: Lindungi Docker socket
  9. Langkah 7: Uji dari luar
  10. Ringkasan pilihan publikasi port
  11. Pertanyaan yang sering diajukan

Sudah mengaktifkan UFW, menolak semua koneksi masuk kecuali SSH dan HTTPS, lalu kaget karena port database di container tetap bisa dibuka dari luar? Kamu tidak salah ketik aturan. Begitulah cara Docker bekerja secara bawaan.

Dokumentasi resmi Docker menjelaskan bahwa lalu lintas ke port yang dipublikasikan (-p atau ports: di Compose) dialihkan di tabel nat sebelum sampai ke rantai INPUT yang dipakai UFW. Akibatnya aturan UFW dilewati. Jadi pengamanannya harus dilakukan di sisi Docker: tentukan port mana yang boleh publik, ikat sisanya ke localhost, dan saring sisanya di rantai DOCKER-USER.

Kenapa UFW tidak melindungi container?

Saat kamu menjalankan docker run -p 8080:80 nginx, Docker menambahkan aturan iptables yang meneruskan paket dari port 8080 di host ke container. Pengalihan ini terjadi di awal, lalu paket berjalan di rantai FORWARD, bukan INPUT. UFW memasang aturannya terutama di INPUT, sehingga tidak pernah "melihat" paket itu.

Kalau kamu memakai firewalld (umum di AlmaLinux atau Rocky), Docker membuat zona bernama docker dengan target ACCEPT dan kebijakan forwarding ke zona itu. Efeknya mirip: port yang dipublikasikan terbuka.

Docker juga memperingatkan agar kamu tidak mematikan pengelolaan iptables ("iptables": false) karena hampir pasti merusak jaringan container. Jadi jalan yang benar bukan melawan Docker, tetapi mengatur publikasi port dengan sadar.

Langkah mengunci port Docker

Langkah 1: Inventaris port yang sedang terbuka

docker ps --format 'table {{.Names}}\t{{.Ports}}'
sudo ss -tlnp

Perhatikan kolom Ports. Tulisan 0.0.0.0:5432->5432/tcp atau [::]:6379->6379/tcp berarti port itu terbuka ke semua alamat, termasuk internet. Yang aman terlihat seperti 127.0.0.1:5432->5432/tcp.

Langkah 2: Ikat port internal ke 127.0.0.1

Untuk layanan yang hanya dipakai di server itu sendiri (database, Redis, panel admin, API internal), tulis alamatnya secara eksplisit. Di Docker Compose:

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

Lebih baik lagi, hapus ports: sama sekali bila hanya container lain yang butuh akses. Container di jaringan Compose yang sama bisa saling memanggil lewat nama layanan (db:5432) tanpa port dipublikasikan ke host.

Terapkan dengan docker compose up -d, lalu cek lagi docker ps.

Langkah 3: Jadikan 127.0.0.1 sebagai bawaan

Supaya port baru tidak terbuka tanpa sengaja, setel alamat bawaan di /etc/docker/daemon.json:

{
  "ip": "127.0.0.1"
}

Lalu sudo systemctl restart docker. Opsi ip membuat -p 8080:80 otomatis terikat ke 127.0.0.1. Port yang memang harus publik ditulis eksplisit, misalnya "0.0.0.0:443:443". Perhatikan bahwa restart Docker ikut menghentikan container sementara, jadi lakukan di jam sepi.

Langkah 4: Buka ke publik hanya lewat reverse proxy

Pola yang rapi: hanya satu container (Nginx, Caddy, atau Traefik) yang mempublikasikan port 80 dan 443. Aplikasi lain berada di jaringan internal dan diakses lewat proxy itu. Keuntungannya, HTTPS, pembatasan laju, dan log akses terpusat di satu tempat. Contoh pengaturan pembatasan laju ada di artikel rate limiting di Nginx.

Langkah 5: Batasi sumber IP dengan DOCKER-USER

Kadang port memang harus publik tapi hanya untuk alamat tertentu, misalnya panel monitoring untuk IP kantor. Docker menyediakan rantai DOCKER-USER yang diproses sebelum aturan Docker sendiri. Contoh dari dokumentasi resmi, disesuaikan:

sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -j DROP
sudo iptables -I DOCKER-USER -m state --state RELATED,ESTABLISHED -j ACCEPT

Baris pertama menolak semua koneksi dari antarmuka publik eth0 kecuali dari IP kantor (ganti dengan IP dan nama antarmukamu). Baris kedua disisipkan paling atas agar balasan untuk koneksi keluar dari container tetap lolos. Aturan ini berlaku untuk semua port container, jadi uji dengan hati-hati. Aturan iptables manual hilang saat reboot kecuali disimpan, misalnya dengan paket iptables-persistent.

Langkah 6: Lindungi Docker socket

Port bukan satu-satunya kebocoran. File /var/run/docker.sock setara akses root ke host. Jangan memasangnya ke container kecuali benar-benar perlu (misalnya alat manajemen), dan jangan pernah membuka API Docker lewat TCP tanpa TLS. Tambahkan pengguna ke grup docker hanya bila kamu sadar itu sama dengan memberi akses root.

Langkah 7: Uji dari luar

Dari komputer lain:

nc -zv IP_PUBLIK_SERVER 5432
nc -zv IP_PUBLIK_SERVER 443

Port internal harus gagal tersambung, port publik (443) harus terbuka. Ulangi setiap kali menambah layanan baru.

Ringkasan pilihan publikasi port

Penulisan di ComposeBisa diakses dariCocok untuk
Tanpa ports:Hanya container di jaringan yang samaDatabase, cache, worker
"127.0.0.1:5432:5432"Hanya host itu sendiriAlat admin lewat SSH tunnel
"10.0.0.2:5432:5432" (IP privat)Jaringan privatServer aplikasi terpisah
"443:443" atau "0.0.0.0:443:443"Seluruh internetReverse proxy saja

Kalau butuh membuka panel admin dari laptop, gunakan SSH tunnel: ssh -L 5432:127.0.0.1:5432 user@server, lalu sambungkan alatmu ke localhost:5432. Port tidak perlu dibuka ke internet sama sekali.

Untuk pemahaman kesalahan lain yang sering terjadi, baca juga kesalahan umum Docker di produksi. Contoh nyata untuk database ada di cara mengunci Redis dan MongoDB.

Pertanyaan yang sering diajukan

Apakah benar UFW tidak berpengaruh sama sekali pada Docker?

UFW tetap melindungi layanan yang berjalan langsung di host, seperti SSH. Yang dilewati adalah port yang dipublikasikan Docker ke container, karena paketnya diteruskan lewat rantai FORWARD.

Bolehkah saya menyetel "iptables": false di daemon.json?

Docker tidak menyarankannya karena jaringan container kemungkinan besar rusak. Lebih aman memakai bind 127.0.0.1, reverse proxy, dan rantai DOCKER-USER.

Apakah Docker rootless atau Podman menyelesaikan masalah ini?

Mode rootless mengurangi dampak bila container dibobol, tapi port yang dipublikasikan ke 0.0.0.0 tetap terbuka. Prinsip memilih alamat bind tetap berlaku.

Bagaimana mengecek port dari luar tanpa komputer kedua?

Kamu bisa memakai VPS lain, koneksi HP dengan aplikasi terminal, atau layanan pengecek port daring. Yang penting pengujian dilakukan dari luar server.

Checklist singkat:

  • Tidak ada 0.0.0.0 atau [::] di docker ps selain reverse proxy.
  • daemon.json berisi "ip": "127.0.0.1".
  • Database dan cache tanpa ports: atau terikat ke 127.0.0.1.
  • Docker socket tidak dipasang ke container sembarangan.
  • Uji nc dari luar sesuai harapan.

Sumber & referensi

Baca juga