გაასწორეთ Docker-ისა და WSL-ის ტრაფიკი, რომელიც ჰოსტის VPN-ს უგულებელყოფს
კონტეინერები და WSL ინარჩუნებენ საკუთარ მარშრუტებს, გადამწყვეტსა და გადაგზავნის გზას, ამიტომ მომუშავე ჰოსტის გვირაბი მათ ძველ DNS-თან ან გასასვლელის გარეშე ტოვებს. გაასწორეთ ყოველი ფენა.
ამ გაზაფხულზე დილის უმეტესი ნაწილი იმ კონტეინერის კვალდაკვალ გავატარე, რომელსაც ერთი შიდა სახელის ამოხსნაც კი არ შეეძლო, მაშინ როცა გვერდით მდგარი ბრაუზერი ვიკის უჩივლელად ხსნიდა. გვირაბი აწეული იყო, ბრაუზერი ამას ამტკიცებდა, კონტეინერი კი სხვა ქსელურ სახელთა სივრცეში იდგა, სხვა მარშრუტების ცხრილითა და სხვა გადამწყვეტით. შემდეგია ის თანმიმდევრობა, რომელსაც ახლა ვამოწმებ: ჯერ მარშრუტების ცხრილები, მეორე სახელების ამოხსნა, მესამე გადაგზავნა და ბოლოს ჰოსტის პლატფორმა. გაჩერდით პირველ საფეხურზე, რომელიც უკვე ვარდება, რადგან წესების დამატება უფრო დაბლა დასტაში საზღვარს მალავს და არა გადაადგილებს.
შეადარეთ სამივე მარშრუტების ცხრილი, სანამ წესს შეცვლით
Docker-ის ნაგულისხმევ ხიდზე მყოფ კონტეინერს აქვს ერთი ნაგულისხმევი მარშრუტი docker0-ის კარიბჭისკენ და წარმოდგენა არ აქვს, რომ tun0 არსებობს. ჰოსტს აქვს tun0, VPN-ის მიერ გაწეული მარშრუტები და, შესაძლოა, პოლიტიკის წესი, რომელიც საცდელ ტრაფიკს თქვენი მოლოდინისგან განსხვავებული ინტერფეისით აგზავნის. WSL მესამე ცხრილს ამატებს საკუთარი ვირტუალური ადაპტერის უკან. დაბეჭდეთ სამივე, სანამ რომელიმეს შეცვლით.
| Context | Command | What a wrong answer looks like |
|---|---|---|
| Host | ip route get 10.8.0.20 | Leaves a physical interface instead of tun0 |
| Container | ip route | Only 172.17.0.0/16 via 172.17.0.1 |
| Container DNS | cat /etc/resolv.conf | A public resolver, or an upstream the tunnel never pushed |
| WSL | ip addr; ip route | Only the WSL subnet, no Windows VPN adapter |
თუ კონტეინერის ერთადერთი მარშრუტი ხიდია და tun0 არასდროს ჩნდება მის სახელთა სივრცეში, ქვემოთ არცერთი არ დაგეხმარებათ: არცერთი სახელთა სერვერის ცვლილება მარშრუტს არ ქმნის და არცერთი მარშრუტი არ ქმნის გვირაბის იდენტობას, რომელსაც თანამოლაპარაკე მიიღებს. თანმიმდევრობა მნიშვნელოვანია, რადგან DNS-ის ჩავარდნა და მარშრუტის ჩავარდნა ჭურვის შიგნიდან ერთნაირად გამოიყურება.
მიუთითეთ გადამწყვეტი გვირაბზე და არა საჯაროზე
ჰოსტზე, resolvectl status ჩამოთვლის DNS სერვერებს კავშირის მიხედვით, და readlink -f /etc/resolv.conf გეუბნებათ, არის თუ არა ფაილი systemd-ის stub, NetworkManager-ის ფაილი თუ რაღაც, რაც მომწოდებლის კლიენტმა გადაწერა. მომხმარებლის განსაზღვრულ ქსელში მყოფი კონტეინერის შიგნით, /etc/resolv.conf ასახელებს 127.0.0.11, Docker-ის ჩაშენებულ გადამწყვეტს, რომელიც გადასცემს იმ upstream-ს, რომლითაც დემონი იყო კონფიგურირებული. ნაგულისხმევი ხიდის კონტეინერმა კი შექმნისას ჰოსტის ფაილის ასლი მიიღო და ინახავს მას.
სწორედ ეს ასლია მიზეზი, რომ კონტეინერის გაშვების შემდეგ აწეული გვირაბი კონტეინერს ძველი upstream-ის კითხვას ტოვებს. ან გადასცეთ VPN-ის გადამწყვეტი ცალსახად docker run --dns 10.8.0.53-ით, ან დააყენეთ "dns": ["10.8.0.53"] /etc/docker/daemon.json -ში და გადატვირთეთ დემონი, ან გვირაბის აწევის შემდეგ ხელახლა შექმენით კონტეინერი. ხელახალი შექმნა ჩვეულებრივ იაფია, ვიდრე საათის წინ სწორი გადამწყვეტის გამართვა.
საჯარო გადამწყვეტით ჩანაცვლება ის შეკეთებაა, რომელიც სიმპტომს ინარჩუნებს. ის ინტერნეტს ხსნის და ყველა კერძო ზონაში ვარდება, და შეიძლება ჩუმად გვერდი აუაროს იმ ფილტრაციას, რომლისთვისაც გვირაბი არსებობს. თუ სცდით ჩაწერას მომუშავე კონტეინერის /etc/resolv.conf-ში, დაელოდეთ Device or resource busy-ს: Docker ამ ფაილს bind-mount-ს უკეთებს, ამიტომ რედაქტირებას ჯერ გაშლა სჭირდება. --dns -ის შექმნისას დაყენება ის თანმიმდევრობაა, რომელიც გადატვირთვას გადაურჩება.
გადასცეთ და თარგმნეთ, როცა კონტეინერები გვირაბს სესხულობენ
ნაგულისხმევი ხიდით ჰოსტი მარშრუტიზატორია docker0-სა და tun0-ს შორის და ნაგულისხმევად არცერთ ამ საქმეს არ აკეთებს. ჩართეთ გადაგზავნა და თარგმნეთ წყაროს მისამართი, რომ თანამოლაპარაკემ ჰოსტის გვირაბის მისამართი დაინახოს და არა ის, რომლისკენაც უკუმარშრუტი არ აქვს (შეცვალეთ tun0 იმ სახელით, რასაც თქვენი კლიენტი ინტერფეისს უწოდებს):
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
iptables -A FORWARD -i docker0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o docker0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTFORWARD წესები მნიშვნელოვანია იმ ჰოსტებზე, რომელთა გადაგზავნის პოლიტიკა DROP-ია. Docker საკუთარ ჯაჭვებს ჩასვამს, ამიტომ შეამოწმეთ პაკეტით და არა კითხვით iptables -S: კონტეინერიდან, ip route get 10.8.0.20 უნდა ასახელებდეს ხიდის კარიბჭეს, და nc -vz 10.8.0.20 443 უნდა დაუკავშირდეს ან დრო ამოეწუროს გვირაბზე და არა გადამწყვეტზე.
MASQUERADE-ის გარეშე თანამოლაპარაკეს უკუმარშრუტი სჭირდება 172.17.0.0/16-ისკენ, რომელსაც დისტანციური წვდომის VPN-ების უმეტესობა არ გაწვდის. აქვე ტყდება გეგმა პოლიტიკაზე დაფუძნებულ კლიენტთან: თუ ის მხოლოდ გარკვეული UID-ების ან cgroups-ების ტრაფიკს იჭერს, თქვენი გადაგზავნის წესები პაკეტს გვირაბის შიგნით ვერასდროს ხედავს და შესწორება კლიენტის მარშრუტიზაციის პოლიტიკას ეკუთვნის და არა iptables-ს. ხელით დამატებული წესები გადატვირთვისას ქრება, ამიტომ ჩასვით ისინი systemd-ის ერთეულში ან netfilter-persistent-ის ცხრილში და გადატვირთვის შემდეგ ხელახლა შეამოწმეთ, რადგან გვიან აწეულმა გვირაბმა შეიძლება ისინი ჩამორეცხოს.
მოათავსეთ გვირაბი კონტეინერის შიგნით, როცა მიზანი იზოლაციაა
თუ ერთმა კონტეინერმა გარკვეული კვანძის გავლით უნდა გავიდეს, გაუშვით კლიენტი იმ კონტეინერში. მას სჭირდება --cap-add NET_ADMIN და --device /dev/net/tun, და შემდეგ მას აქვს საკუთარი tun0, მარშრუტები და ფაირვოლი. თუ სხვა კონტეინერები მასში გაივლიან, მას ასევე სჭირდება გადაგზავნა და MASQUERADE წესი კონტეინერის შიგნით, და იმ თანამოლაპარაკეებს სჭირდებათ ცალსახა მარშრუტი ან პროქსი, რომელიც მისკენ მიუთითებს; Docker-ის ნაგულისხმევი ხიდი ამ მარშრუტს თქვენ ნაცვლად არ ამატებს.
ჰოსტის ქსელი, docker run --network host, იაფი ვარიანტია: კონტეინერი უერთდება ჰოსტის სახელთა სივრცეს და გვირაბის მარშრუტებს პირდაპირ ხედავს. ის ასევე ტოვებს ქსელურ იზოლაციას, იწვევს პორტების შეჯახებას და პრაქტიკაში Linux-ის ვარიანტია. გამოქვეყნებული პორტი შემცვლელი არ არის. -p 8080:80 -ს სერვისი ჰოსტიდან მისაწვდომს ხდის; ის შიდა 10.x მისამართებს კონტეინერის შიგნიდან მისაწვდომს არ ხდის, და ორივეს არევა ის გზაა, რომლითაც მარშრუტიზაციის პრობლემა გამოჩენილ მონაცემთა ბაზის პორტად იქცევა.
გაარჩიეთ სახელის ჩავარდნა გადაფარვადი ქვექსელისგან
Docker-ის ნაგულისხმევი ხიდი 172.17.0.0/16-ია და ბევრი კორპორატიული VPN-ის აუზი 172.16.0.0/12-ის შიგნითაა. როგორც კი გვირაბი იმ დიაპაზონს დაეუფლება, კონტეინერისთვის განკუთვნილი ტრაფიკი შეიძლება გვირაბში გადაიგზავნოს. სახელები ვარდება, შეცდომა DNS-ს ჰგავს, და პრობლემა მისამართებშია. შეამოწმეთ დანიშნულება და არა გადამწყვეტი: ip route get 172.17.0.5 ჰოსტზე, შემდეგ შეადარეთ ხიდის პრეფიქსი VPN-ის აუზს და docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'-ს.
შეკეთება ერთი დიაპაზონის გადაადგილებაა: ან Docker-ისთვის სხვა ნაგულისხმევი მისამართების აუზი daemon.json -ში, ან სხვა აუზი კონცენტრატორზე. კიდევ ერთი სახელთა სერვერის დამატება აქ არაფერს აკეთებს, და ssh -b-იც არა: კლიენტის ინტერფეისზე მიბმა წყაროს მისამართს ცვლის და არა სამიზნე მარშრუტს, რომელიც გვირაბში მიდის. სწრაფი შრომის დანაწილება: სახელი, რომელიც ჰოსტზე იხსნება და კონტეინერში არა, გადამწყვეტის პრობლემაა, ხოლო პირდაპირი IP კავშირი, რომელიც კონტეინერში იკიდებს და ჰოსტზე მუშაობს, მარშრუტის ან გადაგზავნის ჯაჭვის პრობლემაა.
WSL და Docker Desktop Linux-ის წესებს არ მისდევენ
თავის ნაგულისხმევ NAT რეჟიმში WSL საკუთარი ვირტუალური ადაპტერის უკან ზის და არ იღებს Windows VPN-ის შიდა მარშრუტებს ან DNS სუფიქსებს, ამიტომ ბრაუზერი მუშაობს და ping დისტრიბუციის შიგნით არა. Windows 11-ის ბოლო build-ებში ხელმისაწვდომი სარკული ქსელი სამაგიეროდ ჰოსტის ინტერფეისებს იზიარებს, მაგრამ ის უნდა ჩაირთოს .wslconfig -ში და დისტრიბუცია გადატვირთოთ wsl --shutdown-ით. დაადასტურეთ აქტიური რეჟიმი wslinfo --networking-mode -ით, სანამ შესწორებას გამოიყენებთ, რადგან NAT-ის პასუხი არასწორია სარკული რეჟიმისთვის და პირიქით.
Docker Desktop კიდევ ერთ ფენას ამატებს. Windows-ზე Linux კონტეინერები ისტორიულად vpnkit-ად წოდებული NAT განხორციელების უკან არიან, და მათი გზა განსხვავდება მშობლიური Linux ჰოსტისგან. Windows კონტეინერები Hyper-V-ის ვირტუალური კომუტატორის მეშვეობით უერთდებიან, და კორპორატიულ კლიენტს შეუძლია ამ კომუტატორიდან ტრაფიკის დაბლოკვა, ამიტომ ერთსა და იმავე მანქანაზე Linux კონტეინერებმა შეიძლება ქსელს მიაღწიონ, მაშინ როცა Windows კონტეინერებს არაფერი აქვთ. ფიზიკურ ადაპტერზე მიბმული გარე კომუტატორი ჩვეულ გადაწყვეტას წარმოადგენს და კონტეინერის ქსელურ იდენტობას იმგვარად ცვლის, რასაც პოლიტიკა შეიძლება არ დაუშვებდეს.
სანამ კიდევ ერთ წესს დაწერთ, თანმიმდევრულად გაიარეთ ოთხივე ფენა და გაჩერდით იქ, სადაც ერთ-ერთი მათგანი არასწორია:
- კონტეინერის შიგნით: აღწევს თუ არა
ip routeკერძო პრეფიქსამდე, და ასახელებს თუ არა/etc/resolv.confupstream-ს, რომელიც მას ხსნის? - ჰოსტზე: ჩართულია თუ არა გადაგზავნა, მიბმულია თუ არა NAT გვირაბის ინტერფეისზე, და იღებს თუ არა გადაგზავნის ჯაჭვი ხიდიდან გვირაბს?
- თანამოლაპარაკესთან: იცის თუ არა უკუგზა, თუ ენდობით NAT-ს კონტეინერის ქვექსელის დასამალად?
- პლატფორმაზე: WSL NAT თუ სარკულ რეჟიმშია, და კონტეინერი Linux-ია თუ Windows?
ხელახლა გაუშვით ყოველი ტესტი ახალ კონტეინერში ან ჭურვიში. გრძელვადიანი პროცესი ქეშირებული პასუხით ან დამყარებული კავშირით გააგრძელებს ჩავარდნას მას შემდეგაც, რაც კონფიგურაცია სწორი გახდება, და ეს ჩავარდნა ზუსტად იმ შესწორებას ჰგავს, რომელმაც არ იმუშავა.