თვალი ადევნე ერთ მოთხოვნას მატარებელში მყოფი ტელეფონიდან backend-ამდე, რომელიც მხოლოდ კერძო ქსელში არსებობს. ტელეფონი ამოხსნის საჯარო სახელს, უკავშირდება საჯარო მისამართს 443 პორტზე და ასრულებს ჩვეულებრივ TLS ხელის ჩამორთმევას. Nginx იღებს ამ კავშირს და შემდეგ ხსნის მეორეს, სრულიად ცალკე, მისამართამდე, რომელსაც მხოლოდ VPN ინტერფეისის გავლით აღწევს. ტელეფონზე არაფერი იცვლება და ტელეფონი არასდროს გაიგებს, რომ გვირაბი იყო ჩართული.

ერთი მოთხოვნის თვალყურის დევნა ორი კავშირის გავლით

შემომავალი ნაწილი საჯარო HTTP ტრანზაქციაა. მას აქვს საჯარო სახელი, საჯარო სერტიფიკატი და პასუხის გზა, რომელიც უნდა მუშაობდეს იმ მისამართისთვის, რომელიც ვიზიტორს იმ მომენტში აქვს. ნებისმიერი ცვლილება, რომელიც ამ გზას არღვევს, ერთდროულად ამტვრევს ყველა ვიზიტორს. ამიტომ საჯარო ნაწილი ის არის, რომელზეც ყველაზე ნაკლებად გინდა ექსპერიმენტი.

გამავალი ნაწილი კერძოა. Nginx ხდება კლიენტი, ამოხსნის შიდა სახელს ან იყენებს ფიქსირებულ მისამართს და აღწევს სერვისამდე, რომელიც უარს იტყოდა ინტერნეტიდან მომავალ კავშირზე.

Public client  -> frontend public address:443   (TLS, public name)
Frontend       -> VPN private backend 10.40.0.20:8443  (TLS, private name)

გონებაში ცალ-ცალკე შეინახე და კონფიგურაცია თვითგასაგები გახდება. აირიე ისინი და საბოლოოდ შეეცდები საჯარო კლიენტი გვირაბში ჩართო, და სწორედ აქედან მოდის ამ დიზაინის არეულობის უდიდესი ნაწილი. კერძო ნაწილი შეიძლება მყიფე იყოს. საჯარო არა.

სანამ რაიმეს ააშენებ, დაადასტურე, რომ აპლიკაციის გამოქვეყნება განზრახულია. მომუშავე პროქსი ასევე მომუშავე გამომჟღავნებაა, და ავთენტიფიკაციის მოდელი გადაწყვეტილება უნდა იყოს და არა გვერდითი ეფექტი.

შესასვლელის სახელი გვირაბს უნდა გადაურჩეს

სახელების ამოხსნაში დახვეწილი ხაფანგია. თუ კერძო DNS გადაფარავს იმავე სახელს, რომელსაც VPN პროფილი საკუთარი შესასვლელისთვის იყენებს, ხელახალი დაკავშირება შეიძლება საბოლოოდ ცდილობდეს ამ შესასვლელამდე მისვლას იმ გვირაბის გავლით, რომელიც თავად უნდა შექმნას. კლიენტს ეს სახელი ამოხსნილი სჭირდება მანამ, სანამ გვირაბი არსებობს. როგორც კი ის გვირაბის შიგნით ამოიხსნება, კავშირი ვეღარასდროს აშენდება.

გამოიყენე განსხვავებული დამტკიცებული სახელები შესასვლელისა და backend-ისთვის, და შეზღუდე ამომხსნელის პოლიტიკა ისე, რომ თითოეული სახელი ამოიხსნას იმ მისამართზე, რომელიც მის მომხმარებელს სჭირდება. აპლიკაციის Host სათაურის შეცვლა აქ არ გვეხმარება, რადგან ძიებას VPN კლიენტი ასრულებს.

შემდეგ იფიქრე მოცულობაზე. Frontend-ს სჭირდება მარშრუტი backend-ის მისამართამდე და ასევე უნდა გააგრძელოს პასუხი საჯარო კლიენტებზე. სრული გვირაბი ამ ჰოსტზე შეიძლება პირველი მოთხოვნა დააკმაყოფილოს და მეორე გატეხოს.

სადაც პოლიტიკა იძლევა, კონკრეტულ დანიშნულებამდე გვირაბი უფრო სუფთა დიზაინია. სადაც პოლიტიკა სრულ გვირაბს მოითხოვს, frontend ცალკე ქსელურ კონტექსტს ეკუთვნის, ნაცვლად იმისა, რომ ცნობილი ვიზიტორებისთვის ad hoc მარშრუტებით შეიკეროს.

დაწერე სერვერის ბლოკი ისე, რომ ორივე ნაწილმა გადაამოწმოს

