SSD Nodes Learn 🎉 VPS $5.50/महिन्यापासून
मार्गदर्शक Matt Connorद्वारे Matt Connor

nginx मध्ये mTLS ने admin panel सुरक्षित कसे करावे

openssl वापरून private CA आणि client certificates तयार करा, nginx मधील तीन directives लावा आणि certificate नसलेल्या प्रत्येक request ला TLS handshake मध्येच नकार द्या.

mTLS काय करते

Mutual TLS, ज्याला सामान्यतः mTLS असे लिहिले जाते, nginx ला प्रत्येक client कडून certificate मागण्यास भाग पाडते. तो certificate उपलब्ध नसेल किंवा तुमच्या नियंत्रणातील certificate authority (CA) ने जारी केलेला नसेल, तर nginx request नाकारते. ही तपासणी TLS (transport layer security) handshake मध्येच होते. त्यामुळे वैध client certificate नसलेला caller तुमच्या application पर्यंत पोहोचतच नाही. याचा मुख्य फायदा असा आहे: admin panel किंवा metrics endpoint public internet वर login page शिवाय ठेवता येतो आणि bot ला अंदाज लावण्यासाठी काहीही उपलब्ध राहत नाही.

ही रचना लहान आहे. openssl वापरून तयार केलेली एक private CA, प्रत्येक व्यक्तीसाठी एक certificate आणि nginx server block मधील तीन directives एवढेच आवश्यक आहे. ही रचना एक वर्ष सुरळीत चालेल की नाही हे operational कामावर अवलंबून असते. त्यामुळे या मार्गदर्शकात certificates ची validity, revocation, प्रत्येक व्यक्तीसाठी स्वतंत्र certificates आणि client नाकारला गेल्यावर कारण कोणालाही दिसत नसताना काय करावे यावर मुख्यतः चर्चा केली आहे.

दोन स्वतंत्र chain, एक नाही

mTLS रचनेत दोन certificate chain असतात आणि त्यांचा एकमेकांशी काहीही संबंध नसतो. त्या एकत्र करणे ही जवळजवळ प्रत्येकजण करणारी पहिली चूक आहे.

पहिली chain server ची असते. तुमचा VPS admin.example.com साठी certificate सादर करतो. हा certificate Let's Encrypt सारख्या सार्वजनिक CA ने जारी केलेला असतो. Browser हा certificate operating system सोबत येणाऱ्या root store विरुद्ध तपासतो. mTLS मुळे या भागात कोणताही बदल होत नाही. certbot ने आज तुमच्यासाठी हा certificate जारी केला असेल, तर तो जसा आहे तसाच ठेवा: certbot वापरून nginx साठी Let's Encrypt certificate जारी करणे पहा.

दुसरी chain client ची असते. तुम्ही स्वतःचा एक लहान CA तयार करता. प्रवेश आवश्यक असलेल्या प्रत्येक व्यक्तीसाठी एक certificate sign करता. Client तपासताना nginx ने त्या CA वर आणि केवळ त्याच CA वर विश्वास ठेवावा, असे तुम्ही सांगता. कोणत्याही सार्वजनिक root store ला तुमचा CA माहीत नसतो आणि त्याची गरजही नसते. या CA वर विश्वास ठेवणे आवश्यक असलेला एकमेव पक्ष म्हणजे nginx, आणि तो ssl_client_certificate file द्वारे हे करतो.

म्हणून ssl_client_certificate nginx सादर करत असलेल्या certificate वर कधीही परिणाम करत नाही. तसेच Let's Encrypt chain मुळे कोणते clients प्रवेश करू शकतात यावरही परिणाम होत नाही. ssl_client_certificate ला fullchain.pem कडे निर्देशित केल्याने दिसते तसे कार्य होत नाही. त्या directive मध्ये client certificate कोणत्या issuers कडून आलेला असू शकतो, हे नमूद केले जाते. हे connection च्या दुसऱ्या टोकाशी संबंधित आहे. स्वतःच्या outbound कामांसाठी server ने तुमच्या CA वर विश्वास ठेवणे हे स्वतंत्र काम आहे. त्यासाठी Ubuntu trust store मध्ये तुमचा स्वतःचा CA जोडणे पहा. Client ची पडताळणी करताना nginx system trust store वाचत नाही.

