Docker კონტეინერების ტრაფიკი გვერდს უვლის ჰოსტის VPN-ს: როგორ შევამოწმოთ
ჰოსტის VPN მხოლოდ ჰოსტის ქსელურ სახელთა სივრცეს ფარავს. ამ შემოწმებებით გაარკვიეთ, ხვდება თუ არა ხიდზე მდგარი კონტეინერების ტრაფიკი გვირაბში, თუ პირდაპირ გადის გარეთ.
თქვენი ბრაუზერი ჰოსტზე აჩვენებს VPN-ის გამოსვლის მისამართს, კონტეინერი იტყობინება თქვენს სახლის კავშირზე, და ვიღაცამ ამას გაჟონვა უწოდა. არაფერი გაჟონილა. ჰოსტის გვირაბი არის ინტერფეისების, მარშრუტებისა და წესების ერთობლიობა, რომლებიც ჰოსტის ქსელურ სახელთა სივრცეს ეკუთვნის, ხოლო ხიდზე მდგარი კონტეინერი სხვა სახელთა სივრცეა საკუთარი ნაგულისხმევი მარშრუტით. მე მიმიღია Compose-ის სტეკი, სადაც VPN-ის კლიენტი კვირების განმავლობაში იყო დაკავშირებული და არც ერთი კონტეინერის პაკეტი არასდროს შესულა მასში.
შეამოწმეთ სახელთა სივრცე ტრეის ხატულამდე
სახელთა სივრცეები მთელი არგუმენტია. როცა Docker ხიდურ ქსელში კონტეინერს უშვებს, ის ქმნის ქსელურ სახელთა სივრცეს, ამაგრებს veth წყვილის ერთ ბოლოს მის შიგნით და აძლევს ამ სივრცეს მისამართსა და ნაგულისხმევ მარშრუტს. გვირაბის ინტერფეისი, როგორც არ უნდა ერქვას, ჰოსტის სახელთა სივრცეში ცხოვრობს. კონტეინერის შიგნით ტრაფიკი ვერ ხედავს მას, და ვერც ერთი კლიენტის პარამეტრი ვერ შეცვლის ამას გარედან.
ორი მხარდაჭერილი ნაბიჯი გამოგიყვანთ იზოლაციიდან. --network host კონტეინერს ჰოსტის სახელთა სივრცეში ათავსებს, ამიტომ ის ჰოსტის მარშრუტებს და, შესაბამისად, გვირაბს მემკვიდრეობით იღებს. --network container:vpn სხვა კონტეინერის სახელთა სივრცეს იზიარებს, და სწორედ ასე აგზავნის sidecar-ის უმეტესი კონსტრუქცია დატვირთვას VPN კონტეინერის გავლით. ორივე ცხადი არჩევანია; ჩვეულებრივ მხოლოდ ერთი მათგანია ის, რაც განმათავსებელს ჰქონდა მხედველობაში.
ჰოსტის რეჟიმი უფასო განახლება არ არის. კონტეინერი წყვეტს საკუთარი მისამართის ქონას, პორტების გამოქვეყნება წყვეტს მოქმედებას, და იქ NET_ADMIN-ის მქონე პროცესს შეუძლია ჰოსტის მარშრუტიზაცია გადაწეროს. ამის მიღება შეიძლება სწორი გადაწყვეტილება იყოს. ვივარაუდოთ, რომ ის უკვე ჩართულია — ეს არის გზა, რომლითაც ადამიანები საღამოს გვირაბის გამართვაში ატარებენ, რომელიც არასდროს ყოფილა გზაზე.
თვალყური ადევნეთ პაკეტს ხიდიდან გარეთ
ხიდზე მდგარი კონტეინერი პაკეტს თავის ნაგულისხმევ კარიბჭეს უგზავნის, რაც ჰოსტზე ხიდის ინტერფეისია. შემდეგ ჰოსტი პაკეტს გადაამისამართებს და ნაგულისხმევ კონფიგურაციაში წყაროს მისამართს საკუთარზე თარგმნის, სანამ პაკეტი გავა. სწორედ ეს თარგმნის საფეხური ამარცხებს პოლიტიკას ჩუმად.
როცა პაკეტი გვირაბის ბოლო წერტილს აღწევს, ის ჰოსტის მისამართს ატარებს. ნებისმიერი წესი, პოლიტიკის მარშრუტი ან გაყოფილი გვირაბის გადაწყვეტილება, რომელიც კონტეინერის თავდაპირველ მისამართს ეყრდნობა, წყვეტს დამთხვევას, და სხვადასხვა გასასვლელიდან გასვლისთვის განკუთვნილი ორი კონტეინერი მხოლოდ წყაროთი ვერ გაირჩევა. თუ კონტეინერ-კონტეინერ მარშრუტიზაცია გჭირდებათ, გადაწყვეტილება თარგმნამდე ან თქვენ მიერ კონტროლირებად სახელთა სივრცეში უნდა მიღდეს.
Docker თავად ინახავს ამ წესებს, იმ firewall backend-ის მეშვეობით, რომლითაც daemon კონფიგურირდა. გადამისამართებული ტრაფიკისთვის მხარდაჭერილი კაუჭის გარეთ ხელით დაწერილი წესები, DOCKER-USER, შეიძლება გადალაგდეს ან წაიშალოს, როცა daemon გადაიტვირთება ან ჰოსტი განახლდება. Docker-ის დოკუმენტაცია პაკეტების ფილტრაციაზე გვიჩვენებს ჯაჭვების რიგს; პრაქტიკული აზრი ისაა, რომ ყველა წესს სჭირდება მფლობელი და გადატვირთვის გზა, რომელიც ნამდვილად გამოგიცდიათ.
გამოსცადეთ ორი სისტემა ცალ-ცალკე
ერთი სახელთა სივრციდან მიღებული მტკიცებულება მეორეზე არაფერს ამბობს. VPN კონტეინერის შიგნით გაშვებული მოთხოვნა ამტკიცებს, რომ გვირაბი ამ კონტეინერისთვის მუშაობს. ეს ჰოსტის შესახებ მტკიცებულება არ არის, და იგივე ბრძანება --network host კონტეინერიდან გაშვებული ხიდის შესახებ მტკიცებულება არ არის.
# host namespace
ip route get 1.1.1.1
ip -brief addr show
# a fresh container on the same bridge network
docker run --rm --network appnet alpine sh -c 'ip route; cat /etc/resolv.conf; ip -6 route'
შემდეგ გაიმეორეთ ტესტი რეალური აპლიკაციის კონტეინერის შიგნით და არა მსგავს იმიჯში. აპლიკაციას შეიძლება ჰქონდეს დამატებითი მარშრუტი, პროქსის გარემოს ცვლადი ან სხვა გადამწყვეტი, და თითოეული მათგანი პასუხს ცვლის. ყოველ ჯერზე შეამოწმეთ სახელების გადაწყვეტა და მისამართების მეორე ოჯახი: მომხმარებლის მიერ განსაზღვრულ ხიდებზე მდგარი კონტეინერები Docker-ის ჩაშენებული გადამწყვეტით წყდება 127.0.0.11-ზე, და ხიდზე არსებულ IPv6 ნაგულისხმევ მარშრუტს შეუძლია ტრაფიკი გარეთ გაიტანოს, სანამ თქვენ მხოლოდ IPv4 გვირაბის ტესტირებით ხართ დაკავებული.
წაიკითხეთ ნიმუში, სანამ სერთიფიკატებს შეეხებით
როცა ჰოსტი მუშაობს და კონტეინერი არა, მიზეზი თითქმის ყოველთვის სტრუქტურულია, და გასაღებების ბრუნვა ან კლიენტის ხელახლა დაყენება ნახევარ დღეს კარგავს. ჯერ სიმპტომი დაალაგეთ და გასწორება ჩვეულებრივ თავად ირქმევს სახელს.
| What you see | Usual cause | First check |
|---|---|---|
| Host exits through the VPN, container shows the ISP address | The container never entered the tunnel | Default route inside the container |
| Container works until the daemon restarts, then stops | Rules outside the supported hook, or a changed backend | Daemon's reported firewall backend and DOCKER-USER |
| Container loses internal hosts after the tunnel comes up | VPN prefix overlaps the Docker bridge subnet | ip route on the host, then docker network inspect |
| Small replies arrive, large ones hang | Tunnel MTU or missing MSS clamping | Link MTU on both ends of the tunnel |
| Container falls back to the ISP address when the tunnel drops | Nothing enforces the kill switch for forwarded traffic | Stop the tunnel with a request in flight |
გადაფარვის სტრიქონი საკუთარ წინადადებას იმსახურებს. VPN პროფილი, რომელიც მარშრუტს უბიძგებს 172.17.0.0/16-ისთვის, ან 10.0.0.0/8-ისთვის, რომელიც შემთხვევით შეიცავს თქვენი ხიდის პრეფიქსს, გვირაბს აიძულებს კონტეინერების ქვექსელზე განაცხადოს პრეტენზია, და თარგმნილ მისამართებზე მიმავალი პასუხები არასწორი მიმართულებით წავა ან გაქრება. Docker ქსელის ხელახალი ნუმერაცია ჩვეულებრივ უფრო სუფთაა, ვიდრე გამონაკლისების დაგროვება.
გახადეთ დაფარვა ცხადი და შემდეგ განზრახ გატეხეთ იგი
ორი კონსტრუქცია უძლებს განხილვას. გააზიარეთ VPN კონტეინერის სახელთა სივრცე და ყოველი დაფარული დატვირთვა იყენებს ერთ გვირაბს ერთი გადატვირთვის საზღვრით, რაც მარტივია მანამ, სანამ ერთ სერვისს სხვა გასასვლელი ან საკუთარი DNS გზა დასჭირდება. ან გადაამისამართეთ კონტეინერების ქსელი ჰოსტზე არსებულ VPN კარიბჭეზე: გადამისამართება ჩართული, ხიდის პრეფიქსი გვირაბისკენაა მიმართული, დაბრუნების ტრაფიკი ცხადად დამუშავებული. ეს ყიდულობს კონტროლს თითო ქსელზე და გიჯდებათ წესების ნაკრები, რომელიც daemon-ისა და კლიენტის განახლებების განმავლობაში უნდა შეინარჩუნოთ.
როგორც არ უნდა იყოს, მთავარი ტესტი წარუმატებლობის შემთხვევაა. გააჩერეთ გვირაბი, როცა კონტეინერის მოთხოვნა გზაშია, და ნახეთ სად მიდის პაკეტი. თუ ის პირდაპირ გზას უბრუნდება, დაფარვა არასდროს ყოფილა აღსრულებული, რადგან კლიენტის kill switch მართავს კლიენტის საკუთარ ტრაფიკს და არა ხიდიდან გადამისამართებულ პაკეტებს. შემდეგ გადატვირთეთ daemon და ჰოსტი და მეორედ დაადასტურეთ შედეგი; სწორედ იმ მომენტში ქრება ხელით დაწერილი წესები.
სანამ დასრულებულს უწოდებთ, დაადასტურეთ ოთხი რამ: გვირაბის ინტერფეისი იმავე სახელთა სივრცეშია, რაც ტრაფიკი, კონტეინერის ტესტი რეალური კონტეინერის შიგნით ჩატარდა, სახელების გადაწყვეტა და მისამართების ორივე ოჯახი შემოწმდა, და გვირაბის მარცხის ტესტი კვლავ დახურულად მარცხდება. შემდეგ ჩაწერეთ firewall backend, კლიენტის ვერსია და თითოეული წესის მფლობელი. სწორედ ეს ჩანაწერი უშლის ხელს შემდეგ განახლებას ძველი ვარაუდების დაუდოკუმენტირებული ნაზავი დატოვოს.