SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor

Nginx uzerinde mTLS ve istemci sertifikasi yapilandirmasi

Nginx ile yonetim panellerini korumak icin mTLS kurulumunu ogrenin. OpenSSL ile ozel CA olusturma, istemci sertifikasi imzalama ve 400 Bad Request hatalarini onleme rehberi.

mTLS nedir

Genellikle mTLS olarak yazılan Mutual TLS, Nginx'in her istemciden bir sertifika talep etmesini sağlar. İstemci sertifikası eksikse veya sizin kontrolünüzdeki bir sertifika otoritesi (CA) tarafından düzenlenmemişse, Nginx isteği reddeder. Bu kontrol, TLS (transport layer security) el sıkışması sırasında gerçekleşir; dolayısıyla geçerli bir istemci sertifikasına sahip olmayan bir arayan, uygulamanıza hiçbir şekilde ulaşamaz. Sistemin avantajı budur: Bir yönetici paneli veya metrik uç noktası, herhangi bir giriş sayfası olmadan ve botların tahmin edebileceği hiçbir şey barındırmadan genel internet üzerinde güvenle durabilir.

Kurulumu basittir. openssl ile oluşturulan bir özel CA, kişi başına bir sertifika ve Nginx server bloğu içinde üç direktif yeterlidir. Bu yapının bir yıl boyunca sorunsuz çalışmasını sağlayacak olan şey operasyonel süreçtir; bu nedenle kılavuzun büyük bir kısmı sertifika ömürleri, iptal işlemleri, kişiye özel sertifikalar ve bir istemci reddedildiğinde ve nedenini kimse göremediğinde yapılması gerekenler üzerine odaklanmaktadır.

Tek değil, iki zincir

mTLS kurulumunda birbirinden tamamen bağımsız iki sertifika zinciri bulunur. Bu iki zinciri birleştirmek, hemen herkesin yaptığı ilk hatadır.

Birinci zincir sunucuya aittir. VPS'niz, Let's Encrypt gibi halka açık bir CA tarafından verilen admin.example.com için bir sertifika sunar ve tarayıcı, bunu işletim sistemiyle birlikte gelen kök sertifika deposuna (root store) göre doğrular. mTLS süreci bu tarafı hiçbir şekilde değiştirmez. Eğer certbot bu sertifikayı bugün sizin için düzenliyorsa, olduğu gibi bırakın: bkz. certbot ile nginx için Let's Encrypt sertifikası düzenleme.

İkinci zincir ise istemciye aittir. Kendi küçük CA'nızı oluşturursunuz, sisteme erişmesi gereken her kişi için bir sertifika imzalarsınız ve nginx'e istemcileri doğrulaması sırasında sadece bu CA'ya güvenmesini söylersiniz. Hiçbir halka açık kök sertifika deposu sizin CA'nızı tanımaz ve tanımasına da gerek yoktur. Bu CA'ya güvenmesi gereken tek taraf, ssl_client_certificate dosyası aracılığıyla nginx'tir.

Dolayısıyla ssl_client_certificate, nginx'in sunduğu sertifikayı hiçbir şekilde etkilemez; Let's Encrypt zinciri de hangi istemcilerin içeri alınacağını belirlemez. ssl_client_certificate yönergesini fullchain.pem dosyasına işaret etmek, göründüğü işi yapmaz: bu yönerge, bir istemci sertifikasının gelebileceği düzenleyicileri tanımlar; yani bağlantının diğer ucunu belirtir. Sunucunun kendi giden trafiği için sizin CA'nıza güvenmesini sağlamak ayrı bir işlemdir ve Ubuntu güven deposuna kendi CA'nızı ekleme bölümünde açıklanmıştır; sistem güven deposu, nginx'in bir istemciyi doğrulamak için okuduğu yer değildir.

Kendi istemci CA'nızı openssl ile oluşturun

