როგორ გავატაროთ მხოლოდ Docker კონტეინერების ტრაფიკი OpenVPN-ით
დატოვეთ ჰოსტი ჩვეულებრივ uplink-ზე და ერთი კონტეინერის ქვექსელი მოათავსეთ გვირაბში: NAT-ის რიგი, წყაროს წესები, OpenVPN iroute და MSS შემოწმებები.
კონტეინერის პაკეტი არასდროს აღწევს tun0-მდე და კონტეინერზე დამატებითი შესაძლებლობები ამას არ შეცვლის. სტუმარსა და გვირაბს შორის ორი მარშრუტის ძებნა დგას: ერთი სტუმრის ქსელის სახელთა სივრცეში, ერთი ჰოსტზე მას შემდეგ, რაც კადრი ხიდზე დაეშვება, ხოლო მეორე სხვა ცხრილს ეკითხება სხვა შესაბამისობის წესებით. ჩემი საცნობარო განლაგებაა Linux ჰოსტი, რომელზეც მუშაობს OpenVPN კლიენტი, რომელიც ინახავს 10.8.0.10 -ს tun0 -ზე, Docker ხიდი 172.18.0.0/16 -ზე, და კერძო მისამართი, რომელსაც მხოლოდ გვირაბის მხარე აღწევს. მიზანი ვიწრო რჩება: ეს სტუმარი ქვექსელი გადის გვირაბით, სანამ ჰოსტი ინარჩუნებს ჩვეულებრივ uplink-ს.
VPN კლიენტის კონტეინერში გაშვება სხვა დიზაინია, არა მოკლე გზა. მაშინ კონტეინერს ეკუთვნის მისი გვირაბი და რწმუნებები, და სამუშაო გადადის Dockerfile-ში iptables-ში დარჩენის ნაცვლად.
გადაგზავნა ირჩევს მარშრუტს დანიშნულების მიხედვით, შემდეგ NAT ხელახლა წერს წყაროს
როდესაც დაყენებულია net.ipv4.ip_forward=1, გადაგზავნილი პაკეტი მარშრუტიზდება მხოლოდ დანიშნულების მიხედვით, მთავარ ცხრილში. წყარო ამ გადაწყვეტილებაში მონაწილეობას არ იღებს. მხოლოდ მარშრუტის არჩევის შემდეგ ასრულებს ბირთვი POSTROUTING -ს, და სწორედ იქ ზის Docker-ის საკუთარი წესი: -s 172.18.0.0/16 ! -o br-appnet -j MASQUERADE. ყველაფერი, რაც ტოვებს ხიდს სხვა ინტერფეისისკენ, ითარგმნება, და მისამართი, რომელიც ხდება, დამოკიდებულია იმ ინტერფეისზე, სადაც მარშრუტმა გაგზავნა.
ეს წესი ხსნის სიმპტომების უმეტესობას, რომლებზეც მეკითხებიან. თუ მთავარ ცხრილში კერძო დანიშნულების მარშრუტი არ არის, პაკეტი გადის uplink-ით ჰოსტის ფიზიკური მისამართით. თქვენი პროვაიდერი ყრის დანიშნულებას, ან შორეული საიტი ყრის RFC 1918 წყაროს, რომელსაც არასდროს ელოდა, და კონტეინერი დროში იწურება, სანამ გვირაბი ზევით და ჯანმრთელი რჩება. ჩაწერა tun0 -ზე არაფერს აჩვენებს, და ეს არის ნიშანი. თუ მარშრუტი მართლაც მიუთითებს tun0 -ზე, MASQUERADE ხელახლა წერს წყაროს 10.8.0.10 -ზე, და იგივე კონტეინერი მუშაობს თქვენ მიერ არაფრის შეხების გარეშე.
მოითხოვეთ ძებნა, რომელიც ასახელებს სტუმრის წყაროს. from და iif -ის გარეშე ჰოსტი პასუხობს საკუთარი სოკეტისთვის და იუწყება გზას, რომელსაც გადაგზავნილი პაკეტი არასდროს აიღებს:
ip route get 192.168.11.222 from 172.18.0.5 iif br-appnetთუ ეს პასუხი ასახელებს uplink-ს, გვირაბი სურათში არ არის და გვირაბის მხრიდან არც ერთი პარამეტრი არ დაეხმარება.
გადაიყვანეთ სტუმარი გვირაბის საკუთარ მისამართზე
ყველაზე პატარა მომუშავე ცვლილება გასასვლელზე წყაროს მკაფიო გადაწერაა. ჩასვით ის, ნაცვლად იმისა, რომ დაუმატოთ: Docker უკვე ზის POSTROUTING -ის თავში, ამიტომ -A -ით დამატებული წესი ფასდება Docker-ის წესის შემდეგ და არასდროს ამოქმედდება.
iptables -t nat -I POSTROUTING 1 -s 172.18.0.0/16 -o tun0 -j SNAT --to-source 10.8.0.10ორი პირობა წყვეტს, გაძლებს თუ არა ეს. მისამართი უნდა იყოს ის, რაც კლიენტს ნამდვილად აქვს, ამიტომ მიამაგრეთ ის თითოეული კლიენტის ჩანაწერით, რომელიც შეიცავს ifconfig-push 10.8.0.10 255.255.255.0 -ს; წესი, რომელიც ასახელებს არასდროს მინიჭებულ მისამართს, წარმოქმნის ჩუმ ჩამოშლებებს და არა შეცდომას. და შორეულმა მხარემ უნდა მიიღოს ეს წყარო და უკან დააბრუნოს პასუხები, ამიტომ იქაური დაშვებულთა სია ასახელებს გვირაბის მისამართს და არა სტუმრის ქვექსელს. როცა კლიენტის მისამართი დინამიკურია, -j MASQUERADE -თან ერთად -o tun0 მიჰყვება იმას, რაც ინტერფეისს აქვს, შორეული პოლიტიკის ხარჯზე, რომლის წინააღმდეგაც ფიქსირებულ მნიშვნელობას ვერ დაწერთ.
ფილტრაცია მეორე ნახევარია. Docker აყენებს FORWARD პოლიტიკას drop-ზე და ყოველ დემონის გადატვირთვაზე ასუფთავებს საკუთარ ჯაჭვებს, ამიტომ ლოკალური პოლიტიკა ჩადეთ DOCKER-USER -ში, რომელიც გადარჩება:
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დაბრუნების წესი მნიშვნელოვანია: tun0 -ზე მისული პასუხი ხიდით გახსნილი კავშირისთვის არ მიიღება მხოლოდ იმიტომ, რომ გასასვლელი მიმართულება მიღებული იყო.
შეინარჩუნეთ ჰოსტი პირდაპირ წყაროს წესით
როცა მთელმა ხიდმა უნდა გამოიყენოს გვირაბი, მაგრამ ჰოსტზე სხვა არაფერი, გამოტოვეთ NAT და მიეცით ამ წყაროს საკუთარი ცხრილი. სტუმრის მისამართები მაშინ თვალსაჩინო რჩება ბოლომდე.
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წესები განიხილება მთავარ ცხრილამდე, და from ემთხვევა წყაროს. გვირაბის საკუთარი გარე პაკეტები ატარებენ ჰოსტის ფიზიკურ წყაროს, ამიტომ ისინი არასდროს ემთხვევა from 172.18.0.0/16 -ს და გვირაბს არ შეუძლია საკუთარი თავის უკან დაბრუნება. სწორედ ეს თვითგამორიცხვა ხდის განლაგებას სტაბილურს.
დაბრუნების გზას ახლა დახმარება სჭირდება. შორეული მხარე ხედავს აუთარგმნელ 172.18.0.x -ს და არ აქვს მიზეზი იცოდეს, სად ცხოვრობს. სერვერზე თითოეული კლიენტის ფაილს სჭირდება iroute 172.18.0.0 255.255.0.0 , რომ სერვერის შიდა ცხრილმა ეს პრეფიქსი ამ კლიენტს მისცეს, და გაგზავნილი route 172.18.0.0 255.255.0.0 , რომ სხვა კლიენტებმა იცოდნენ, რომ ის არსებობს.
ორი შემოწმება ნდობამდე. გაუშვით ip route show default -ი: redirect-gateway def1 -ით ცხრილი ინახავს 0.0.0.0/1 და 128.0.0.0/1 -ს tun0 -ის გავლით, და ჰოსტი სულაც არ არის პირდაპირი, რადგან ეს ნახევრად-ნაგულისხმევები იმარჯვებენ default -ის ჩანაცვლების გარეშე. ip rule add -ით დამატებული წესები ასევე ქრება გადატვირთვისას, ამიტომ ატარეთ ისინი up სკრიპტში.
პაკეტის ზომა მეორე მარცხია, რომელსაც წააწყდებით
1500 ბაიტიანი ხიდის ინტერფეისი გვირაბს აძლევს იმაზე მეტს, ვიდრე მას შეუძლია ატაროს, როცა გარე UDP და IP სათაურები ემატება. პატარა გაცვლები გადის, ამიტომ DNS წყდება და TLS ხელის ჩამორთმევა სრულდება, შემდეგ უფრო დიდი ატვირთვა იკიდება ყოველგვარი შეცდომის გარეშე. გამომგზავნი იმეორებს იმავე სეგმენტის ზომას, რადგან ICMP-ის შეტყობინება ფრაგმენტაციის საჭიროების შესახებ, რომელიც ეტყოდა შემცირებას, არასდროს აღწევს.
გაასწორეთ ინტერფეისზე, MSS-ზე ან ორივეზე. დაჭიმვას სტუმრის თანამშრომლობა არ სჭირდება:
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დაჭიმვა გადაწერს მხოლოდ TCP-ს. UDP-ს, QUIC-ის ჩათვლით, სამაგიეროდ სჭირდება დაბალი სტუმრის ინტერფეისი, --opt com.docker.network.driver.mtu=1400 -ით ქსელის შექმნისას ან შესაბამისი compose გასაღებით.
DNS და შემოწმებები, რომლებიც ღირს გამეორება
სახელების ამოხსნა იმავე გზას მიჰყვება. Docker-ის ჩაშენებული ამომხსნელი 127.0.0.11 -ზე გადასცემს ჰოსტის კონფიგურირებულ სერვერებს, ამიტომ კონტეინერი მემკვიდრეობით იღებს იმას, რასაც ჰოსტი იყენებს. როცა კერძო ზონას პასუხობს მხოლოდ გვირაბით ხელმისაწვდომი ამომხსნელი, მიმართეთ სტუმარი ამ მისამართზე, და შეამოწმეთ getent hosts -ით კონტეინერის შიგნით და არა ჰოსტზე.
- გაუშვით წარუმატებელი მოთხოვნა სტუმრის შიგნით; ჰოსტის მხარეს ტესტი სხვა წყაროს მისამართს იყენებს და მალავს პრობლემას.
- წაიკითხეთ თქვენ მიერ დამატებული NAT და ფილტრის წესების მრიცხველები. ნულოვანი დამთხვევის წესი მარშრუტიზაციის შეცდომაა და არა პოლიტიკის.
- დაადასტურეთ, რომ თქვენი SNAT წესის გვირაბის მისამართი ის არის, რაც კლიენტს ნამდვილად აქვს.
- დაწიეთ სტუმრის MTU ან დაჭიმეთ MSS, სანამ სერვერს გაჩერებაში დაადანაშაულებთ.
გაიარეთ სია და გაჩერდით პირველ მარცხზე. სწორედ ის ფენაა გამოსასწორებელი; ქვემოთ დამატებული ყველაფერი მხოლოდ ტეხად საზღვარს მალავს.