mTLS nginx: Sertifikat Klien dengan openssl
Lindungi panel admin dengan mTLS: buat CA privat memakai openssl, terbitkan sertifikat klien, lalu konfigurasi nginx agar menolak klien tanpa sertifikat valid.
Apa yang dilakukan mTLS
Mutual TLS, yang biasanya ditulis sebagai mTLS, membuat nginx meminta sertifikat dari setiap klien dan menolak permintaan jika sertifikat tersebut tidak ada atau tidak diterbitkan oleh certificate authority (CA) yang Anda kendalikan. Pemeriksaan ini berlangsung di dalam handshake TLS (transport layer security), sehingga pemanggil tanpa sertifikat klien yang valid sama sekali tidak pernah mencapai aplikasi Anda. Inilah manfaatnya: panel admin atau endpoint metrik dapat berada di Internet publik tanpa halaman login dan tanpa kredensial yang dapat ditebak bot.
Konfigurasinya sederhana. Satu CA privat yang dibuat dengan openssl, satu sertifikat untuk setiap orang, dan tiga directive di server block nginx. Keberlangsungan konfigurasi ini selama satu tahun ditentukan oleh pengelolaan operasional. Karena itu, sebagian besar panduan ini membahas masa berlaku, pencabutan, sertifikat per orang, serta tindakan yang harus dilakukan ketika klien ditolak dan tidak ada yang dapat mengetahui penyebabnya.
Dua rantai, bukan satu
Ada dua rantai sertifikat dalam penyiapan mTLS, dan keduanya tidak saling berkaitan. Menggabungkan keduanya adalah kesalahan pertama yang hampir selalu terjadi.
Rantai pertama adalah milik server. VPS Anda menyajikan sertifikat untuk admin.example.com yang diterbitkan oleh CA publik seperti Let's Encrypt, lalu browser memeriksanya terhadap penyimpanan root yang disertakan dalam sistem operasi. mTLS tidak mengubah bagian ini. Jika certbot menerbitkan sertifikat tersebut untuk Anda hari ini, pertahankan apa adanya: lihat menerbitkan sertifikat Let's Encrypt untuk nginx dengan certbot.
Rantai kedua adalah milik klien. Anda membuat CA kecil sendiri, menandatangani satu sertifikat untuk setiap orang yang memerlukan akses, lalu memberi tahu nginx agar mempercayai CA tersebut dan hanya CA tersebut saat memeriksa klien. Tidak ada penyimpanan root publik yang mengenal CA Anda, dan memang tidak perlu. Satu-satunya pihak yang harus mempercayainya adalah nginx, melalui file ssl_client_certificate.
Jadi, ssl_client_certificate tidak pernah memengaruhi sertifikat yang disajikan nginx, dan rantai Let's Encrypt tidak pernah memengaruhi klien mana yang diizinkan masuk. Mengarahkan ssl_client_certificate ke fullchain.pem tidak menghasilkan fungsi seperti yang terlihat: direktif tersebut menentukan penerbit yang dapat menjadi asal sertifikat klien, yaitu sisi lain dari koneksi. Membuat server itu sendiri mempercayai CA Anda untuk pekerjaan keluar merupakan tugas terpisah, yang dibahas dalam menambahkan CA sendiri ke penyimpanan trust Ubuntu, dan penyimpanan trust sistem bukan penyimpanan yang dibaca nginx saat memverifikasi klien.
Buat CA klien sendiri dengan openssl
Buat CA di lokasi selain server web. nginx hanya memerlukan sertifikat publik CA. Kunci privat CA digunakan untuk menandatangani sertifikat klien baru. Jika kunci tersebut disimpan pada server yang menghadap Internet, satu pembobolan saja memungkinkan penyerang membuat klien valid untuk dirinya sendiri kapan saja.
mkdir -p ~/client-ca/certs ~/client-ca/newcerts ~/client-ca/private ~/client-ca/csr
cd ~/client-ca
chmod 700 private
touch index.txt
echo 1000 > serial
echo 1000 > crlnumberindex.txt, serial, dan crlnumber adalah basis data CA. openssl ca tidak dapat berjalan tanpa ketiganya. Berkas-berkas tersebut juga memungkinkan pencabutan sertifikat di kemudian hari, karena daftar pencabutan berisi nomor seri. Oleh karena itu, CA harus mengingat nomor seri yang diberikan kepada setiap pihak.
Buat ~/client-ca/openssl.cnf. Atur dir ke path direktori tersebut yang sebenarnya, karena openssl ca tidak memperluas ~.
[ ca ]
default_ca = client_ca
[ client_ca ]
dir = /home/you/client-ca
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/ca.crt
private_key = $dir/private/ca.key
serial = $dir/serial
crlnumber = $dir/crlnumber
default_md = sha256
default_days = 365
default_crl_days = 30
policy = policy_loose
rand_serial = no
unique_subject = no
email_in_dn = no
[ policy_loose ]
commonName = supplied
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
emailAddress = optional
[ client_ext ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuerSelanjutnya, buat kunci CA dan sertifikat yang ditandatangani sendiri:
openssl genrsa -aes256 -out private/ca.key 4096
chmod 600 private/ca.key
openssl req -x509 -new -key private/ca.key -sha256 -days 3650 \
-subj "/O=Example Ops/CN=Example Ops Client CA" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-out ca.crt-aes256 menambahkan passphrase pada kunci CA, sehingga setiap proses penandatanganan akan memintanya. Itulah tujuan opsi tersebut. Periksa hasilnya:
openssl x509 -in ca.crt -noout -subject -dates -ext basicConstraintsSubject harus menunjukkan CA Anda, dan masa berlaku harus sepuluh tahun. Baris ekstensi harus berisi CA:TRUE, pathlen:0. pathlen:0 berarti CA ini dapat menandatangani sertifikat akhir, tetapi tidak dapat menandatangani CA lain. Dengan demikian, rantai sertifikat tetap memiliki kedalaman tepat satu tingkat dan Anda dapat membiarkan ssl_verify_depth apa adanya.
Terbitkan sertifikat klien untuk setiap orang
Satu sertifikat untuk setiap orang. Jangan pernah menggunakan satu sertifikat bersama untuk satu tim, karena sertifikat bersama tidak dapat dicabut tanpa mengunci akses semua orang, dan sertifikat tersebut tidak memberi informasi tentang siapa yang melakukan permintaan.
openssl genrsa -out private/alice.key 2048
openssl req -new -key private/alice.key -out csr/alice.csr \
-subj "/O=Example Ops/CN=alice"
openssl ca -config openssl.cnf -extensions client_ext \
-days 365 -notext -in csr/alice.csr -out certs/alice.crtopenssl ca mencetak sertifikat yang akan ditandatangani, meminta kata sandi CA, meminta konfirmasi dua kali, lalu menambahkan satu baris ke index.txt. Tambahkan -batch saat Anda membuat skrip untuk proses ini. Bagian client_ext penting karena memuat satu baris: extendedKeyUsage = clientAuth. Sertifikat yang hanya mencantumkan serverAuth sebagai penggunaan kunci tambahan akan ditolak karena dianggap tidak sesuai untuk autentikasi klien. Nyatakan tujuannya secara eksplisit.
Verifikasi pasangan tersebut terhadap CA sebelum menyerahkannya:
openssl verify -CAfile ca.crt certs/alice.crtPerintah tersebut mencetak certs/alice.crt: OK. Output lain apa pun berarti sertifikat dan CA tidak cocok, dan tidak ada konfigurasi nginx yang dapat memperbaikinya.
Gabungkan kunci dan sertifikat ke dalam satu file yang dapat diimpor oleh browser:
openssl pkcs12 -export -inkey private/alice.key -in certs/alice.crt \
-name "alice at example ops" -out alice.p12Proses ekspor meminta kata sandi untuk melindungi file selama pengiriman. Kirim file dan kata sandinya melalui saluran yang berbeda, dan berikan kepada pengguna .p12, bukan .key biasa. Anda dapat menambahkan -certfile ca.crt untuk menyertakan CA dalam paket, tetapi nginx tidak memerlukannya: nginx sudah menyimpan ca.crt, sehingga sertifikat yang ditandatangani langsung oleh CA tersebut dapat diverifikasi tanpa file tambahan.
OpenSSL 3, yang disertakan dalam Ubuntu 24.04, menulis file PKCS#12 dengan enkripsi saat ini, dan browser serta sistem operasi yang digunakan hingga Agustus 2026 dapat membacanya. Jika importer lama menolak file tersebut, ekspor ulang dengan menambahkan -legacy. Opsi ini menggunakan algoritma lama yang diharapkan oleh importer tersebut. Baca pesan yang diberikan importer sebelum menggunakan opsi itu.
Konfigurasikan nginx dengan ssl_client_certificate dan ssl_verify_client
Salin sertifikat CA, dan hanya sertifikat CA, ke server.
scp ca.crt user@admin.example.com:/tmp/client-ca.crt
ssh user@admin.example.com \
'sudo install -o root -g root -m 644 /tmp/client-ca.crt /etc/nginx/client-ca.crt'Mode 644 benar untuk kasus ini. Sertifikat CA merupakan informasi publik. Kunci CA tetap berada di workstation Anda.
Kemudian tambahkan tiga direktif ke blok server yang sudah menangani terminasi TLS:
server {
listen 443 ssl;
server_name admin.example.com;
ssl_certificate /etc/letsencrypt/live/admin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;
ssl_client_certificate /etc/nginx/client-ca.crt;
ssl_verify_client on;
ssl_verify_depth 1;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}ssl_verify_depth 1 adalah nilai default nginx. Direktif ini menyatakan bahwa sertifikat klien harus ditandatangani langsung oleh CA dalam file tersebut. Naikkan nilainya hanya jika Anda menambahkan sertifikat perantara. nginx juga mengirimkan nama subjek dari ssl_client_certificate kepada klien selama handshake. Dengan demikian, browser mengetahui sertifikat mana yang dapat ditawarkan. Perilaku ini menjadi alasan untuk menggunakan ssl_client_certificate, bukan ssl_trusted_certificate. Keduanya melakukan verifikasi dengan cara yang sama, tetapi ssl_trusted_certificate tidak mengirimkan daftar sertifikat.
Ubuntu 24.04 menyediakan nginx 1.24. Pada versi ini, HTTP/2 ditambahkan ke baris listen sebagai listen 443 ssl http2;. Pada nginx 1.25.1 dan versi lebih baru, format tersebut tidak digunakan lagi. HTTP/2 ditetapkan melalui direktif tersendiri, yaitu http2 on;. Kedua pilihan tersebut tidak mengubah pemeriksaan sertifikat.
Muat ulang konfigurasi dan baca hasilnya:
sudo nginx -t && sudo systemctl reload nginx
curl -i https://admin.example.com/nginx -t menampilkan syntax is ok dan test is successful. Pemanggilan curl tidak membawa sertifikat, sehingga hasilnya seharusnya 400 Bad Request dengan isi No required SSL certificate was sent. Artinya, nginx menolak permintaan pada gerbangnya sendiri. Ini menunjukkan bahwa konfigurasi sudah aktif dan aplikasi belum menerima permintaan. Sekarang lakukan pengujian dengan benar:
curl --cert certs/alice.crt --key private/alice.key https://admin.example.com/Perintah tersebut seharusnya mengembalikan apa pun yang disediakan oleh aplikasi Anda.
Mengapa gerbang harus berada di blok server
Sertifikat dipertukarkan selama TLS handshake, sebelum nginx membaca baris permintaan. Karena itu, pada saat tersebut nginx belum mengetahui location tempat permintaan akan diarahkan. Menempatkan ssl_verify_client on; di dalam location meminta client melakukan renegosiasi di tengah koneksi. TLS 1.3 menghapus renegosiasi, dan HTTP/2 melarangnya. Pada stack saat ini, pola tersebut gagal dan tidak menampilkan permintaan sertifikat.
Tentukan cakupannya sendiri. Minta sertifikat pada tingkat server, lalu ambil keputusan untuk setiap location:
ssl_verify_client optional;
location /metrics {
if ($ssl_client_verify != SUCCESS) { return 403; }
proxy_pass http://127.0.0.1:9090;
}
location /healthz {
proxy_pass http://127.0.0.1:8080;
}$ssl_client_verify berisi SUCCESS, atau NONE jika client tidak mengirim apa pun, atau FAILED: diikuti alasan. Dengan optional, nginx meminta sertifikat dan hanya memverifikasinya jika sertifikat diterima. Inilah yang memungkinkan path publik /healthz di atas berfungsi, sementara /metrics tetap ditutup. Sertifikat yang dikirim tetapi gagal diverifikasi tetap ditolak nginx pada tahap tersebut. Jika Anda ingin memeriksa sendiri sertifikat yang gagal, gunakan optional_no_ca. Dalam kasus itu, pengujian Anda sendiri harus memperlakukan setiap nilai selain SUCCESS sebagai penolakan.
nginx memiliki kode status nonstandar untuk kondisi ini, dan error_page dapat menangkapnya agar pengunjung yang ditolak menerima penjelasan, bukan sekadar respons 400:
error_page 495 496 = @needcert;
location @needcert {
default_type text/plain;
return 200 "This host requires a client certificate. Ask ops for one.\n";
}495 berarti sertifikat client gagal diverifikasi. 496 berarti client tidak menyertakan sertifikat. Gunakan teks biasa untuk halaman tersebut, karena orang yang membacanya tidak memiliki sesi maupun akun.
Bagaimana cara memasang sertifikat klien di browser?
Firefox menggunakan penyimpanan sertifikatnya sendiri: Settings, lalu Privacy and Security, lalu View Certificates, lalu tab Your Certificates, lalu Import, kemudian pilih file .p12 dan masukkan kata sandinya.
Chrome dan Edge menggunakan penyimpanan sistem operasi di Windows dan macOS, sehingga membuka file .p12 akan memulai wizard impor sistem. Di Linux, Chrome membaca database NSS (network security services) terpisah di direktori home Anda. Gunakan alat command line sebagai cara yang andal:
sudo apt install -y libnss3-tools
pk12util -d sql:$HOME/.pki/nssdb -i alice.p12Buka situs tersebut setelahnya. Browser akan menanyakan sertifikat yang akan dikirim. Chrome mengingat pilihan tersebut selama sesi browser berlangsung. Jadi, restart browser jika Anda ingin ditanya lagi. Sertifikat tersimpan dalam satu profil browser pada satu mesin. Karena itu, sertifikat yang diimpor ke Firefox tidak terlihat oleh Chrome. Keduanya juga tidak terlihat oleh ponsel Anda.
Pengujian dengan curl --cert
Lakukan debug dengan curl karena perintah ini melaporkan tindakan yang dijalankannya.
curl -v --cert certs/alice.crt --key private/alice.key https://admin.example.com/Anda dapat menggabungkan sertifikat dan kunci ke dalam satu file PEM, lalu meneruskannya sebagai --cert alice.pem. Jika kunci memiliki passphrase, curl akan meminta passphrase tersebut. curl juga menerima --cert alice.pem:passphrase, tetapi nilainya akan tersimpan dalam shell history. Karena itu, gunakan prompt.
Sebelum menyalahkan nginx, lakukan dua pemeriksaan berikut. Pertama, sertifikat dan kunci harus merupakan pasangan:
openssl x509 -noout -pubkey -in certs/alice.crt | openssl sha256
openssl pkey -pubout -in private/alice.key | openssl sha256Hash yang identik menunjukkan bahwa kedua file tersebut saling berpasangan. Hash yang berbeda menunjukkan bahwa Anda tertukar menggunakan file milik dua pihak berbeda. Tidak ada klien yang akan menjelaskan penyebab tersebut untuk Anda.
Kedua, server seharusnya meminta CA Anda:
openssl s_client -connect admin.example.com:443 -servername admin.example.com </dev/nullCari blok Acceptable client certificate CA names dalam output, lalu cari subject CA Anda di dalamnya. Jika blok tersebut sama sekali tidak ada, nginx tidak meminta sertifikat pada server block yang memberikan respons. Artinya, direktif Anda diterapkan pada server block lain, yang sering kali merupakan default server.
Meneruskan CN klien ke aplikasi
Sertifikat menunjukkan siapa yang melakukan koneksi, tetapi aplikasi di belakang proxy tidak dapat melihat lapisan TLS. Karena itu, nginx harus meneruskan nama tersebut.
map $ssl_client_s_dn $client_cn {
default "";
"~,?CN=(?<cn>[^,]+)" $cn;
}$ssl_client_s_dn berisi distinguished name subjek dalam format RFC 2253, yang terlihat seperti CN=alice,O=Example Ops. Pemetaan tersebut mengambil field CN dan memasukkannya ke $client_cn. Gunakan CN sederhana sebagai nama pengguna, karena koma di dalam CN akan di-escape dalam format tersebut dan regular expression singkat di atas tidak menangani escape itu.
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Client-Cert-CN $client_cn;
proxy_set_header X-Client-Cert-Serial $ssl_client_serial;
}proxy_set_header mengganti header apa pun dengan nama tersebut yang dikirim oleh pemanggil. Dengan demikian, tidak ada pihak yang dapat memalsukan X-Client-Cert-CN melalui location ini. Dua kondisi diperlukan agar hal tersebut tetap berlaku. nginx hanya mewarisi proxy_set_header dari level luar jika level dalam tidak mendefinisikan konfigurasi miliknya sendiri. Artinya, location kedua yang memiliki satu baris proxy_set_header akan secara diam-diam kehilangan semua header yang ditetapkan di atasnya, termasuk header ini. Selain itu, aplikasi harus tidak dapat dijangkau selain melalui nginx. Ini berarti aplikasi harus di-bind ke 127.0.0.1, bukan 0.0.0.0, karena aplikasi pada port publik akan membaca header palsu tersebut langsung dari Internet. Konfigurasi proxy untuk kondisi ini dibahas dalam konfigurasi reverse proxy nginx yang dijelaskan baris demi baris. Jika aplikasi memerlukan seluruh sertifikat, $ssl_client_escaped_cert membawanya dalam format URL-encoded yang aman di dalam header.
Bagaimana cara mencabut satu sertifikat klien?
Seseorang keluar, atau sebuah laptop hilang. Cabut sertifikat tersebut saja dan semua orang lain tetap dapat bekerja. Inilah alasan setiap orang menerima satu sertifikat.
cd ~/client-ca
openssl ca -config openssl.cnf -revoke certs/alice.crt
openssl ca -config openssl.cnf -gencrl -out crl.pemPerintah pertama mengubah baris untuk serial tersebut dalam index.txt dari V menjadi R. Perintah kedua menulis certificate revocation list (CRL), yaitu file bertanda tangan yang mencantumkan nomor serial yang dicabut. Salin file tersebut ke server dan arahkan nginx ke sana dengan ssl_crl /etc/nginx/client-ca.crl; di samping direktif lainnya.
scp crl.pem user@admin.example.com:/tmp/client-ca.crl
ssh user@admin.example.com 'sudo install -m 644 /tmp/client-ca.crl /etc/nginx/client-ca.crl && sudo nginx -t && sudo systemctl reload nginx'Berikut jebakan yang dapat menghentikan akses semua orang. CRL memiliki tanggal nextUpdate yang ditetapkan oleh default_crl_days, yaitu 30 pada konfigurasi di atas. Setelah tanggal tersebut lewat, OpenSSL menganggap daftar itu kedaluwarsa dan gagal memverifikasi setiap sertifikat klien dengan CRL has expired, bukan hanya sertifikat yang dicabut. nginx membaca file saat memuat konfigurasinya. Karena itu, CRL baru di disk tidak berpengaruh sampai konfigurasi dimuat ulang. Buat ulang dan muat ulang CRL secara berkala, dengan jadwal yang berada jauh di dalam jangka waktu tersebut, misalnya setiap minggu untuk jangka waktu 30 hari. Periksa tanggalnya sebelum menyalin file:
openssl crl -in crl.pem -noout -lastupdate -nextupdateUntuk sejumlah kecil pengguna, tersedia opsi yang lebih sederhana. CA tersebut adalah milik Anda, sehingga nginx dapat menolak serial secara langsung dan melewati mekanisme CRL:
map $ssl_client_serial $revoked {
default 0;
"1002" 1;
}Gabungkan konfigurasi tersebut dengan if ($revoked) { return 403; } di dalam location. Konfigurasi ini tidak memiliki tanggal kedaluwarsa yang dapat terlupakan. Konfigurasi ini juga tidak dapat digunakan di tempat lain, sehingga apa pun yang mempercayai CA Anda tidak mengetahuinya. Untuk satu nginx di depan satu aplikasi, ini adalah pilihan sederhana yang tepat. Gunakan CRL setelah terdapat lebih dari satu titik akses.
Berapa lama sertifikat klien sebaiknya berlaku?
Tetapkan masa berlaku sertifikat klien selama satu tahun, atau kurang jika Anda siap menangani penerbitan ulang. Kedaluwarsa adalah kegagalan yang sering tidak terlihat karena pemegang sertifikat tidak menerima peringatan sebelumnya. Suatu pagi, mereka membuka panel, nginx menolak koneksi, dan browser menjelaskan penolakan tersebut dengan istilahnya sendiri. Biasanya, browser jarang menyebut kata “kedaluwarsa”. Tetapkan masa berlaku CA selama sepuluh tahun dan simpan tanggal kedaluwarsanya di tempat yang benar-benar akan Anda periksa. Jika sertifikat CA kedaluwarsa, semua sertifikat di bawahnya berhenti diverifikasi pada hari yang sama.
Dua perintah berikut membantu Anda memantaunya:
openssl x509 -in certs/alice.crt -noout -subject -serial -enddate
awk -F'\t' '{print $1, $2, $4}' ~/client-ca/index.txtKolom pertama index.txt adalah status: V untuk valid, R untuk dicabut, dan E untuk kedaluwarsa. Kolom kedua adalah tanggal kedaluwarsa dalam format YYMMDDHHMMSSZ, sedangkan kolom keempat adalah serial. File tersebut adalah satu-satunya catatan Anda tentang siapa yang memegang sertifikat tertentu. Cadangkan file ini bersama kunci CA dan perlakukan keduanya sebagai rahasia.
Perpanjangan berarti membuat sertifikat baru, bukan memperpanjang sertifikat lama. Buat kunci dan CSR (certificate signing request) baru, tanda tangani, serahkan kepada pemegangnya, lalu cabut sertifikat lama setelah orang tersebut memastikan sertifikat baru berfungsi.
Perlindungan mTLS dan hal-hal yang tidak dilindunginya
Yang dihilangkan adalah akses tanpa autentikasi. Pemindai yang menemukan hostname Anda akan ditolak saat handshake. Pemindai tersebut tidak pernah mengirim permintaan HTTP, tidak pernah melihat formulir login, dan tidak pernah dapat mencoba kata sandi curian pada formulir itu. Credential stuffing tidak memiliki kredensial untuk dicoba. Kerentanan pada alur login aplikasi tidak dapat dijangkau oleh siapa pun yang tidak memiliki sertifikat. mTLS juga menghilangkan shared secret yang biasanya ditempel orang ke chat, karena private key berupa file yang sulit tersalin secara tidak sengaja.
mTLS tidak melindungi dari client yang telah disusupi. Malware pada laptop memiliki file key tersebut. Malware juga memperoleh passphrase segera setelah pemilik mengetikkannya. Bagi server, penyerang itu terlihat persis seperti pengguna yang sah, karena sertifikat membuktikan kepemilikan file, bukan kehadiran seseorang. Kata sandi .p12 dan enkripsi seluruh disk tetap penting.
mTLS juga bukan authorization. Setiap pemegang sertifikat yang valid dapat mengakses semua layanan yang disediakan server block tersebut, kecuali Anda memeriksa $client_cn dan bertindak berdasarkan nilainya. Secara default, dua pemegang sertifikat memiliki akses yang sama.
mTLS juga hanya melindungi jalur melalui nginx. Jika aplikasi juga listen pada port publik, mTLS di depannya hanya menjadi hiasan. Bind aplikasi ke 127.0.0.1 dan pastikan firewall menutup portnya. Pintu lain ke server yang sama adalah SSH. Pintu ini juga memerlukan perhatian yang sama, seperti dijelaskan dalam memperkuat keamanan akses SSH pada VPS.
Ada satu batasan terakhir, dan dampaknya langsung terasa saat Anda mengaktifkannya. Semua hal yang tidak dapat menyajikan sertifikat akan berhenti berfungsi: monitor uptime, webhook dari penyedia pembayaran, pembaca RSS, atau aplikasi seluler yang tidak memiliki certificate store yang dapat Anda akses. Tentukan penanganannya sebelum mengatur ssl_verify_client on, karena kegagalannya bersifat total dan biasanya tidak terlihat di sisi mereka.
Baca laporan dari klien saat koneksi ditolak
Pesan yang ditampilkan klien yang ditolak bergantung pada browser, versi curl, dan pustaka TLS yang digunakan, jadi baca keluaran dari klien Anda sendiri. Jangan mencocokkannya dengan pesan yang ditulis di tempat lain. Detail yang berguna ada di server.
sudo tail -n 50 /var/log/nginx/error.logSertifikat yang ditolak menghasilkan baris yang berisi client SSL certificate verify error, diikuti alasan yang diberikan OpenSSL. Alasan tersebut adalah dasar tindakan yang perlu diambil. Biasanya penyebabnya termasuk salah satu dari beberapa hal berikut. Sertifikat berasal dari CA yang berbeda dari file yang disebutkan dalam ssl_client_certificate. Sertifikat berada di luar masa berlaku. CRL di server telah melewati nextUpdate, sehingga sekarang menyebabkan semua klien gagal, bukan hanya satu klien.
Jika browser sama sekali tidak menawarkan sertifikat, masalahnya terjadi sebelum tahap verifikasi. nginx mengirimkan nama penerbit yang dapat diterima selama handshake. Browser tidak menemukan apa pun di penyimpanannya yang cocok, sehingga tidak memiliki sertifikat untuk ditawarkan. Impor kembali .p12 ke profil yang benar-benar Anda gunakan untuk menjelajah.
Ada satu kasus lain yang perlu disebutkan. Jika Anda menguji dengan sertifikat klien self-signed tunggal, bukan sertifikat yang ditandatangani oleh CA Anda, verifikasi tidak dapat berhasil karena nginx memeriksa tanda tangan terhadap file CA, sedangkan sertifikat self-signed tidak tercantum di dalamnya. Proses pembuatan sertifikat sama seperti pada membuat sertifikat self-signed di Ubuntu. mTLS hanya memerlukan langkah tambahan untuk menandatanganinya dengan CA Anda.
FAQ
Apakah saya masih memerlukan sertifikat Let's Encrypt jika menggunakan mTLS?
Ya. Kedua sertifikat tersebut tidak berkaitan. Server Anda menyajikan sertifikatnya sendiri agar browser memercayai hostname, dan sertifikat itu tetap harus berasal dari CA yang sudah dikenal browser. CA klien Anda adalah rantai privat terpisah yang hanya digunakan untuk memeriksa identitas pihak yang terhubung. Pengaturan ssl_client_certificate tidak mengubah sertifikat yang disajikan nginx dan tidak boleh menunjuk ke rantai Let's Encrypt Anda.
Mengapa browser saya tidak pernah meminta saya memilih sertifikat?
nginx mengirimkan daftar penerbit yang diterima selama handshake. Daftar tersebut dibuat dari file pada ssl_client_certificate. Browser hanya menawarkan sertifikat yang penerbitnya tercantum dalam daftar itu. Tidak munculnya permintaan berarti browser tidak memiliki sertifikat dari CA Anda. Sertifikat mungkin diimpor ke profil browser yang berbeda, atau ditandatangani oleh CA yang berbeda dari CA yang dipasang di server. Jalankan openssl s_client -connect admin.example.com:443 dan cari nama CA sertifikat klien yang diterima pada output untuk melihat CA yang sebenarnya diminta oleh server.
Apakah saya dapat mewajibkan sertifikat klien hanya pada satu URL?
Tidak dengan ssl_verify_client on di dalam location. Sertifikat dipertukarkan selama handshake, sebelum nginx mengetahui path permintaan. Renegosiasi yang dapat digunakan untuk mengatasi hal tersebut sudah dihapus dari TLS 1.3 dan dilarang dalam HTTP/2. Atur ssl_verify_client optional; di server block, lalu pada setiap location yang dilindungi, uji $ssl_client_verify dan kembalikan 403 jika nilainya bukan SUCCESS.
Bagaimana cara mencabut akses satu orang?
Cabut sertifikat tersebut dengan openssl ca -revoke, buat ulang daftar menggunakan openssl ca -gencrl, salin daftar itu ke server, lalu reload nginx agar membaca file baru. Pengguna lain tidak terdampak. Hal ini hanya berlaku jika setiap orang memiliki sertifikat sendiri, bukan sertifikat bersama. Perhatikan tanggal nextUpdate pada CRL karena CRL yang kedaluwarsa menyebabkan verifikasi gagal untuk semua klien, bukan hanya klien yang sertifikatnya dicabut.
Apakah mTLS menggantikan halaman login?
Untuk akses ke aplikasi, ya. Tanpa sertifikat, tidak ada koneksi yang dapat mencapai aplikasi. Dengan demikian, tidak ada formulir yang dapat diserang dan tidak ada kata sandi yang dapat ditebak. Untuk identitas di dalam aplikasi, tidak. Sertifikat membuktikan bahwa pihak pemanggil memiliki file key, sehingga laptop yang dicuri tetap dapat digunakan sebagai pengguna yang valid. Teruskan CN ke upstream, pertahankan akun dan izin yang sudah dimiliki aplikasi, lalu perlakukan sertifikat sebagai gerbang di depannya.