Nginx di hadapan, backend peribadi di belakang VPN
Pelayar bercakap dengan Nginx, dan Nginx membuka sambungan kedua ke dalam terowong. Halakan kedua-duanya, sahkan kedua-dua sijil dan kekalkan lencongan awam sebagai awam.
Jejaki satu permintaan daripada telefon di dalam kereta api hingga ke backend yang hanya wujud di dalam rangkaian peribadi. Telefon menyelesaikan nama awam, bersambung ke alamat awam pada port 443 dan melengkapkan jabat tangan TLS biasa. Nginx menerima sambungan itu lalu membuka sambungan kedua yang berasingan sepenuhnya ke alamat yang hanya boleh dicapai melalui antara muka VPN. Tiada apa-apa berubah pada telefon, dan telefon tidak pernah tahu bahawa ada terowong yang terlibat.
Menjejaki satu permintaan melalui dua sambungan
Bahagian masuk ialah transaksi HTTP awam. Ia mempunyai nama awam, sijil awam dan laluan balasan yang mesti berfungsi untuk mana-mana alamat yang ada pada pelawat ketika itu. Apa-apa perubahan yang mengganggu laluan itu akan merosakkan semua pelawat sekaligus. Itulah sebabnya bahagian awam ialah bahagian yang paling kurang ingin anda uji kaji.
Bahagian keluar bersifat peribadi. Nginx menjadi klien, menyelesaikan nama dalaman atau menggunakan alamat tetap, dan mencapai perkhidmatan yang akan menolak sambungan daripada internet.
Public client -> frontend public address:443 (TLS, public name)
Frontend -> VPN private backend 10.40.0.20:8443 (TLS, private name)
Asingkan kedua-duanya dalam fikiran anda dan konfigurasi menjadi jelas. Gabungkan kedua-duanya, dan anda akhirnya akan cuba membenarkan klien awam menyertai terowong, dan dari situlah sebahagian besar kekeliruan dalam reka bentuk ini datang. Bahagian peribadi boleh rapuh. Bahagian awam tidak.
Sebelum membina apa-apa daripada ini, pastikan penerbitan aplikasi memang disengajakan. Proksi yang berfungsi juga merupakan pendedahan yang berfungsi, dan model pengesahan mesti menjadi keputusan, bukan kesan sampingan.
Nama gerbang mesti bertahan melalui terowong
Ada perangkap halus dalam penyelesaian nama. Jika DNS peribadi menimpa nama yang sama yang digunakan oleh profil VPN untuk gerbangnya sendiri, sambungan semula mungkin akhirnya cuba mencapai gerbang itu melalui terowong yang sepatutnya dibinanya sendiri. Klien memerlukan nama itu diselesaikan sebelum terowong wujud. Sebaik sahaja ia diselesaikan di dalam terowong, sambungan itu tidak akan pernah boleh dibina.
Gunakan nama yang diluluskan secara berasingan untuk gerbang dan backend, dan hadkan dasar penyelesai supaya setiap nama diselesaikan ke alamat yang diperlukan oleh penggunanya. Menukar pengepala Host aplikasi tidak membantu di sini, kerana klien VPN yang melakukan carian.
Kemudian fikirkan tentang skop. Frontend memerlukan laluan ke alamat backend, dan ia juga mesti terus menjawab klien awam. Terowong penuh pada hos itu boleh memenuhi syarat pertama dan merosakkan syarat kedua.
Di tempat dasar membenarkannya, terowong ke destinasi tertentu ialah reka bentuk yang lebih bersih. Di tempat dasar mewajibkan terowong penuh, frontend tergolong dalam konteks rangkaian berasingan dan bukannya ditampal dengan laluan sementara untuk pelawat yang dikenali.
Tulis blok pelayan supaya kedua-dua bahagian mengesahkan
Andaikan backend membentangkan sijil untuk nama ini: backend.corp.example dan menjawab pada alamat peribadi. Bersambung ke alamat sambil mengesahkan nama ialah tepat kegunaan pilihan SNI dan nama modul proksi.
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;
}
}
Setiap laluan dan alamat di atas ialah pemegang tempat. Bahagian penting ialah bahagian yang membiarkan pengesahan hidup: nama untuk diperiksa, berkas kepercayaan untuk memeriksanya, dan kedalaman yang sepadan dengan rantaian yang diluluskan.
proxy_set_header Host app.example.com memang disengajakan. Backend harus dikonfigurasikan untuk menerima asal awam itu, dan ia hanya patut mempercayai pengepala yang dimajukan daripada frontend ini sahaja. Menimpa X-Forwarded-For dengan alamat klien betul untuk satu proksi dan salah untuk rantaian, jadi penggunaan berbilang proksi memerlukan susunan alamat dipercayai tersendiri.
Menjadikan asal awam berkelakuan seperti asal awam
Aplikasi harus mengeluarkan URL mutlak, lencongan dan kuki untuk asal awam. Jika ia melencongkan pelawat ke backend.corp.example, pelawat itu dihantar ke nama yang tidak dapat diselesaikannya.
Betulkan itu melalui tetapan URL luaran yang disokong aplikasi dan dasar lencongan yang telah disemak, bukan dengan menulis semula nama hos dalam badan respons. Penggantian rentetan pada kandungan sewenang-wenangnya merosakkan muatan bertandatangan dan mencipta pepijat yang sukar dijejaki.
Kuki juga wajar mendapat penjagaan yang sama. Kuki yang terhad kepada nama peribadi tidak akan pernah dihantar oleh pelayar yang hanya mengenali nama awam.
Sahkan juga bahawa Nginx benar-benar boleh memiliki soket awam. Perkhidmatan web sedia ada pada 443 mesti berpindah atau frontend mesti berada di tempat lain, kerana dua proses tidak boleh mengikat alamat dan port yang sama.
Periksa setiap bahagian di tempat ia benar-benar berjalan
Sahkan konfigurasi dengan penghurai yang dipasang sebelum setiap muat semula, supaya salah taip tidak menjatuhkan pendengar yang sedang berkhidmat.
Kemudian uji bahagian backend daripada hos frontend itu sendiri, dalam konteks rangkaian yang sama dengan yang digunakan Nginx. Ping melalui terowong membuktikan kebolehcapaian dan tidak membuktikan apa-apa tentang TLS, kebenaran atau aplikasi.
Akhir sekali buat permintaan awam baharu ke nama hos sebenar dengan pengesahan sijil biasa, dan perhatikan laluan balasan bukan hanya badan respons.
Simpan kedua-dua pemerhatian bersama konfigurasi yang digunakan, dan catat apa yang berlaku apabila terowong tidak tersedia. Frontend harus memulangkan ralat upstream terkawal, bukan berundur secara senyap ke backend lain yang tiada siapa mendokumentasikannya. 502 dengan baris log yang jelas ialah hasil yang baik. Halaman yang dicache daripada asal yang tidak dimaksudkan bukan hasil yang baik.