Paket kontainer tidak pernah mencapai tun0, dan kemampuan tambahan pada kontainer tidak akan mengubahnya. Ada dua pencarian rute antara tamu dan terowongan: satu di dalam ruang nama jaringan tamu, satu di host setelah frame mendarat di jembatan, dan yang kedua menanyakan tabel lain dengan aturan pencocokan berbeda. Tata letak acuan saya adalah host Linux yang menjalankan OpenVPN klien yang memegang 10.8.0.10 di tun0, jembatan Docker di 172.18.0.0/16, dan alamat privat yang hanya bisa dicapai sisi terowongan. Tujuannya tetap sempit: subnet tamu itu keluar melalui terowongan sementara host mempertahankan uplink biasanya.

Menjalankan klien VPN di dalam kontainer adalah desain lain, bukan jalan pintas. Kontainer lalu memiliki terowongan dan kredensialnya sendiri, dan pekerjaannya pindah ke Dockerfile alih-alih tetap di iptables.

Penerusan memilih rute berdasarkan tujuan, lalu NAT menulis ulang sumber

Dengan net.ipv4.ip_forward=1 disetel, paket yang diteruskan dirutekan hanya berdasarkan tujuan, di tabel utama. Sumber tidak berperan dalam keputusan itu. Baru setelah rute dipilih, kernel menjalankan POSTROUTING, dan di situlah aturan Docker sendiri berada: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. Segala sesuatu yang meninggalkan jembatan ke antarmuka lain diterjemahkan, dan alamat yang menjadi tujuannya bergantung pada antarmuka tempat rute mengirimnya.

Aturan itu menjelaskan sebagian besar gejala yang ditanyakan kepada saya. Jika tabel utama tidak punya rute untuk tujuan privat, paket keluar melalui uplink dengan alamat fisik host. Penyedia Anda membuang tujuannya, atau situs jauh membuang sumber RFC 1918 yang tidak pernah diharapkannya, dan kontainer kehabisan waktu sementara terowongan tetap hidup dan sehat. Tangkapan pada tun0 tidak menunjukkan apa pun, dan itulah tandanya. Jika rutenya memang menunjuk ke tun0, MASQUERADE menulis ulang sumber ke 10.8.0.10 dan kontainer yang sama bekerja tanpa Anda menyentuh apa pun.

Mintalah pencarian yang menyebut sumber tamu. Tanpa from dan iif host menjawab untuk soketnya sendiri dan melaporkan jalur yang tidak akan pernah diambil paket yang diteruskan:

ip route get 192.168.11.222 from 172.18.0.5 iif br-appnet

Jika jawaban itu menyebut uplink, terowongan tidak ada dalam gambaran dan tidak ada penyetelan di sisi terowongan yang membantu.

Pindahkan tamu ke alamat terowongan itu sendiri

Perubahan terkecil yang bekerja adalah penulisan ulang sumber yang eksplisit saat keluar. Sisipkan, jangan tambahkan di akhir: Docker sudah duduk di kepala POSTROUTING, jadi aturan yang ditambahkan dengan -A dievaluasi di belakang aturan Docker dan tidak pernah menyala.

iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10

Dua syarat menentukan apakah ini bertahan. Alamatnya harus yang benar-benar dipegang klien, jadi patok dengan entri per klien yang berisi ifconfig-push 10.8.0.10 255.255.255.0; aturan yang menyebut alamat yang tidak pernah diberikan menghasilkan penurunan senyap, bukan galat. Dan sisi jauh harus menerima sumber itu dan merutekan balasan kembali, karena itu daftar izin di sana menyebut alamat terowongan, bukan subnet tamu. Saat alamat klien dinamis, -j MASQUERADE dengan -o tun0 mengikuti apa pun yang dipegang antarmuka, dengan biaya kebijakan jauh yang tidak bisa Anda tulis terhadap nilai tetap.

Penyaringan adalah separuh lainnya. Docker menyetel kebijakan FORWARD ke drop dan membersihkan rantai miliknya pada setiap mulai ulang daemon, jadi taruh kebijakan lokal di DOCKER-USER, yang bertahan:

iptables -I DOCKER-USER -i br-appnet -o tun0 -j ACCEPT
iptables -I DOCKER-USER -i tun0 -o br-appnet -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Aturan balik itu penting: balasan yang tiba di tun0 untuk koneksi yang dibuka jembatan tidak diterima hanya karena arah keluarnya diterima.

Pertahankan host tetap langsung dengan aturan sumber

Ketika seluruh jembatan harus memakai terowongan tetapi tidak ada yang lain di host, lewati NAT dan beri sumber itu tabelnya sendiri. Alamat tamu lalu tetap terlihat dari ujung ke ujung.

echo "200 vpnbox" >> /etc/iproute2/rt_tables
ip rule add from 172.18.0.0/16 lookup vpnbox pref 1000
ip route add default dev tun0 table vpnbox

Aturan dikonsultasikan sebelum tabel utama, dan from cocok dengan sumber. Paket luar terowongan itu sendiri membawa sumber fisik host, jadi tidak pernah cocok dengan from 172.18.0.0/16 dan terowongan tidak bisa merutekan dirinya kembali ke dirinya sendiri. Pengecualian diri itulah yang membuat tata letaknya stabil.

Jalur balik sekarang butuh bantuan. Sisi jauh melihat 172.18.0.x yang tidak diterjemahkan dan tidak punya alasan tahu di mana ia tinggal. Di server, file per klien membutuhkan iroute 172.18.0.0 255.255.0.0 agar tabel internal server menyerahkan prefiks itu ke klien ini, dan route 172.18.0.0 255.255.0.0 yang didorong agar klien lain tahu itu ada.

Dua pemeriksaan sebelum memercayainya. Jalankan ip route show default: dengan redirect-gateway def1 tabel memegang 0.0.0.0/1 dan 128.0.0.0/1 lewat tun0, dan host sama sekali tidak langsung, karena setengah-default itu menang tanpa mengganti default. Aturan yang ditambahkan dengan ip rule add juga hilang saat reboot, jadi bawa di skrip up.

Ukuran paket adalah kegagalan kedua yang Anda temui

Antarmuka jembatan pada 1500 byte memberi terowongan lebih banyak daripada yang bisa dibawanya setelah header UDP dan IP luar ditambahkan. Pertukaran kecil lewat, jadi DNS terurai dan jabat tangan TLS selesai, lalu unggahan lebih besar menggantung tanpa galat di mana pun. Pengirim terus mengulang ukuran segmen yang sama karena pesan ICMP tentang kebutuhan fragmentasi, yang akan menyuruhnya mengecil, tidak pernah tiba.

Perbaiki di antarmuka, di MSS, atau keduanya. Penjepitan tidak butuh kerja sama tamu:

ip link set dev tun0 mtu 1400
iptables -t mangle -A FORWARD -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Penjepitan hanya menulis ulang TCP. UDP, termasuk QUIC, justru membutuhkan antarmuka tamu yang lebih rendah, dengan --opt com.docker.network.driver.mtu=1400 saat membuat jaringan atau kunci compose yang sesuai.

DNS dan pemeriksaan yang layak diulang

Resolusi nama mengikuti jalur yang sama. Resolver tertanam Docker di 127.0.0.11 meneruskan ke server yang dikonfigurasi host, jadi kontainer mewarisi apa pun yang dipakai host. Ketika zona privat hanya dijawab oleh resolver yang bisa dicapai lewat terowongan, arahkan tamu ke alamat itu, dan uji dengan getent hosts di dalam kontainer, bukan di host.

  • Jalankan permintaan yang gagal di dalam tamu; uji di sisi host memakai alamat sumber lain dan menyembunyikan masalahnya.
  • Baca penghitung pada aturan NAT dan filter yang Anda tambahkan. Aturan tanpa satu pun kecocokan adalah kesalahan perutean, bukan kesalahan kebijakan.
  • Pastikan alamat terowongan di aturan SNAT Anda adalah yang benar-benar dipegang klien.
  • Turunkan MTU tamu atau jepit MSS sebelum menyalahkan server atas kemacetan.

Telusuri daftar itu dan berhenti pada kegagalan pertama. Lapisan itulah yang harus diperbaiki; apa pun yang ditambahkan lebih jauh hanya menyembunyikan batas yang patah.

Artikel terkait