openssl वापरून तुमचा स्वतःचा client CA तयार करा

CA web server व्यतिरिक्त इतर ठिकाणी तयार करा. nginx ला फक्त CA चे सार्वजनिक प्रमाणपत्र आवश्यक असते. CA ची private key नवीन client certificates वर स्वाक्षरी करते. त्यामुळे ती internet-facing मशीनवर ठेवल्यास, एकदा server breached झाला की हल्लेखोर स्वतःसाठी हवे तितके वैध clients तयार करू शकतो.

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 आणि crlnumber हा CA database आहे. openssl ca त्यांच्याशिवाय चालत नाही. पुढे revocation शक्य होण्याचे कारणही हेच आहे. Revocation list मध्ये serial numbers असतात. त्यामुळे कोणता serial number कोणाला दिला हे CA ने लक्षात ठेवणे आवश्यक असते.

~/client-ca/openssl.cnf लिहा. dir मध्ये त्या directory चा वास्तविक path सेट करा, कारण openssl ca हे ~ expand करत नाही.

[ 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

आता CA key आणि त्याचे self-signed certificate तयार करा:

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 key वर passphrase सेट करते. त्यामुळे प्रत्येक signing run वेळी ती विचारली जाते. हेच तिचे उद्दिष्ट आहे. तयार केलेली माहिती तपासा:

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

subject तुमच्या CA चे असावे आणि validity दहा वर्षांची असावी. extension line मध्ये CA:TRUE, pathlen:0 असे दिसले पाहिजे. pathlen:0 याचा अर्थ हा CA end certificates वर स्वाक्षरी करू शकतो, पण दुसऱ्या CA वर स्वाक्षरी करू शकत नाही. त्यामुळे chain अगदी एका स्तराची राहते आणि ssl_verify_depth मध्ये बदल करण्याची आवश्यकता राहत नाही.

प्रत्येक व्यक्तीसाठी client certificate जारी करा

प्रत्येक व्यक्तीसाठी एक certificate वापरा. संपूर्ण team साठी एक shared certificate कधीही वापरू नका, कारण सर्वांना access पासून वंचित न करता shared certificate revoke करता येत नाही. तसेच कोणत्या व्यक्तीने विनंती केली हे त्यातून समजत नाही.

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 sign करण्यापूर्वीचा certificate दाखवते, CA passphrase विचारते, confirmation साठी दोनदा विचारते आणि त्यानंतर index.txt मध्ये एक line जोडते. Script मध्ये वापरताना -batch जोडा. client_ext section महत्त्वाचा आहे, कारण त्यात एक line आहे: extendedKeyUsage = clientAuth. फक्त serverAuth अशी extended key usage listing असलेला certificate client authentication साठी योग्य नसल्याचे मानून reject केला जातो. त्यामुळे उद्देश स्पष्टपणे नमूद करा.

काहीही सुपूर्द करण्यापूर्वी CA विरुद्ध pair पडताळा:

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

यामुळे certs/alice.crt: OK दिसते. याशिवाय कोणतेही output आल्यास certificate आणि CA जुळत नाहीत. कोणतीही nginx configuration ही समस्या सोडवू शकत नाही.

Browser import करू शकेल अशा एका file मध्ये key आणि certificate एकत्र करा:

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

Export करताना password विचारला जातो. त्यामुळे transit दरम्यान file सुरक्षित राहते. File आणि password वेगवेगळ्या channels मधून पाठवा. तसेच लोकांना bare .key ऐवजी .p12 द्या. CA चा समावेश bundle मध्ये करण्यासाठी -certfile ca.crt जोडू शकता. मात्र nginx ला त्याची गरज नाही. nginx कडे आधीच ca.crt असते. त्यामुळे त्या CA ने थेट sign केलेला certificate स्वतंत्रपणे verify होतो.

Ubuntu 24.04 मध्ये उपलब्ध असलेले OpenSSL 3 सध्याच्या encryption सह PKCS#12 files लिहिते. August 2026 पर्यंत वापरात असलेले browsers आणि operating systems हे files वाचू शकतात. एखाद्या जुन्या importer ने file नाकारल्यास -legacy जोडून पुन्हा export करा. त्यामुळे त्या importer ला अपेक्षित असलेल्या जुन्या algorithms चा वापर होतो. तो flag वापरण्यापूर्वी importer ने दिलेला message वाचा.

ssl_client_certificate आणि ssl_verify_client सह nginx कॉन्फिगर करा

CA certificate कॉपी करा. फक्त CA certificateच 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 योग्य आहे. CA certificate ही सार्वजनिक माहिती आहे. CA key तुमच्या workstation वरच ठेवा.

त्यानंतर TLS termination आधीच करणाऱ्या server block मध्ये तीन directives जोडा:

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 चा default आहे. या file मधील CA ने client certificate थेट sign केलेले असणे आवश्यक आहे, असे तो सांगतो. Intermediate जोडल्यासच ही value वाढवा. Handshake दरम्यान nginx ssl_client_certificate मधील subject names client कडेही पाठवतो. त्यामुळे browser ला त्यापैकी कोणते certificate द्यायचे हे कळते. याच कारणामुळे ssl_client_certificate वापरावे. ssl_trusted_certificate त्याच पद्धतीने पडताळणी करते, परंतु कोणतीही list पाठवत नाही.

Ubuntu 24.04 मध्ये nginx 1.24 समाविष्ट आहे. या आवृत्तीत HTTP/2 साठी listen line वर listen 443 ssl http2; वापरले जाते. nginx 1.25.1 आणि त्यानंतरच्या आवृत्त्यांमध्ये हा प्रकार deprecated आहे. तेथे HTTP/2 साठी स्वतंत्र directive http2 on; आहे. कोणताही पर्याय certificate check बदलत नाही.

Reload करा आणि परिणाम वाचा:

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

nginx -t मध्ये syntax is ok आणि test is successful दिसतात. curl call मध्ये कोणतेही certificate नसते. त्यामुळे body No required SSL certificate was sent सह 400 Bad Request परत मिळायला हवे. nginx ने स्वतःच्या gate वर विनंती नाकारली आहे. याचा अर्थ configuration लागू झाले आहे आणि application ला विनंती पाठवलीच गेली नाही. आता योग्य पद्धतीने प्रयत्न करा:

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

यामुळे तुमचे application जे काही serve करते ते परत मिळायला हवे.

सर्व्हर ब्लॉकमध्ये gate का ठेवावे

TLS handshake दरम्यान प्रमाणपत्राची देवाणघेवाण होते. nginx ने request line वाचण्यापूर्वीच ही प्रक्रिया पूर्ण होते. त्यामुळे त्या क्षणी विनंती कोणत्या location मध्ये जाईल, हे nginx ला माहीत नसते. ssl_verify_client on; ला location च्या आत ठेवणे म्हणजे connection च्या मध्यभागी client कडून पुन्हा negotiation करण्याची विनंती करणे होय. TLS 1.3 ने renegotiation काढून टाकले आहे आणि HTTP/2 मध्ये ते निषिद्ध आहे. त्यामुळे सध्याच्या stack वर ही रचना prompt दाखवण्याऐवजी अपयशी ठरते.

Scope स्वतः निश्चित करा. server स्तरावर प्रमाणपत्राची मागणी करा आणि नंतर प्रत्येक 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 मध्ये SUCCESS असते; client ने काहीही पाठवले नसल्यास NONE असते; किंवा कारणासह FAILED: असते. optional वापरल्यास nginx प्रमाणपत्राची मागणी करतो आणि प्रमाणपत्र मिळाल्यासच त्याची पडताळणी करतो. त्यामुळे वरील सार्वजनिक /healthz path कार्यरत ठेवता येतो, तर /metrics बंद ठेवता येते. पाठवलेले प्रमाणपत्र पडताळणीत अपयशी ठरल्यास nginx ते त्याच वेळी नाकारतो. त्याऐवजी अपयशी प्रमाणपत्राची तपासणी स्वतः करायची असल्यास ते optional_no_ca आहे. अशा वेळी SUCCESS व्यतिरिक्त प्रत्येक value नकार म्हणून तुमच्या चाचणीने हाताळली पाहिजे.

nginx यासाठी non-standard status codes वापरतो. error_page च्या मदतीने हे codes पकडता येतात. त्यामुळे नाकारलेल्या visitor ला फक्त 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 म्हणजे client certificate ची पडताळणी अयशस्वी झाली. 496 म्हणजे client ने कोणतेही certificate सादर केले नाही. हे page plain text ठेवा, कारण ते वाचणाऱ्या व्यक्तीकडे session किंवा account नसते.

ब्राउझरमध्ये client certificate कसे स्थापित करावे?

Firefox स्वतःचा certificate store वापरतो: Settings, त्यानंतर Privacy and Security, त्यानंतर View Certificates, त्यानंतर Your Certificates tab, आणि मग Import निवडा. त्यानंतर .p12 निवडा आणि त्याचा password टाइप करा.

Windows आणि macOS वर Chrome आणि Edge operating system store वापरतात. त्यामुळे .p12 file उघडल्यावर system import wizard सुरू होतो. Linux वर Chrome तुमच्या home directory मधील स्वतंत्र NSS (network security services) database वाचतो. त्यामुळे command line tool हा विश्वासार्ह पर्याय आहे:

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

त्यानंतर site लोड करा. browser कोणते certificate पाठवायचे ते विचारतो. Chrome ही निवड browser session संपेपर्यंत लक्षात ठेवतो. पुन्हा विचारले जावे असे वाटत असल्यास browser restart करा. Certificate एका machine वरील एका browser profile मध्येच उपलब्ध असते. त्यामुळे Firefox मध्ये import केलेले certificate Chrome मध्ये दिसत नाही. तसेच दोन्ही certificates तुमच्या phone मध्ये दिसत नाहीत.

curl --cert सह चाचणी

curl वापरून डीबग करा, कारण त्याने केलेल्या कृतींची नोंद तो दाखवतो.

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

प्रमाणपत्र आणि key एकाच PEM file मध्ये जोडून ते --cert alice.pem म्हणून देऊ शकता. key ला passphrase असल्यास curl ती विचारते. --cert alice.pem:passphrase हेदेखील स्वीकारले जाते; मात्र ते तुमच्या shell history मध्ये नोंदले जाते. त्यामुळे prompt वापरा.

nginx वर दोष ठेवण्यापूर्वी दोन तपासण्या करा. प्रथम, प्रमाणपत्र आणि key ही एकमेकांची जोडी असली पाहिजे:

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

दोन समान hashes म्हणजे files एकमेकांशी संबंधित आहेत. दोन वेगवेगळे hashes म्हणजे तुम्ही दोन व्यक्तींच्या files मध्ये गल्लत केली आहे. कोणताही client हे कारण तुमच्यासाठी स्पष्ट करणार नाही.

दुसरे म्हणजे, server ने तुमच्या CA साठी विनंती केली पाहिजे:

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

आउटपुटमध्ये Acceptable client certificate CA names block शोधा आणि त्यात तुमच्या CA चा subject आहे का ते तपासा. हा block पूर्णपणे अनुपस्थित असल्यास, उत्तर देणाऱ्या server block वर nginx प्रमाणपत्राची विनंती करत नाही. याचा अर्थ तुमच्या directives दुसऱ्या server block मध्ये लागू झाल्या आहेत. बहुतेक वेळा तो default server असतो.

अॅप्लिकेशनकडे client CN पाठवणे

प्रमाणपत्रावर विनंती कोणी केली हे नमूद असते. परंतु proxy मागील अॅप्लिकेशनला TLS स्तर दिसत नाही. त्यामुळे nginx ने हे नाव पुढे पाठवणे आवश्यक आहे.

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

$ssl_client_s_dn मध्ये RFC 2253 स्वरूपातील subject distinguished name असतो. तो CN=alice,O=Example Ops सारखा दिसतो. map निर्देश $client_cn मध्ये CN field ठेवतो. CN साधे username ठेवा. या स्वरूपात CN मधील comma escape केला जातो आणि वरील लहान regular expression हे escape हाताळत नाही.

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 caller ने पाठवलेला त्या नावाचा कोणताही header बदलतो. त्यामुळे या location मधून कोणीही X-Client-Cert-CN बनावट तयार करू शकत नाही. हे कायम राहण्यासाठी दोन अटी आवश्यक आहेत. बाहेरील स्तराकडून nginx ला proxy_set_header तेव्हाच वारशाने मिळते, जेव्हा आतील स्तरावर स्वतःची कोणतीही अशी configuration नसते. त्यामुळे दुसऱ्या location मध्ये एक proxy_set_header ओळ असल्यास, त्याच्या वर सेट केलेले सर्व headers, यासह हा header, शांतपणे गमावले जातात. तसेच अॅप्लिकेशन nginx शिवाय उपलब्ध नसले पाहिजे. यासाठी ते 0.0.0.0 ऐवजी 127.0.0.1 शी bind करा. सार्वजनिक port वर चालणारे अॅप्लिकेशन internet वरून आलेला बनावट header थेट वाचेल. Proxy संबंधित भाग nginx reverse proxy configuration चे ओळीनुसार स्पष्टीकरण मध्ये दिला आहे. अॅप्लिकेशनला नावाऐवजी संपूर्ण प्रमाणपत्र हवे असल्यास, $ssl_client_escaped_cert ते URL-encoded करून header मध्ये सुरक्षितपणे पाठवते.

एक client certificate कसे revoke करावे?

एखादी व्यक्ती संस्था सोडते किंवा laptop हरवतो. त्या एकाच certificate चे revocation करा. इतर सर्वांचे काम सुरू राहते. प्रत्येक व्यक्तीसाठी स्वतंत्र certificate देण्यामागचे हेच मुख्य कारण आहे.

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

पहिली command index.txt मधील त्या serial ची स्थिती V वरून R करते. दुसरी command certificate revocation list (CRL) लिहिते. ही revoked serial numbers नमूद करणारी signed file असते. ती deploy करा आणि इतर directives च्या शेजारी ssl_crl /etc/nginx/client-ca.crl; वापरून nginx ला तिच्याकडे निर्देशित करा.

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'

येथे अशी अडचण आहे की संपूर्ण प्रवेशद्वार सर्वांसाठी बंद होऊ शकते. CRL मध्ये nextUpdate date असते. ती default_crl_days ने सेट केली जाते आणि वरील configuration मध्ये तिची किंमत 30 आहे. ही date उलटून गेल्यावर OpenSSL ही list stale मानते आणि CRL has expired असलेल्या प्रत्येक client certificate साठी verification अयशस्वी करते. हा परिणाम फक्त revoked certificate पुरता मर्यादित नसतो. nginx configuration load करताना file वाचतो. त्यामुळे disk वरील नवीन CRL लागू होत नाही, जोपर्यंत reload केले जात नाही. 30 दिवसांच्या window च्या आत, उदाहरणार्थ weekly, CRL पुन्हा तयार करून reload करा. Copy करण्यापूर्वी dates तपासा:

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

कमी users असतील, तर यापेक्षा लहान पर्याय उपलब्ध आहे. CA तुमच्या नियंत्रणात असल्यामुळे nginx serial थेट नाकारू शकतो आणि CRL ची यंत्रणा टाळू शकतो:

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

यासोबत location मध्ये if ($revoked) { return 403; } वापरा. विसरता येईल अशी expiry date यामध्ये नसते. मात्र ही माहिती इतरत्र पाठवली जात नाही. त्यामुळे तुमच्या CA वर विश्वास ठेवणाऱ्या इतर कोणत्याही प्रणालीला याची माहिती नसते. एका application समोर एकच nginx असेल, तर हा प्रामाणिक आणि सोपा पर्याय आहे. एकापेक्षा अधिक प्रवेशद्वारे झाल्यावर CRL वापरा.

क्लायंट प्रमाणपत्रांची वैधता किती काळ ठेवावी?

क्लायंट प्रमाणपत्रे एक वर्षासाठी वैध ठेवा. पुनर्प्रमाणीकरणाचे काम सहन होत असल्यास त्यापेक्षा कमी कालावधी ठेवा. येथे expiry ही शांतपणे उद्भवणारी समस्या असते, कारण त्याबद्दल धारकाला आधी कोणतीही सूचना मिळत नाही. एके सकाळी ते panel उघडतात, nginx connection नाकारतो आणि browser हा नकार स्वतःच्या शब्दांत दाखवतो. त्या शब्दांत expired हा शब्द क्वचितच असतो. CA दहा वर्षांसाठी वैध ठेवा आणि त्याची expiry date तुम्ही प्रत्यक्ष पाहाल अशा ठिकाणी नोंदवा. CA certificate ची expiry झाल्यावर त्याखालील प्रत्येक certificate त्याच दिवशी verification मध्ये अपयशी ठरतो.

या समस्येवर आधीपासून लक्ष ठेवण्यासाठी दोन commands पुरेसे आहेत:

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

index.txt मधील पहिला column status दाखवतो: वैध प्रमाणपत्रासाठी V, revoked प्रमाणपत्रासाठी R आणि expired प्रमाणपत्रासाठी E. दुसरा column YYMMDDHHMMSSZ format मधील expiry दाखवतो आणि चौथा column serial दाखवतो. कोणाकडे कोणते प्रमाणपत्र आहे याची हीच तुमची एकमेव नोंद असते. त्यामुळे तिचा backup CA key सोबत घ्या आणि दोन्हींना secrets म्हणून हाताळा.

Renewal म्हणजे नवीन certificate तयार करणे; ते विद्यमान certificate ची मुदतवाढ नसते. नवीन key आणि CSR (certificate signing request) तयार करा, त्यावर स्वाक्षरी करा, ते संबंधित व्यक्तीकडे द्या आणि नवीन certificate व्यवस्थित काम करत असल्याची पुष्टी मिळाल्यावर जुने certificate revoke करा.

mTLS कशापासून संरक्षण करते आणि कशापासून करत नाही

mTLS मुळे अनधिकृत प्रवेशाचा मार्ग बंद होतो. तुमचा hostname शोधणाऱ्या scanner ला handshake दरम्यान नकार मिळतो. त्यामुळे तो कधीही HTTP request पाठवू शकत नाही, login form पाहू शकत नाही आणि चोरीला गेलेला password वापरून प्रयत्न करू शकत नाही. त्यामुळे credential stuffing करण्यासाठी काहीही उरत नाही. प्रमाणपत्र नसलेल्या कोणालाही अॅप्लिकेशनच्या login flow मधील vulnerability पर्यंत पोहोचता येत नाही. तसेच लोक chat मध्ये कॉपी करून पाठवतात तो shared secret देखील काढून टाकता येतो. कारण private key ही चुकून कॉपी करणे अवघड असलेली file असते.

मात्र compromised client पासून mTLS संरक्षण देत नाही. Laptop वरील malware कडे key file असते. मालकाने passphrase टाइप करताच तो malware तो passphrase देखील मिळवतो. Server च्या दृष्टीने तो attacker वैध user सारखाच दिसतो. कारण certificate मुळे एखाद्या व्यक्तीची उपस्थिती नव्हे, तर एखादी file ताब्यात असल्याचे सिद्ध होते. .p12 password आणि full disk encryption यांचे महत्त्व तरीही कायम राहते.

mTLS हे authorization देखील नाही. प्रत्येक वैध certificate ला त्या server block ने उपलब्ध करून दिलेल्या सर्व गोष्टींवर प्रवेश मिळतो, जोपर्यंत तुम्ही $client_cn तपासत नाही आणि त्या value नुसार कृती करत नाही. Default नुसार दोन certificate holders ना समान access असतो.

हे संरक्षण फक्त nginx मधून जाणाऱ्या मार्गाला मिळते. अॅप्लिकेशनने public port वरही listening केले असेल, तर त्याच्यासमोर mTLS लावणे निरुपयोगी आहे. अॅपला 127.0.0.1 वर bind करा आणि त्याचा port firewall मध्ये बंद ठेवा. त्याच server मध्ये प्रवेश करण्याचा दुसरा मार्ग SSH आहे. त्याकडेही तेवढेच लक्ष द्या. याबद्दल तुमच्या VPS वरील SSH access hardening मध्ये माहिती दिली आहे.

शेवटची एक मर्यादा आहे. mTLS सुरू केल्याच्या दिवशी ती लगेच जाणवते. Certificate सादर करू न शकणारी कोणतीही गोष्ट काम करणे थांबवते. यात uptime monitor, payment provider कडून येणारा webhook, RSS reader किंवा वापरता येईल असा certificate store नसलेले mobile app यांचा समावेश होतो. ssl_verify_client on सेट करण्यापूर्वी या घटकांबाबत निर्णय घ्या. कारण त्यांचे अपयश पूर्ण असते आणि त्यांच्या बाजूने कोणताही error दिसत नाही.

क्लायंट नाकारला गेल्यावर तो काय नोंदवतो ते वाचा

नाकारलेल्या क्लायंटने दाखवलेला संदेश browser, curl ची version आणि त्याखालील TLS library यांवर अवलंबून असतो. त्यामुळे दुसरीकडे लिहिलेल्या संदेशाशी तो जुळवण्याऐवजी तुमचा स्वतःचा क्लायंट काय छापतो ते वाचा. उपयुक्त तपशील server वर असतो.

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

नाकारलेल्या certificate मुळे client SSL certificate verify error असलेली आणि त्यानंतर OpenSSL ने दिलेले कारण नमूद करणारी एक ओळ दिसते. कृतीसाठी महत्त्वाचे तथ्य हेच कारण असते. सामान्यतः यापैकी एखादे कारण असते. Certificate ssl_client_certificate मध्ये नमूद केलेल्या file पेक्षा वेगळ्या CA कडून आलेले असते. Certificate ची validity dates संपलेल्या असतात. Server वरील CRL ची nextUpdate उलटून गेलेली असते. त्यामुळे आता एका client ऐवजी प्रत्येक client अपयशी ठरतो.

Browser कधीही certificate पाठवत नसेल, तर समस्या verification च्या आधीच्या टप्प्यात असते. Handshake दरम्यान nginx स्वीकार्य issuer names पाठवतो. Browser ला त्याच्या store मध्ये जुळणारे काहीही सापडत नाही. त्यामुळे त्याच्याकडे तुम्हाला पाठवण्यासाठी certificate नसते. तुम्ही प्रत्यक्ष browsing करत असलेल्या profile मध्ये .p12 पुन्हा import करा.

आणखी एका परिस्थितीचा उल्लेख करणे आवश्यक आहे. तुम्ही CA ने sign केलेल्या certificate ऐवजी स्वतंत्र self-signed client certificate वापरून चाचणी केली असेल, तर verification यशस्वी होऊ शकत नाही. nginx signature ची पडताळणी CA file विरुद्ध करतो आणि self-signed certificate त्यात नसते. Certificate तयार करण्याची पद्धत Ubuntu वर self-signed certificate तयार करणे याप्रमाणेच आहे. mTLS साठी फक्त अतिरिक्त टप्पा आवश्यक असतो: तुमच्या CA कडून त्यावर sign करून घेणे.

FAQ

mTLS वापरल्यास मला अजूनही Let's Encrypt प्रमाणपत्राची गरज आहे का?

होय. ही दोन प्रमाणपत्रे परस्परांशी संबंधित नाहीत. hostname वर browser ला विश्वास ठेवता यावा म्हणून server स्वतःचे प्रमाणपत्र सादर करतो. ते प्रमाणपत्र browser ला आधीपासून माहीत असलेल्या CA कडून घेतलेले असणे आवश्यक आहे. तुमचा client CA हा स्वतंत्र private chain आहे. कोण कनेक्ट होत आहे हे तपासण्यासाठीच त्याचा वापर केला जातो. ssl_client_certificate सेट केल्याने nginx सादर करत असलेल्या प्रमाणपत्रात कोणताही बदल होत नाही. त्याने तुमच्या Let's Encrypt chain कडे निर्देश करू नये.

माझा browser प्रमाणपत्र निवडण्यास कधीच का विचारत नाही?

handshake दरम्यान nginx स्वीकार्य issuers ची यादी पाठवतो. ही यादी ssl_client_certificate मधील file वरून तयार होते. ज्या प्रमाणपत्राचा issuer त्या यादीत आहे, अशीच प्रमाणपत्रे browser दाखवतो. त्यामुळे prompt दिसत नसल्यास browser मध्ये तुमच्या CA चे काहीही नाही. प्रमाणपत्र वेगळ्या browser profile मध्ये import झाले असू शकते किंवा server वर install केलेल्या CA पेक्षा वेगळ्या CA ने प्रमाणपत्र sign केलेले असू शकते. openssl s_client -connect admin.example.com:443 चालवा आणि output मध्ये स्वीकार्य client certificate CA names शोधा. त्यातून server प्रत्यक्षात कोणत्या CA ची मागणी करत आहे हे दिसेल.

मी केवळ एका URL वर client certificate अनिवार्य करू शकतो का?

location मध्ये ssl_verify_client on वापरून तसे करता येत नाही. handshake दरम्यान प्रमाणपत्रांची देवाणघेवाण होते. त्या वेळी nginx ला request path माहीत नसतो. हे टाळण्यासाठी आवश्यक असलेले renegotiation TLS 1.3 मध्ये काढून टाकले आहे आणि HTTP/2 मध्ये प्रतिबंधित आहे. server block मध्ये ssl_verify_client optional; सेट करा. त्यानंतर प्रत्येक संरक्षित location मध्ये $ssl_client_verify तपासा आणि ते SUCCESS नसल्यास 403 परत करा.

एका व्यक्तीचा access मी कसा रद्द करू?

openssl ca -revoke वापरून त्या प्रमाणपत्राचे revocation करा. openssl ca -gencrl वापरून यादी पुन्हा तयार करा. ती server वर copy करा आणि nginx reload करा, जेणेकरून ते नवीन file वाचेल. इतर सर्वांवर याचा परिणाम होत नाही. मात्र प्रत्येक व्यक्तीकडे स्वतंत्र प्रमाणपत्र असणे आवश्यक आहे; shared प्रमाणपत्र असल्यास हे शक्य होत नाही. CRL ची nextUpdate date तपासत राहा. CRL expired झाल्यास revoked clients पुरतेच नव्हे, तर प्रत्येक client साठी verification अयशस्वी होते.

mTLS login page ची जागा घेते का?

पोहच नियंत्रित करण्यासाठी, होय. प्रमाणपत्राशिवाय application पर्यंत काहीही पोहोचत नाही. त्यामुळे हल्ला करण्यासाठी form राहत नाही आणि अंदाजाने password शोधता येत नाही. मात्र application मधील identity साठी, नाही. प्रमाणपत्रावरून caller कडे key file आहे हे सिद्ध होते; त्यामुळे चोरीला गेलेला laptop असलेला वापरकर्ता वैध user ठरतो. CN upstream कडे पाठवा. Application मध्ये आधीपासून असलेली accounts आणि permissions कायम ठेवा. प्रमाणपत्राला त्या accounts आणि permissions समोरील gate म्हणून हाताळा.

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