Ikuti satu permintaan dari ponsel di kereta sampai ke backend yang hanya ada di dalam jaringan privat. Ponsel menyelesaikan nama publik, terhubung ke alamat publik pada port 443, dan menyelesaikan jabat tangan TLS biasa. Nginx menerima koneksi itu lalu membuka koneksi kedua yang sepenuhnya terpisah ke alamat yang hanya dapat dijangkau melalui antarmuka VPN. Tidak ada yang berubah di ponsel, dan ponsel tidak pernah tahu bahwa ada terowongan yang terlibat.

Mengikuti satu permintaan melalui dua koneksi

Bagian masuk adalah transaksi HTTP publik. Ia punya nama publik, sertifikat publik, dan jalur balasan yang harus berfungsi untuk alamat mana pun yang dimiliki pengunjung saat itu. Perubahan apa pun yang mengganggu jalur itu akan merusak semua pengunjung sekaligus. Karena itulah bagian publik adalah bagian yang paling tidak ingin Anda eksperimenkan.

Bagian keluar bersifat privat. Nginx menjadi klien, menyelesaikan nama internal atau memakai alamat tetap, dan menjangkau layanan yang akan menolak koneksi dari internet.

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

Pisahkan keduanya di kepala Anda dan konfigurasinya menjadi jelas. Gabungkan keduanya, dan Anda akhirnya akan mencoba membuat klien publik ikut serta dalam terowongan, dan dari situlah sebagian besar kebingungan dalam desain ini berasal. Bagian privat boleh rapuh. Bagian publik tidak.

Sebelum membangun apa pun dari ini, pastikan bahwa penerbitan aplikasi memang disengaja. Proksi yang berfungsi juga merupakan pemaparan yang berfungsi, dan model autentikasi harus menjadi keputusan, bukan efek samping.

Nama gateway harus bertahan melewati terowongan

Ada jebakan halus dalam penyelesaian nama. Jika DNS privat menimpa nama yang sama yang dipakai profil VPN untuk gateway-nya sendiri, koneksi ulang bisa berakhir mencoba menjangkau gateway itu melalui terowongan yang justru harus dibangunnya sendiri. Klien membutuhkan nama itu terselesaikan sebelum terowongan ada. Begitu nama itu diselesaikan di dalam terowongan, koneksinya tidak akan pernah bisa dibangun.

Gunakan nama yang disetujui secara terpisah untuk gateway dan backend, dan batasi kebijakan resolver sehingga setiap nama terselesaikan ke alamat yang dibutuhkan konsumennya. Mengubah header Host aplikasi tidak membantu di sini, karena klien VPN-lah yang melakukan pencarian.

Lalu pikirkan cakupannya. Frontend membutuhkan rute ke alamat backend, dan juga harus terus menjawab klien publik. Terowongan penuh pada host itu bisa memenuhi syarat pertama dan merusak syarat kedua.

Di tempat kebijakan mengizinkannya, terowongan ke tujuan tertentu adalah desain yang lebih bersih. Di tempat kebijakan mewajibkan terowongan penuh, frontend berada di konteks jaringan terpisah alih-alih ditambal dengan rute sementara untuk pengunjung yang dikenal.

Tulis blok server sehingga kedua bagian memverifikasi

Anggap backend menyajikan sertifikat untuk nama ini: backend.corp.example dan menjawab pada alamat privat. Menghubungkan ke alamat sambil memverifikasi nama adalah tepat kegunaan opsi SNI dan nama pada 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 jalur dan alamat di atas adalah placeholder. Bagian yang penting adalah bagian yang menjaga verifikasi tetap aktif: nama untuk diperiksa, bundel kepercayaan untuk memeriksanya, dan kedalaman yang sesuai dengan rantai yang disetujui.

proxy_set_header Host app.example.com memang disengaja. Backend harus dikonfigurasi untuk menerima asal publik itu, dan hanya boleh memercayai header yang diteruskan dari frontend ini saja. Menimpa X-Forwarded-For dengan alamat klien benar untuk satu proksi dan salah untuk rantai, jadi penerapan banyak proksi membutuhkan pengaturan alamat tepercaya sendiri.

Membuat asal publik berperilaku seperti asal publik

Aplikasi harus mengeluarkan URL absolut, pengalihan, dan cookie untuk asal publik. Jika ia mengalihkan pengunjung ke backend.corp.example, pengunjung itu dikirim ke nama yang tidak dapat diselesaikannya.

Perbaiki itu melalui pengaturan URL eksternal yang didukung aplikasi dan kebijakan pengalihan yang ditinjau, bukan dengan menulis ulang nama host di badan respons. Penggantian string pada konten sembarang merusak muatan bertanda tangan dan menciptakan bug yang sulit dilacak.

Cookie juga pantas mendapat perhatian yang sama. Cookie yang dibatasi pada nama privat tidak akan pernah dikirim oleh browser yang hanya mengenal nama publik.

Pastikan juga bahwa Nginx benar-benar dapat memiliki soket publik. Layanan web yang sudah ada pada 443 harus pindah atau frontend harus berada di tempat lain, karena dua proses tidak dapat mengikat alamat dan port yang sama.

Periksa setiap bagian di tempat ia benar-benar berjalan

Validasi konfigurasi dengan parser yang terpasang sebelum setiap pemuatan ulang, agar salah ketik tidak menjatuhkan listener yang sedang melayani.

Lalu uji bagian backend dari host frontend itu sendiri, dalam konteks jaringan yang sama dengan yang dipakai Nginx. Ping melalui terowongan membuktikan keterjangkauan dan tidak membuktikan apa pun tentang TLS, otorisasi, atau aplikasi.

Terakhir, kirim permintaan publik baru ke nama host sebenarnya dengan verifikasi sertifikat normal, dan amati jalur balasan, bukan hanya badan respons.

Simpan kedua pengamatan bersama konfigurasi yang diterapkan, dan catat apa yang terjadi ketika terowongan tidak tersedia. Frontend harus mengembalikan galat upstream yang terkendali, bukan diam-diam beralih ke backend lain yang tidak didokumentasikan siapa pun. 502 dengan baris log yang jelas adalah hasil yang baik. Halaman yang di-cache dari asal yang tidak dimaksudkan bukan hasil yang baik.

Artikel terkait