CA'yı web sunucusu dışında bir yerde oluşturun. nginx yalnızca CA'nın genel sertifikasına ihtiyaç duyar. CA özel anahtarı yeni istemci sertifikalarını imzalar; bu nedenle anahtarı internete açık bir makinede bırakmak, bir sızma durumunda saldırgana kendi adına geçerli istemci sertifikaları oluşturma yetkisi verir.

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 > crlnumber

index.txt, serial ve crlnumber CA veritabanını oluşturur. openssl ca bunlar olmadan çalışmayı reddeder. Ayrıca bunlar, daha sonra iptal işlemlerini mümkün kılan unsurlardır; çünkü bir iptal listesi seri numaralarını belirtir, bu yüzden CA hangi seri numarasının kime verildiğini hatırlamak zorundadır.

~/client-ca/openssl.cnf dosyasını yazın. openssl ca, ~ ifadesini genişletmediği için dir değerini ilgili dizinin gerçek yolu olarak ayarlayın.

[ 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,issuer

Şimdi CA anahtarını ve kendinden imzalı sertifikasını oluşturun:

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, CA anahtarına bir parola koyar, böylece her imzalama işleminde parola sorulur. Amacı da budur. Oluşturduklarınızı kontrol edin:

openssl x509 -in ca.crt -noout -subject -dates -ext basicConstraints

Konu (subject) CA'nız olmalı ve geçerlilik süresi on yıl olmalıdır. Uzantı satırı CA:TRUE, pathlen:0 şeklinde görünmelidir. pathlen:0, bu CA'nın uç sertifikaları imzalayabileceği ancak başka bir CA'yı imzalayamayacağı anlamına gelir; bu durum zinciri tam olarak bir seviye derinlikte tutar ve ssl_verify_depth ayarını olduğu gibi bırakmanıza olanak tanır.

Kişi başına bir istemci sertifikası düzenleyin

Kişi başına bir sertifika düzenlenmelidir. Bir ekip için asla ortak bir sertifika kullanmayın; çünkü ortak bir sertifika, herkesin erişimini kesmeden iptal edilemez ve kimin çağrı yaptığına dair hiçbir bilgi vermez.

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.crt

openssl ca, imzalamak üzere olduğu sertifikayı yazdırır, CA parolasını sorar, onay için iki kez doğrulama ister ve ardından index.txt dosyasına bir satır ekler. Komut dosyası haline getirirken -batch ekleyin. client_ext bölümü, içindeki tek bir satır nedeniyle önemlidir: extendedKeyUsage = clientAuth. Yalnızca serverAuth içeren bir genişletilmiş anahtar kullanımı (extended key usage) listesine sahip sertifikalar, istemci kimlik doğrulaması için uygun olmadığı gerekçesiyle reddedilir; bu nedenle varsayımlara dayanmak yerine kullanım amacını açıkça belirtin.

Sertifikayı teslim etmeden önce çifti CA ile doğrulayın:

openssl verify -CAfile ca.crt certs/alice.crt

Bu komut certs/alice.crt: OK çıktısını verir. Başka herhangi bir çıktı, sertifika ile CA'nın eşleşmediği anlamına gelir ve hiçbir nginx yapılandırması bunu düzeltemez.

Anahtarı ve sertifikayı, tarayıcının içe aktarabileceği tek bir dosyada birleştirin:

openssl pkcs12 -export -inkey private/alice.key -in certs/alice.crt \
  -name "alice at example ops" -out alice.p12

Dışa aktarma işlemi bir parola ister; bu parola dosyayı taşıma sırasında korur. Dosyayı ve parolayı farklı kanallardan gönderin ve kullanıcılara ham bir .key yerine .p12 dosyasını iletin. CA'yı pakete dahil etmek için -certfile ca.crt ekleyebilirsiniz ancak nginx buna ihtiyaç duymaz; nginx zaten ca.crt dosyasını tutar, bu nedenle doğrudan o CA tarafından imzalanmış bir sertifika kendi başına doğrulanabilir.

Ubuntu 24.04 ile gelen OpenSSL 3, PKCS#12 dosyalarını güncel şifreleme yöntemleriyle yazar ve Ağustos 2026 itibarıyla kullanımda olan tarayıcılar ve işletim sistemleri bunları okuyabilir. Eğer eski bir içe aktarma aracı dosyayı reddederse, ilgili aracın beklediği eski algoritmalara geri dönen -legacy bayrağını ekleyerek yeniden dışa aktarın. Bu bayrağa başvurmadan önce içe aktarma aracının verdiği mesajı okuyun.

nginx üzerinde ssl_client_certificate ve ssl_verify_client yapılandırması

CA sertifikasını, yalnızca CA sertifikasını, sunucuya kopyalayın.

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'

Burada 644 modu doğrudur. CA sertifikası herkese açık bir bilgidir. CA anahtarı ise iş istasyonunuzda kalmalıdır.

Ardından, TLS sonlandırmasını halihazırda yapan server bloğuna üç direktif ekleyin:

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, nginx varsayılanıdır ve istemci sertifikasının doğrudan bu dosyadaki CA tarafından imzalanmış olması gerektiğini belirtir. Bu değeri yalnızca bir ara (intermediate) sertifika eklerseniz yükseltin. nginx ayrıca el sıkışma sırasında ssl_client_certificate içindeki konu adlarını istemciye gönderir; tarayıcı, hangi sertifikasını sunacağını bu sayede anlar. Bu davranış, aynı şekilde doğrulama yapan ancak herhangi bir liste göndermeyen ssl_trusted_certificate yerine ssl_client_certificate kullanılmasının nedenidir.

Ubuntu 24.04, nginx 1.24 ile gelir; bu sürümde HTTP/2, listen satırında listen 443 ssl http2; olarak tanımlanır. nginx 1.25.1 ve sonraki sürümlerde bu kullanım kullanımdan kaldırılmıştır ve HTTP/2 kendi direktifi olan http2 on; ile tanımlanır. Her iki seçenek de sertifika kontrolünü değiştirmez.

Yapılandırmayı yeniden yükleyin ve sonucu okuyun:

sudo nginx -t && sudo systemctl reload nginx
curl -i https://admin.example.com/

nginx -t, syntax is ok ve test is successful çıktılarını verir. curl çağrısı herhangi bir sertifika taşımaz, bu nedenle No required SSL certificate was sent gövdesiyle birlikte 400 Bad Request yanıtını döndürmelidir. Bu, nginx'in kendi kapısında reddetme yaptığı anlamına gelir; yani yapılandırma aktiftir ve uygulamaya hiçbir istek ulaşmamıştır. Şimdi düzgün şekilde deneyin:

curl --cert certs/alice.crt --key private/alice.key https://admin.example.com/

Bu komut, uygulamanızın sunduğu içeriği döndürmelidir.

Neden gate server bloğu içinde yer almalıdır

Sertifika, TLS el sıkışması sırasında, nginx henüz bir istek satırını okumadan önce değiş tokuş edilir; bu nedenle o anda nginx, isteğin hangi location içine düşeceğini bilmez. ssl_verify_client on; ifadesini bir location içine yerleştirmek, istemciden bağlantının ortasında yeniden anlaşma yapmasını ister. TLS 1.3 yeniden anlaşmayı kaldırmış, HTTP/2 ise bunu yasaklamıştır; bu yüzden güncel bir yığında bu desen, uyarı vermek yerine başarısız olur.

Kapsam belirleme işlemini kendiniz yapın. Sertifikayı server seviyesinde isteyin, ardından her location için karar verin:

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, SUCCESS değerini veya istemci hiçbir şey göndermediğinde NONE değerini ya da bir nedenin takip ettiği FAILED: değerini tutar. optional ile nginx bir sertifika talep eder ve yalnızca bir sertifika gelirse doğrular; yukarıdaki genel /healthz yolunun çalışmasını sağlayan ve /metrics kısmının kapalı kalmasına izin veren mekanizma budur. Gönderilen ve doğrulaması başarısız olan bir sertifika, o noktada nginx tarafından reddedilir. Bunun yerine başarısız olan bir sertifikayı kendiniz incelemek isterseniz, bu optional_no_ca işlemidir ve bu durumda kendi testinizin SUCCESS dışındaki her değeri bir ret olarak ele alması gerekir.

nginx'in bunun için standart dışı durum kodları vardır ve error_page bunları yakalayabilir; böylece reddedilen bir ziyaretçi, boş bir 400 hatası yerine bir açıklama görür:

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, istemci sertifikasının doğrulanamadığı anlamına gelir. 496, istemcinin hiçbir sertifika sunmadığı anlamına gelir. Bu sayfayı düz metin olarak tutun, çünkü okuyan kişinin bir oturumu veya hesabı yoktur.

İstemci sertifikasını tarayıcıya nasıl yüklerim?

Firefox kendi sertifika deposunu kullanır: Ayarlar, ardından Gizlilik ve Güvenlik, ardından Sertifikaları Görüntüle, ardından Sertifikalarınız sekmesi, ardından İçe Aktar yolunu izleyin ve .p12 dosyasını seçip parolasını girin.

Chrome ve Edge, Windows ve macOS üzerinde işletim sistemi deposunu kullanır; bu nedenle .p12 dosyasını açmak sistemin içe aktarma sihirbazını başlatır. Linux üzerinde Chrome, ev dizininizdeki ayrı bir NSS (network security services) veritabanını okur; bu durumda komut satırı aracı güvenilir yöntemdir:

sudo apt install -y libnss3-tools
pk12util -d sql:$HOME/.pki/nssdb -i alice.p12

İşlemden sonra siteyi yükleyin; tarayıcı hangi sertifikanın gönderileceğini soracaktır. Chrome bu seçimi tarayıcı oturumu boyunca hatırlar; bu nedenle tekrar sorulmasını istiyorsanız tarayıcıyı yeniden başlatın. Sertifika, tek bir makinedeki tek bir tarayıcı profilinde yaşar; bu nedenle Firefox'a aktarılan bir sertifika Chrome tarafından görülmez ve her ikisi de telefonunuzda görünmez.

curl --cert ile test etme

curl ile hata ayıklayın, çünkü ne yaptığını raporlar.

curl -v --cert certs/alice.crt --key private/alice.key https://admin.example.com/

Sertifikayı ve anahtarı tek bir PEM dosyasında birleştirebilir ve --cert alice.pem olarak iletebilirsiniz. Anahtarın bir parola koruması varsa, curl bunu soracaktır. Ayrıca --cert alice.pem:passphrase de kabul edilir, ancak bu komut geçmişinize kaydedileceği için parola istemini kullanmanız daha güvenlidir.

Nginx'i suçlamadan önce iki kontrolün yapılması yararlıdır. İlk olarak, sertifika ve anahtar bir çift olmalıdır:

openssl x509 -noout -pubkey -in certs/alice.crt | openssl sha256
openssl pkey -pubout -in private/alice.key | openssl sha256

İki özdeş hash değeri, dosyaların birbirine ait olduğu anlamına gelir. İki farklı hash değeri, iki farklı dosyayı karıştırdığınızı gösterir; hiçbir istemci size bu hatanın nedenini açıkça belirtmeyecektir.

İkinci olarak, sunucu CA sertifikanızı talep ediyor olmalıdır:

openssl s_client -connect admin.example.com:443 -servername admin.example.com </dev/null

Çıktı içerisinde Acceptable client certificate CA names bloğunu ve bu bloğun içinde CA'nizin konu (subject) bilgisini arayın. Eğer bu blok tamamen eksikse, Nginx yanıt veren sunucu bloğunda bir sertifika talep etmiyordur; bu durumda yönergeleriniz genellikle varsayılan sunucu (default server) olan farklı bir bloğa düşmüştür.

İstemci CN bilgisinin uygulamaya aktarılması

Sertifika, çağrıyı kimin yaptığını belirtir ancak proxy arkasındaki uygulama TLS katmanını göremez; bu nedenle nginx'in bu ismi iletmesi gerekir.

map $ssl_client_s_dn $client_cn {
    default              "";
    "~,?CN=(?<cn>[^,]+)" $cn;
}

$ssl_client_s_dn, RFC 2253 biçimindeki özne ayırt edici adını (distinguished name) tutar ve bu, CN=alice,O=Example Ops gibi görünür. Map bloğu, CN alanını $client_cn değişkenine aktarır. CN değerini düz bir kullanıcı adı olarak tutun; çünkü bu biçimde CN içindeki virgüller kaçış karakteriyle korunur ve yukarıdaki basit düzenli ifade bu kaçış karakterlerini işlemez.

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, çağrı yapan tarafın gönderdiği aynı isimli tüm başlıkları değiştirir; böylece hiç kimse bu konum üzerinden X-Client-Cert-CN başlığını taklit edemez. Bu durumun geçerli kalması için iki koşul gerekir. nginx, proxy_set_header değerini yalnızca iç seviyede hiçbir tanım yoksa dış seviyeden devralır; bu nedenle tek bir proxy_set_header satırı içeren ikinci bir konum bloğu, yukarıda ayarlanan tüm başlıkları (buna dahil olanlar dahil) sessizce devre dışı bırakır. Ayrıca uygulamanın yalnızca nginx üzerinden erişilebilir olması gerekir; bu da uygulamanın 0.0.0.0 yerine 127.0.0.1 adresine bağlanması anlamına gelir, çünkü herkese açık bir porttaki uygulama taklit edilmiş başlığı doğrudan internet üzerinden okuyabilir. Bunun proxy tarafı satır satır açıklanan bir nginx reverse proxy yapılandırması içinde ele alınmıştır. Eğer uygulama isim yerine sertifikanın tamamını istiyorsa, $ssl_client_escaped_cert bunu URL kodlamalı ve güvenli bir şekilde bir başlık içinde taşır.

Tek bir istemci sertifikasını nasıl iptal ederim?

Bir çalışan işten ayrıldığında veya bir dizüstü bilgisayar kaybolduğunda, sadece ilgili sertifikayı iptal ederek diğer kullanıcıların çalışmaya devam etmesini sağlayabilirsiniz. Kişi başına ayrı sertifika düzenlemenin temel amacı budur.

cd ~/client-ca
openssl ca -config openssl.cnf -revoke certs/alice.crt
openssl ca -config openssl.cnf -gencrl -out crl.pem

İlk komut, index.txt dosyasındaki ilgili seri numarasına ait satırı V durumundan R durumuna getirir. İkinci komut ise iptal edilen seri numaralarını içeren imzalı bir dosya olan sertifika iptal listesini (CRL) oluşturur. Bu dosyayı sunucuya aktarın ve ssl_crl /etc/nginx/client-ca.crl; yönergesi ile diğer direktiflerin yanına ekleyerek nginx yapılandırmasında belirtin.

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'

Burada herkesin erişimini engelleyebilecek bir tuzak bulunmaktadır. Bir CRL, default_crl_days tarafından belirlenen ve yukarıdaki yapılandırmada 30 gün olarak ayarlanan bir nextUpdate tarihine sahiptir. Bu tarih geçtiğinde, OpenSSL listeyi güncelliğini yitirmiş kabul eder ve sadece iptal edilen sertifika için değil, tüm istemci sertifikaları için CRL has expired hatası vererek doğrulamayı reddeder. nginx, yapılandırmasını yüklediği sırada dosyayı okur; bu nedenle diskteki yeni bir CRL, servis yeniden yüklenene kadar bir değişiklik yaratmaz. CRL dosyasını 30 günlük sürenin içinde kalacak şekilde, örneğin haftalık olarak yeniden oluşturun ve kopyalamadan önce tarihleri kontrol edin:

openssl crl -in crl.pem -noout -lastupdate -nextupdate

Az sayıda kullanıcı için daha basit bir seçenek mevcuttur. CA sizin kontrolünüzde olduğundan, nginx bir seri numarasını doğrudan reddedebilir ve CRL mekanizmasını kullanmanıza gerek kalmaz:

map $ssl_client_serial $revoked {
    default 0;
    "1002"  1;
}

Bunu, ilgili location bloğunda if ($revoked) { return 403; } ile birlikte kullanın. Bu yöntemde unutulması gereken bir son kullanma tarihi yoktur. Ayrıca bu ayar taşınabilir değildir; bu nedenle CA'nize güvenen başka herhangi bir sistem bu iptal işleminden haberdar olmaz. Tek bir uygulama önünde duran tek bir nginx örneği için bu en dürüst ve basit çözümdür. Birden fazla giriş noktası olduğunda CRL yapısına geçiş yapın.

İstemci sertifikalarının ömrü ne kadar olmalıdır?

İstemci sertifikalarına bir yıl veya yeniden düzenleme iş yükünü göze alabiliyorsanız daha kısa bir süre tanımlayın. Sertifika süresinin dolması, sessiz bir hata türüdür çünkü kullanıcıya önceden herhangi bir uyarı gitmez. Bir sabah panele giriş yapmaya çalıştıklarında nginx bağlantıyı reddeder; tarayıcı ise bu reddi kendi ifadeleriyle açıklar ve bu ifadeler nadiren "süresi doldu" bilgisini içerir. CA sertifikasının ömrünü on yıl olarak belirleyin ve son kullanma tarihini mutlaka göreceğiniz bir yere not edin; çünkü CA sertifikasının süresi dolduğunda, onun altında imzalanmış tüm sertifikalar aynı gün doğrulamayı durdurur.

Aşağıdaki iki komut bu durumun önüne geçmenizi sağlar:

openssl x509 -in certs/alice.crt -noout -subject -serial -enddate
awk -F'\t' '{print $1, $2, $4}' ~/client-ca/index.txt

index.txt dosyasının ilk sütunu durumu gösterir: V geçerli, R iptal edilmiş, E süresi dolmuş anlamına gelir. İkinci sütun YYMMDDHHMMSSZ formatındaki son kullanma tarihini, dördüncü sütun ise seri numarasını belirtir. Bu dosya, kimin elinde ne olduğunu gösteren tek kayıt kaynağınızdır; bu nedenle dosyayı CA anahtarı ile birlikte yedekleyin ve her ikisini de gizli veri olarak saklayın.

Yenileme işlemi bir uzatma değil, yeni bir sertifika oluşturma sürecidir. Yeni bir anahtar ve CSR (sertifika imzalama isteği) oluşturun, imzalayın, kullanıcıya iletin ve kullanıcı yeni sertifikanın çalıştığını onayladığında eskisini iptal edin.

mTLS neye karşı korur, neye karşı korumaz

mTLS, kimliği doğrulanmamış erişimi ortadan kaldırır. Sunucu adınızı bulan bir tarayıcı, el sıkışma (handshake) aşamasında reddedilir; bu nedenle HTTP isteği gönderemez, giriş formunu göremez ve çalınmış bir parolayı deneme şansı bulamaz. Kimlik bilgisi doldurma (credential stuffing) saldırıları için bir hedef kalmaz. Uygulamanın giriş akışındaki bir güvenlik açığına, sertifikası olmayan hiç kimse ulaşamaz. Ayrıca, insanların sohbet uygulamalarında paylaştığı paylaşımlı gizli anahtarlar (shared secret) sorununu da ortadan kaldırır; çünkü özel anahtar, yanlışlıkla kopyalanması zor olan bir dosyadır.

mTLS, ele geçirilmiş bir istemciye karşı hiçbir koruma sağlamaz. Dizüstü bilgisayardaki kötü amaçlı yazılım, anahtar dosyasına sahiptir ve sahibi parolasını girdiği anda onu da ele geçirir. Sunucu açısından saldırgan, meşru bir kullanıcı gibi görünür; çünkü sertifika bir kişinin varlığını değil, bir dosyanın sahipliğini kanıtlar. .p12 parolası ve tam disk şifrelemesi hala önemini korumaktadır.

mTLS bir yetkilendirme yöntemi de değildir. $client_cn değerini kontrol edip buna göre işlem yapmadığınız sürece, geçerli bir sertifikaya sahip herkes sunucu bloğunun sunduğu her şeye erişebilir. Varsayılan olarak, iki sertifika sahibi de aynı erişim haklarına sahiptir.

Ayrıca mTLS yalnızca nginx üzerinden geçen yolu korur. Uygulama aynı zamanda genel bir port üzerinden dinleme yapıyorsa, önündeki mTLS yalnızca bir süstür: uygulamayı 127.0.0.1 adresine bağlayın ve portunu güvenlik duvarında kapalı tutun. Aynı kutuya açılan diğer kapı SSH'tır ve bu kapı da VPS üzerinde SSH erişimini sıkılaştırma bölümünde ele alındığı gibi aynı ilgiyi hak eder.

Son bir sınırlama daha vardır ve bunu etkinleştirdiğiniz gün sorun yaratır: Sertifika sunamayan her şey çalışmayı durdurur; bir çalışma süresi izleyicisi (uptime monitor), bir ödeme sağlayıcısından gelen webhook, bir RSS okuyucu veya sertifika deposuna erişemediğiniz bir mobil uygulama gibi. ssl_verify_client on ayarını yapmadan önce bunlar hakkında kararınızı verin; çünkü yaşanacak hata tam kapsamlıdır ve karşı taraf için sessizce gerçekleşir.

Bir istemci reddedildiğinde, istemcinin ne bildirdiğini okuyun

Reddedilen bir istemcinin gösterdiği mesaj tarayıcıya, curl sürümüne ve altta yatan TLS kütüphanesine bağlıdır; bu nedenle başka bir yerde yazılı bir mesajla eşleştirmeye çalışmak yerine kendi istemcinizin ne yazdırdığını okuyun. Yararlı ayrıntı sunucudadır.

sudo tail -n 50 /var/log/nginx/error.log

Reddedilen bir sertifika, client SSL certificate verify error ifadesini ve ardından OpenSSL'in verdiği nedeni içeren bir satır bırakır. İşlem yapılması gereken gerçek, bu nedendir. Genellikle bu durum birkaç şeyden biridir. Sertifika, ssl_client_certificate içinde belirtilen dosyadan farklı bir CA'den gelmiştir. Sertifika, geçerlilik tarihleri dışındadır. Sunucudaki CRL, nextUpdate süresini doldurmuştur; bu nedenle artık tek bir istemci yerine tüm istemcileri reddetmektedir.

Tarayıcı hiçbir sertifika sunmadığında, sorun doğrulamadan daha önceki bir aşamadadır. nginx, el sıkışma sırasında kabul edilebilir yayıncı adlarını gönderir; tarayıcı kendi deposunda eşleşen hiçbir şey bulamadığı için size sunacak bir şeyi yoktur. .p12 dosyasını, gerçekten gezinti yaptığınız profile tekrar içe aktarın.

Bahsetmeye değer bir durum daha vardır. CA'nizin imzaladığı bir sertifika yerine tek başına kendinden imzalı (self-signed) bir istemci sertifikası ile test yaptıysanız, doğrulama başarılı olamaz; çünkü nginx imzayı CA dosyasına göre kontrol eder ve kendinden imzalı bir sertifika bu dosyada bulunmaz. Sertifika oluşturma mekaniği, Ubuntu üzerinde kendinden imzalı sertifika oluşturma ile aynıdır. mTLS, yalnızca CA'nizin sertifikayı imzaladığı ek bir adımı gerektirir.

FAQ

mTLS kullanıyorsam yine de Let's Encrypt sertifikasına ihtiyacım var mı?

Evet. İki sertifika birbirinden bağımsızdır. Sunucunuz, tarayıcının ana bilgisayar adını doğrulaması için kendi sertifikasını sunar ve bu sertifikanın tarayıcı tarafından zaten bilinen bir CA tarafından imzalanmış olması gerekir. İstemci CA'nız ise yalnızca kimin bağlandığını kontrol etmek için kullanılan ayrı bir özel zincirdir. ssl_client_certificate ayarını yapmak, nginx'in sunduğu sertifikada hiçbir şeyi değiştirmez ve bu ayar Let's Encrypt zincirinizi işaret etmemelidir.

Tarayıcım neden benden hiçbir zaman sertifika seçmemi istemiyor?

nginx, el sıkışma sırasında ssl_client_certificate içindeki dosyadan oluşturulan kabul edilebilir yetkililerin bir listesini gönderir. Bir tarayıcı yalnızca vereni bu listede yer alan sertifikaları sunar. Dolayısıyla bir istemin gelmemesi, tarayıcınızın CA'nıza ait herhangi bir sertifika tutmadığı anlamına gelir: sertifika farklı bir tarayıcı profiline aktarılmış olabilir veya sertifika sunucuda yüklü olandan farklı bir CA tarafından imzalanmış olabilir. openssl s_client -connect admin.example.com:443 komutunu çalıştırın ve sunucunun aslında hangi CA'yı talep ettiğini görmek için çıktıdaki kabul edilebilir istemci sertifikası CA adlarını inceleyin.

İstemci sertifikasını yalnızca tek bir URL için zorunlu tutabilir miyim?

location içindeki ssl_verify_client on ile bu mümkün değildir. Sertifika, nginx istek yolunu öğrenmeden önce, el sıkışma sırasında değiş tokuş edilir. Bu sorunu aşmayı sağlayacak yeniden müzakere (renegotiation) süreci TLS 1.3'ten kaldırılmıştır ve HTTP/2'de yasaklanmıştır. ssl_verify_client optional; ayarını sunucu bloğunda yapın, ardından korumalı her konum (location) bloğunda $ssl_client_verify değerini test edin ve değer SUCCESS değilse 403 hatası döndürün.

Bir kişinin erişimini nasıl iptal ederim?

İlgili sertifikayı openssl ca -revoke ile iptal edin, listeyi openssl ca -gencrl ile yeniden oluşturun, sunucuya kopyalayın ve yeni dosyayı okuması için nginx'i yeniden yükleyin. Herkesin paylaşımlı bir sertifika yerine kendi sertifikasına sahip olması durumunda, diğer kullanıcılar bu durumdan etkilenmez. CRL'nin nextUpdate tarihini takip edin; çünkü süresi dolmuş bir CRL, yalnızca iptal edilenler için değil, tüm istemciler için doğrulamanın başarısız olmasına neden olur.

mTLS bir giriş sayfasının yerini tutar mı?

Erişim açısından evet: sertifika olmadan uygulamaya hiçbir veri ulaşmaz, bu nedenle saldırılabilecek bir form veya tahmin edilebilecek bir parola yoktur. Uygulama içindeki kimlik doğrulaması açısından ise hayır. Bir sertifika, arayanın bir anahtar dosyasına sahip olduğunu kanıtlar; bu nedenle çalınan bir dizüstü bilgisayar geçerli bir kullanıcı olarak kabul edilir. CN değerini yukarı yöne (upstream) iletin, uygulamanın mevcut hesap ve izin yapısını koruyun ve sertifikayı bu yapıların önündeki bir kapı olarak kullanın.

#tls#mtls#nginx#openssl#access-control