Docker konteyner trafiğini yalnızca OpenVPN üzerinden nasıl yönlendirirsiniz
Ana makineyi normal uplink'inde bırakın ve tek bir konteyner alt ağını tünele koyun: NAT sırası, kaynak kuralları, OpenVPN iroute ve MSS kontrolleri.
Konteynerin paketi asla tun0 adresine ulaşmaz ve konteyner üzerindeki ek yetenekler bunu değiştirmez. Konuk ile tünel arasında iki rota araması vardır: biri konuğun ağ ad alanının içinde, biri çerçeve köprüye indikten sonra ana makinede, ikincisi ise farklı eşleşme kuralları olan başka bir tabloya danışır. Benim referans düzenim, üzerinde OpenVPN istemcisi çalışan bir Linux ana makinesidir; istemci 10.8.0.10 adresini tun0 üzerinde tutar, 172.18.0.0/16 üzerinde bir Docker köprüsü ve yalnızca tünel tarafının erişebildiği özel bir adres vardır. Hedef dar kalır: o konuk alt ağı tünelden çıkar, ana makine olağan uplink'ini korur.
VPN istemcisini konteynerin içinde çalıştırmak başka bir tasarımdır , kestirme değil. O zaman konteyner kendi tünelinin ve kimlik bilgilerinin sahibidir ve iş iptables'ta kalmak yerine Dockerfile'a taşınır.
Yönlendirme rotayı hedefe göre seçer, sonra NAT kaynağı yeniden yazar
Şu ayarlıyken net.ipv4.ip_forward=1 , iletilen paket ana tabloda yalnızca hedefe göre yönlendirilir. Kaynağın bu kararda hiçbir rolü yoktur. Ancak rota seçildikten sonra çekirdek POSTROUTING çalıştırır ve Docker'ın kendi kuralı tam orada durur: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE . Köprüden başka bir arayüze çıkan her şey çevrilir ve hangi adrese dönüştüğü, rotanın onu gönderdiği arayüze bağlıdır.
Bu kural bana sorulan belirtilerin çoğunu açıklar. Ana tabloda özel hedef için rota yoksa paket, ana makinenin fiziksel adresiyle uplink'ten çıkar. Sağlayıcınız hedefi düşürür veya uzak site hiç beklemediği bir RFC 1918 kaynağını düşürür ve tünel ayakta ve sağlıklıyken konteyner zaman aşımına uğrar. tun0 üzerinde yakalama hiçbir şey göstermez ve işaret budur. Rota gerçekten tun0 adresine işaret ediyorsa MASQUERADE kaynağı 10.8.0.10 adresine yeniden yazar ve aynı konteyner siz hiçbir şeye dokunmadan çalışır.
Konuğun kaynağını adlandıran aramayı isteyin. from ve iif olmadan ana makine kendi soketi için yanıt verir ve iletilen paketin asla almayacağı bir yolu bildirir:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetBu yanıt uplink'i adlandırıyorsa tünel resimde yoktur ve tünel tarafındaki hiçbir ayar yardımcı olmaz.
Konuğu tünelin kendi adresine taşıyın
İşleyen en küçük değişiklik, çıkışta açık bir kaynak yeniden yazımıdır. Ekleme yapmak yerine araya sokun: Docker zaten POSTROUTING başında oturur, bu yüzden -A ile eklenen kural Docker'ınkinin arkasında değerlendirilir ve asla tetiklenmez.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10Bunun kalıp kalmayacağını iki koşul belirler. Adres, istemcinin gerçekten tuttuğu bir adres olmalıdır, bu yüzden onu her istemci için ifconfig-push 10.8.0.10 255.255.255.0 içeren bir kayıtla sabitleyin; hiç atanmamış bir adresi adlandıran kural sessiz düşüşler üretir, hata değil. Ve uzak taraf bu kaynağı kabul edip yanıtları geri yönlendirmelidir, bu yüzden oradaki izin listesi konuk alt ağını değil tünel adresini adlandırır. İstemci adresi dinamikse, -j MASQUERADE ile -o tun0 arayüzün tuttuklarını izler, sabit bir değere karşı yazamayacağınız uzak politika pahasına.
Filtreleme diğer yarıdır. Docker FORWARD politikasını drop yapar ve her daemon yeniden başlatmasında sahip olduğu zincirleri temizler, bu yüzden yerel politikayı DOCKER-USER içine koyun, o hayatta kalır:
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 ACCEPTDönüş kuralı önemlidir: tun0 üzerine gelen yanıt, köprü tarafından açılan bağlantı için yalnızca giden yön kabul edildi diye kabul edilmez.
Ana makineyi bir kaynak kuralıyla doğrudan tutun
Tüm köprü tüneli kullanmalı ama ana makinede başka hiçbir şey kullanmamalıysa NAT'ı atlayın ve o kaynağa kendi tablosunu verin. Konuk adresleri o zaman baştan sona görünür kalır.
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 vpnboxKurallar ana tablodan önce danışılır ve from kaynakla eşleşir. Tünelin kendi dış paketleri ana makinenin fiziksel kaynağını taşır, bu yüzden from 172.18.0.0/16 ile asla eşleşmezler ve tünel kendini tekrar içine yönlendiremez. Bu kendini dışlama düzeni kararlı kılar.
Dönüş yolunun artık yardıma ihtiyacı var. Uzak taraf çevrilmemiş bir 172.18.0.x görür ve nerede yaşadığını bilmesi için bir neden yoktur. Sunucuda her istemci için bir dosya iroute 172.18.0.0 255.255.0.0 gerektirir, böylece sunucunun iç tablosu bu öneki bu istemciye verir, ve itilen bir route 172.18.0.0 255.255.0.0 gerekir, böylece diğer istemciler var olduğunu bilir.
Güvenmeden önce iki kontrol. Şunu çalıştırın ip route show default : redirect-gateway def1 ile tablo 0.0.0.0/1 ve 128.0.0.0/1 tutar tun0 üzerinden ve ana makine hiç doğrudan değildir, çünkü o yarım varsayılanlar default yerine geçmeden kazanır. ip rule add ile eklenen kurallar da yeniden başlatmada kaybolur, bu yüzden onları bir up betiğinde taşıyın.
Paket boyutu karşılaşacağınız ikinci arızadır
1500 baytlık bir köprü arayüzü, dış UDP ve IP başlıkları eklendiğinde tünele taşıyabileceğinden fazlasını verir. Küçük alışverişler geçer, yani DNS çözülür ve TLS el sıkışması tamamlanır, sonra daha büyük bir yükleme hiçbir yerde hata vermeden askıda kalır. Gönderen aynı segment boyutunu yinelemeye devam eder çünkü küçülmesini söyleyecek ICMP parçalanma gereksinimi iletisi asla gelmez.
Bunu arayüzde, MSS'te veya ikisinde birden düzeltin. Kenetleme konuğun işbirliğini gerektirmez:
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-pmtuKenetleme yalnızca TCP'yi yeniden yazar. UDP, QUIC dahil, bunun yerine daha düşük bir konuk arayüzü gerektirir, --opt com.docker.network.driver.mtu=1400 ile ağ oluştururken veya eşleşen compose anahtarıyla.
DNS ve yinelemeye değer kontroller
Ad çözümlemesi aynı yolu izler. Docker'ın gömülü çözümleyicisi 127.0.0.11 adresinde ana makinenin yapılandırılmış sunucularına iletir, böylece konteyner ana makinenin kullandığını devralır. Özel bölge yalnızca tünelden erişilebilen bir çözümleyici tarafından yanıtlanıyorsa konuğu o adrese yönlendirin ve getent hosts ile konteynerin içinde test edin, ana makinede değil.
- Başarısız isteği konuğun içinde çalıştırın; ana makine tarafındaki test başka bir kaynak adres kullanır ve sorunu gizler.
- Eklediğiniz NAT ve filtre kurallarındaki sayaçları okuyun. Sıfır eşleşmeli bir kural yönlendirme hatasıdır, politika hatası değil.
- SNAT kuralınızdaki tünel adresinin istemcinin gerçekten tuttuğu adres olduğunu doğrulayın.
- Sunucuyu bir takılma için suçlamadan önce konuğun MTU'sunu düşürün veya MSS'yi kenetleyin.
Listeyi baştan sona işleyin ve ilk arızada durun. Düzeltilmesi gereken katman odur; daha aşağıya eklenen her şey yalnızca kırılan sınırı gizler.