OpenVPN Docker-ში: მუშაობის ხელშეკრულება, რომელიც მინიმალურ კლიენტს სჭირდება
შემოწმებული იმიჯი, ერთი დაცული პროფილი, TUN-წვდომა და NET_ADMIN. რას იღებს კონტეინერი, რა არ უნდა მიიღოს და როგორ დავამტკიცოთ, რომ გვირაბი მუშაობს.
ჩადეთ VPN კლიენტი კონტეინერში და მიიღებთ ერთ სახელთა სივრცეს შიგნით გვირაბით, და არა ჰოსტს, რომელიც მთელ თავის ტრაფიკს VPN-ით მარშრუტირებს. ეს ცალკეული პროექტებია, და მათი ერთად მოქცევა სწორედ ის გზაა, რომლითაც კონტეინერები შემთხვევით ხდება პრივილეგირებული. ეს სახელმძღვანელო მხოლოდ პირველს მოიცავს: ერთი შემოწმებული იმიჯი, ერთი დაცული პროფილი და კლიენტის პროცესი, რომელიც შემოიფარგლება საკუთარი ქსელის სახელთა სივრცით.
დაიწყეთ არტეფაქტით, რომელიც უკვე დამტკიცებულია ვიღაცის მიერ. ქვემოთ მოცემული ნაბიჯები ვარაუდობს იმიჯს, რომელიც შეიცავს openvpn შესრულებად ფაილს და სრულ კლიენტურ პროფილს, რომლის სერტიფიკატი და გასაღების მასალა უკვე ამოხსნილია მთაუნტირების შიგნით. ისინი არ ქმნიან პროგრამულ უზრუნველყოფას, არ გასცემენ რწმუნებებს და არ მოიცავს root-ის გარეშე გაშვების გარემოებსა და Windows-ის მშობლიურ კონტეინერებს, რომელთაგან თითოეული ცვლის პასუხს.
რას იღებს კონტეინერი და რას არა
შეინახეთ პროფილი და ყველაფერი, რაზეც ის მიუთითებს, შეზღუდული უფლებების ლოკალურ დირექტორიაში, მთაუნტირებული მხოლოდ-წაკითხვის რეჟიმში. პროფილის შიგნით არსებული ფარდობითი ბილიკები ამ მთაუნტის შიგნით უნდა გადაწყდეს; ეს ის დეტალია, რომელსაც ადამიანები გამოტოვებენ, როცა სერტიფიკატი პროფილიდან ერთი დირექტორიით ზემოთაა და კონტეინერი მას ვერასოდეს ხედავს.
services:
vpn:
image: ${VPN_CLIENT_IMAGE:?set the reviewed image reference}
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./vpn:/vpn:ro
working_dir: /vpn
entrypoint: ["openvpn"]
command: ["--config", "/vpn/client.ovpn"]
ეს ფაილი სამ რამეს იძლევა და სხვას არაფერს ითხოვს. იმიჯის მითითება უნდა დამაგრდეს digest-ით, თუ თქვენი განლაგების პოლიტიკა ასე ამბობს, და პროფილი შემოწმებული უნდა იყოს, და არა ასლი, რომელიც ვიღაცამ ჰოსტზე ადგილზე შეასწორა.
მხოლოდ-წაკითხვა აქ ნიშნავს, რომ კონტეინერი ვერ შეცვლის თქვენს ფაილებს ჰოსტზე; ეს არ ნიშნავს, რომ კონტეინერი ვერ წაიკითხავს თქვენ მიერ მთაუნტირებულ კერძო გასაღებს. სწორედ ესაა მთაუნტის მთელი აზრი, და ამიტომ ფაილის უფლებები, მფლობელობა და ის, როგორ მოხვდა საიდუმლო ჰოსტზე, კვლავ მნიშვნელოვანია. არასოდეს გადასცეთ პაროლი ბრძანების სტრიქონში და არ გამოაცხვოთ იგი იმიჯის შრეში, რადგან ორივე რჩება ისეთ ადგილებში, რომლებსაც მოგვიანებით ვერ გაასუფთავებთ.
შესაძლებლობები, entrypoint და მათ შორის არსებული ხაფანგი
საჭიროა ორი პარამეტრი და ისინი დაუკავშირებელ სამუშაოს ასრულებენ. NET_ADMIN კონტეინერის სახელთა სივრცეში ინტერფეისებისა და მარშრუტების მანიპულირების საშუალებას იძლევა. /dev/net/tun მოწყობილობის ჩანაწერი პროცესს გვირაბის მოწყობილობის გახსნის საშუალებას აძლევს. ერთის მეორის გარეშე მიცემა მკაფიო მარცხს იწვევს, რაც მოსახერხებელია: დაკარგული TUN მოწყობილობა გახსნისას ვარდება, ხოლო დაკარგული შესაძლებლობა მაშინ ვარდება, როცა კლიენტი ინტერფეისის კონფიგურირებას ცდილობს.
არც ერთი მათგანი არ ამართლებს privileged: true-ს. პრივილეგირებული კონტეინერი ყველა შესაძლებლობას პლუს მოწყობილობებზე წვდომას იღებს, და ეს გაცილებით დიდი მინიჭებაა, ვიდრე ამ დატვირთვას სჭირდება. გაშვების გარემოს პრივილეგიებისა და შესაძლებლობების ცნობარი განმარტავს, რამდენად შორს აღწევს ეს მინიჭება. თუ chart ან ბლოგის პოსტი ამას ითხოვს, ჯერ შეამოწმეთ, ხომ არ იყო მოთხოვნა სინამდვილეში დაკარგული მოწყობილობის ბილიკი ან დაკარგული შესაძლებლობა.
entrypoint-ის გადაფარვა ის ნაწილია, რომელიც კბენს. openvpn-ის პირდაპირ გამოძახება სწორია ჩვეულებრივი იმიჯისთვის და არასწორია ნებისმიერი პროდუქტული იმიჯისთვის, რომლის გაშვების სკრიპტი დემონის გაშვებამდე ამზადებს firewall-ის წესებს, ამომხსნელის ფაილებს ან რწმუნებებს. ამ სკრიპტის გადაფარვა გამოტოვებს მომზადებას და გტოვებთ ნახევრად კონფიგურირებულ სახელთა სივრცეში აწეული გვირაბის გამართვაზე.
გაუშვით კლიენტი წინა პლანზე და დაუშვით, რომ ის კონტეინერის მთავარი პროცესი იყოს. დემონიზებული პროცესი მაშინვე გადის, კონტეინერი ციკლურად გადაიტვირთება, და საჭირო ლოგები გაფანტულია გადატვირთვებს შორის, რომლებიც ყველა ერთნაირად გამოიყურება.
შეამოწმეთ სახელთა სივრცის შიგნიდან და არა ჰოსტიდან
ასწიეთ სტეკი, ლოგში დაადასტურეთ, რომ კლიენტმა დაკავშირებულ მდგომარეობას მიაღწია, და შემდეგ შეამოწმეთ ის სახელთა სივრცე, რომელიც რეალურად ინახავს გვირაბს.
docker compose up -d
docker compose logs --tail 40 vpn
docker compose exec vpn ip -br addr
docker compose exec vpn ip route
მისამართების სიაში უნდა ჩანდეს გვირაბის ინტერფეისი პულიდან მიღებული მისამართით, ხოლო მარშრუტების ცხრილში უნდა ჩანდეს მისკენ მიმართული გაგზავნილი დანიშნულებები. თუ ინტერფეისი არ არის, კლიენტი მის შექმნის წერტილს ვერასოდეს მიაღწია. თუ ის არსებობს, მაგრამ არც ერთი მარშრუტი არ იყენებს მას, საძიებო ადგილი push ან მარშრუტის პარამეტრებია.
შემდეგ შეამოწმეთ თქვენთვის მნიშვნელოვანი დანიშნულება იმ სახელთა სივრციდან, რომელმაც უნდა გამოიყენოს გვირაბი, აპლიკაციით და არა მხოლოდ ping-ით. სახელების ამოხსნა ცალკე მარცხია, რომლის ცალსახად შემოწმებაც ღირს: გაგზავნილი DNS მისამართი თავისთავად არ აახლებს კონტეინერის ამომხსნელს, და push-ის უგულებელყოფილი დამოუკიდებელი კლიენტი სახელებს სწორედ იმით ამოხსნის, რაც იმიჯმა დააყენა.
გათიშული მდგომარეობა ხელშეკრულების ნაწილია
ხელახალი დაკავშირების ქცევას საკუთარი შემოწმება სჭირდება, რადგან გადატვირთვის პოლიტიკა ამტკიცებს, რომ პროცესი გადაიტვირთება, და არაფერს ამბობს იმაზე, რას აკეთებს ტრაფიკი ამასობაში. შეაჩერეთ გვირაბი, სცადეთ განზრახული დანიშნულება და დააკვირდით, უარყოფილია კავშირი, ჩერდება თუ ჩუმად წარმატდება კონტეინერის ჩვეულებრივი მარშრუტით. ბოლო შედეგია მნიშვნელოვანი, და ის ჯანსაღ ლოგში უხილავია.
თუ დატვირთვამ არასოდეს არ უნდა გამოიყენოს ჩვეულებრივი გზა, დააპროექტეთ ეს ცალსახად firewall-ის წესებით და სახელთა სივრცის შიგნით მზადყოფნის სიგნალით. Kill switch არის პოლიტიკა, რომელსაც წერთ და ამოწმებთ, და არა თვისება, რომელსაც კონტეინერში კლიენტის გაშვებით მემკვიდრეობით იღებთ.
და ბოლოს, გახსოვდეთ საზღვარი: ეს მარშრუტები კონტეინერის სახელთა სივრცეში ცხოვრობს და არ ცვლის ჰოსტის მარშრუტიზაციის ცხრილს. Docker-ის გადაგზავნის, თარგმნის და firewall-ის წესები რჩება გარე გზის ნაწილად, და გამოქვეყნებული პორტი VPN ინტერფეისი არ არის.