დავუშვათ, backend წარადგენს სერტიფიკატს სახელისთვის backend.corp.example და პასუხობს კერძო მისამართზე. მისამართზე დაკავშირება სახელის გადამოწმებისას ზუსტად ის არის, რისთვისაც პროქსი მოდულის SNI და სახელის ოფციებია განკუთვნილი.

upstream private_app {
    server 10.40.0.20:8443;
}

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate     /etc/nginx/tls/app-fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/app-key.pem;

    location / {
        proxy_pass https://private_app;
        proxy_ssl_server_name on;
        proxy_ssl_name backend.corp.example;
        proxy_ssl_trusted_certificate /etc/nginx/trust/backend-ca.pem;
        proxy_ssl_verify on;
        proxy_ssl_verify_depth 3;

        proxy_set_header Host app.example.com;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

ზემოთ მოცემული ყოველი გზა და მისამართი ჩანაცვლებითია. მთავარია ის ნაწილები, რომლებიც გადამოწმებას ჩართულს ტოვებს: სახელი შესამოწმებლად, ნდობის პაკეტი მის შესამოწმებლად და სიღრმე, რომელიც დამტკიცებულ ჯაჭვს შეესაბამება.

proxy_set_header Host app.example.com განზრახ არის. backend უნდა იყოს კონფიგურირებული, რომ მიიღოს ეს საჯარო წარმომავლობა, და მან მხოლოდ ამ frontend-იდან უნდა ენდოს გადაცემულ სათაურებს. გადაწერა X-Forwarded-For კლიენტის მისამართით სწორია ერთი პროქსისთვის და არასწორია ჯაჭვისთვის, ამიტომ მრავალპროქსიან განთავსებას საკუთარი სანდო მისამართების მოწყობა სჭირდება.

აიძულე საჯარო წარმომავლობა ისე მოიქცეს, როგორც საჯარო წარმომავლობა

აპლიკაციამ უნდა გამოსცეს აბსოლუტური URL-ები, გადამისამართებები და cookie-ები საჯარო წარმომავლობისთვის. თუ ის ვიზიტორს გადაამისამართებს backend.corp.example-ზე, ეს ვიზიტორი იგზავნება სახელზე, რომელსაც ვერ ამოხსნის.

გამოასწორე ეს აპლიკაციის მხარდაჭერილი გარე URL-ის პარამეტრით და გადამოწმებული გადამისამართების პოლიტიკით, და არა ჰოსტის სახელების გადაწერით პასუხის სხეულებში. სტრიქონების ჩანაცვლება თვითნებურ შიგთავსში ამტვრევს ხელმოწერილ დატვირთვებს და ქმნის ძნელად მისადევნებელ შეცდომებს.

Cookie-ები იგივე ყურადღებას იმსახურებენ. კერძო სახელზე შეზღუდული cookie არასდროს გაიგზავნება ბრაუზერის მიერ, რომელმაც მხოლოდ საჯარო იცის.

ასევე დაადასტურე, რომ Nginx ნამდვილად შეიძლება ფლობდეს საჯარო სოკეტს. არსებული ვებ სერვისი 443-ზე უნდა გადავიდეს, ან frontend სხვაგან უნდა ცხოვრობდეს, რადგან ორი პროცესი ერთსა და იმავე მისამართსა და პორტს ვერ დააბამს.

შეამოწმე თითოეული ნაწილი იქ, სადაც ის რეალურად მუშაობს

დაავალიდირე კონფიგურაცია დაყენებული პარსერით ყოველ გადატვირთვამდე, რომ ბეჭდვითი შეცდომა არ ჩამოაგდოს ის მსმენელი, რომელიც ახლა ემსახურება.

შემდეგ გამოსცადე backend-ის ნაწილი თავად frontend ჰოსტიდან, იმავე ქსელურ კონტექსტში, რომელსაც Nginx იყენებს. Ping გვირაბის გავლით ამტკიცებს ხელმისაწვდომობას და არაფერს TLS-ზე, ავტორიზაციაზე ან აპლიკაციაზე.

ბოლოს გააკეთე ახალი საჯარო მოთხოვნა ნამდვილი ჰოსტის სახელზე ჩვეულებრივი სერტიფიკატის გადამოწმებით და დააკვირდი პასუხის გზას და არა მხოლოდ სხეულს.

შეინახე ორივე დაკვირვება განთავსებულ კონფიგურაციასთან ერთად და ჩაწერე, რა ხდება, როცა გვირაბი მიუწვდომელია. frontend-მა უნდა დააბრუნოს კონტროლირებადი upstream შეცდომა და არა ჩუმად გადაერთოს სხვა backend-ზე, რომელიც არავის დაუდოკუმენტირებია. 502 მკაფიო ლოგის სტრიქონით კარგი შედეგია. დაქეშირებული გვერდი არასასურველი წარმომავლობიდან არ არის.

დაკავშირებული სტატიები