Հետևիր մեկ հարցումով գնացքի մեջ գտնվող հեռախոսից մինչև 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-ը հստակ լոգի տողով լավ արդյունք է։ Քեշավորված էջ անցանկալի ծագումից այդպիսին չէ։

Առնչվող հոդվածներ