Meningkatkan skill pf ke level lanjutan: mengonfigurasi NAT outbound dan port forwarding dengan rdr, membangun dynamic ruleset memakai tables dengan persist, memodularisasi konfigurasi lewat anchors, serta mengandalkan state tracking keep state.

Di episode 12 sebelumnya, kalian membangun ruleset pf dasar dengan block dan pass. Tapi firewall yang hanya menutup dan membuka port hanyalah separuh jalan. Episode ini membawa kalian ke kemampuan lanjutan pf: NAT, redirect, tables, dan anchors — alat yang mengubah OpenBSD dari sekadar firewall menjadi router, gateway, dan filter pintar.
Setelah episode ini, kalian bisa membangun skenario nyata: mesin gateway yang membagi internet ke LAN, meneruskan port ke server internal, memblokir daftar alamat mencurigakan, dan mengorganisasi ruleset besar tanpa menjadi kacau.
NAT (Network Address Translation) menerjemahkan alamat internal (misal 192.168.1.0/24) ke satu alamat publik saat keluar. Dengan pf, ini satu baris:
pass in on int_if from 192.168.1.0/24 nat-to egressAturan ini meneruskan semua trafik dari LAN ke luar lewat antarmuka egress, mengganti alamat sumber dengan alamat publik. nat-to menggantikan sintaks nat lama pada pf modern.
pfctl -sn
pfctl -sspfctl -sn menampilkan aturan NAT yang aktif, pfctl -ss menampilkan state termasuk translasi alamat. Keduanya wajib untuk debugging.
rdr (redirect) meneruskan trafik dari port publik ke server internal. Ini cara mempublikasikan layanan tanpa server internal terpapar langsung:
pass in on egress proto tcp to port 8080 rdr-to 192.168.1.10 port 80
pass in on egress proto tcp to port 2222 rdr-to 192.168.1.10 port 22Permintaan ke port 8080 publik diteruskan ke port 80 server 192.168.1.10; SSH publik di port 2222 diteruskan ke SSH internal. Untuk HTTP publik, pola yang lazim adalah rdr-to 127.0.0.1 port 80 di gateway itu sendiri:
pass in on egress proto tcp to port 443 rdr-to 127.0.0.1 port 443Warning
rdr hanya mengubah tujuan — ia tidak melewati firewall. Pastikan ada aturan pass yang cocok untuk trafik tersebut, dan untuk layanan yang diteruskan ke server internal, perhatikan aturan pass tambahan di dalamnya. RDR dan filtering bekerja berdampingan, bukan menggantikan satu sama lain.
Tables menyimpan daftar alamat (IP, network) yang bisa diubah tanpa me-reload seluruh ruleset. Definisi dengan persist membuat table tetap hidup walau ruleset di-reload:
table <bruteforce> persist
table <badhosts> persist file "/etc/pf.badhosts"
block quick from <bruteforce>
block from <badhosts>Aturan block quick from <bruteforce> langsung memblokir semua yang masuk daftar — tanpa mengevaluasi aturan lain. Data table bisa ditambah dan dihapus saat runtime:
pfctl -t badhosts -T add 203.0.113.5
pfctl -t badhosts -T show
pfctl -t badhosts -T delete 203.0.113.5Table adalah mekanisme inti untuk mitigasi brute force — dipakai di episode 14 bersama OpenSSH hardening.
Saat ruleset membesar, satu file menjadi sulit dirawat. Anchors memecah ruleset menjadi bagian-bagian yang independen:
anchor "ssh"
anchor "web"
load anchor "web" from "/etc/pf.web.conf"Anchors bisa dimuat dari file eksternal atau bahkan diatur dinamis. Contoh anchors yang sering dipakai: satu anchor untuk aturan SSH (termasuk rate-limiting), satu untuk web, satu lagi untuk layanan lain. Setiap bagian bisa diuji dan di-reload tanpa menyentuh yang lain.
pfctl -a ssh -s rules
pfctl -a web -f /etc/pf.web.confpfctl -a menargetkan anchor tertentu. Ini juga mekanisme yang dipakai blacklistd dan alat mitigasi lain untuk menambahkan aturan dinamis.
pf melacak state — setiap koneksi yang diizinkan diingat sehingga paket balasan otomatis diizinkan tanpa aturan eksplisit:
pass in on egress proto tcp to port 22 keep state
pass out on egress proto tcp to any keep statekeep state membuat pf mengingat koneksi; paket dari arah sebaliknya yang masih satu koneksi diizinkan tanpa aturan pass baru. Ini alasan mengapa NAT bekerja mulus: state menyimpan translasi alamat. Saat ini keep state adalah default, tapi menuliskannya eksplisit membuat niat kalian jelas.
Aturan dinamis juga bisa dikombinasikan dengan max untuk membatasi jumlah state per alamat — bentuk paling sederhana anti-DoS:
pass in on egress proto tcp to port 22 max-src-conn 10, max-src-conn-rate 5/60Kalimat itu membatasi maksimal 10 koneksi per sumber, maksimal 5 koneksi baru per 60 detik. Ideal untuk layanan yang rawan brute force.
Mari gabungkan semuanya dalam satu gateway sederhana:
int_if = "em1"
lan_net = "192.168.1.0/24"
set skip on lo
table <badhosts> persist file "/etc/pf.badhosts"
match in all scrub (no-df max-mss 1440)
block return log in all
pass in on egress proto tcp to port 22 max-src-conn 10, max-src-conn-rate 5/60
pass in on egress proto { tcp udp } to port 53
pass in on egress proto tcp to port 80
pass in on egress proto tcp to port 443
pass out on egress proto { tcp udp } to any
pass in on $int_if from $lan_net nat-to egress
pass in on egress proto tcp to port 8080 rdr-to 192.168.1.10 port 80
block from <badhosts>Ingat: agar bisa meneruskan antar antarmuka, IP forwarding harus aktif (episode 8). Firewall tanpa forwarding adalah filter statis; dengan forwarding, ia menjadi gateway.
Info
Selalu jaga jalur administrasi. Saat gateway mengatur forwarding dan NAT, kesalahan kecil bisa memutus akses kalian ke sistem — dan untuk gateway, akses via konsol atau out-of-band sangat disarankan.
Di episode 13 ini kalian menaikkan level pf: melakukan NAT outbound dengan nat-to, port forwarding dengan rdr-to, membangun daftar alamat hidup dengan tables dan persist, memodularisasi ruleset dengan anchors, serta mengandalkan state tracking dan aturan pembatas untuk pertahanan dinamis.
Inti yang harus dibawa pulang:
nat-to egress; port forwarding memakai rdr-to.persist) bisa diubah saat runtime tanpa reload ruleset.keep state dan pembatas koneksi membuat firewall tanggap terhadap serangan.Di episode 14 selanjutnya, kita akan mengamankan akses jauh: OpenSSH & hardening — mengonfigurasi sshd_config modern dengan kunci ed25519, mematikan password dan root login, memanfaatkan pf tables untuk anti brute force, dan memakai tunneling SSH.