詳解Nginx SSL快速雙向認(rèn)證配置(腳本)
目前遇到一個(gè)項(xiàng)目有安全性要求,要求只有個(gè)別用戶有權(quán)限訪問。本著能用配置解決就絕不用代碼解決的原則,在Nginx上做一下限制和修改即可。
這種需求其實(shí)實(shí)現(xiàn)方式很多,經(jīng)過綜合評估考慮,覺得SSL雙向認(rèn)證方案對用戶使用最簡單,遂決定用此方案。
注: 本方案在Ubuntu Server 16.04 LTS實(shí)施,其他操作系統(tǒng)請酌情修改
SSL雙向認(rèn)證
絕大多數(shù)SSL應(yīng)用都以單向認(rèn)證為主,即客戶端只要信任服務(wù)端,就可以使用服務(wù)端的公鑰加密后向服務(wù)端發(fā)起請求,由服務(wù)端的私鑰解密之后獲得請求數(shù)據(jù)。
如果這個(gè)過程反過來,讓服務(wù)端信任客戶端,服務(wù)端使用客戶端的公鑰加密之后將數(shù)據(jù)返回給客戶端,其實(shí)也是可以做到的,原理和實(shí)現(xiàn)跟單向認(rèn)證都差不多。
服務(wù)端信任客戶端的操作往往也會(huì)伴隨著客戶端認(rèn)證服務(wù)端的過程,所以讓服務(wù)端信任客戶端的SSL認(rèn)證方式往往也被稱為SSL雙向認(rèn)證,并且要配置SSL雙向認(rèn)證必須先開啟服務(wù)端SSL,先配置客戶端信任服務(wù)端。
Nginx的SSL雙向認(rèn)證配置
第一步 開啟https訪問
根據(jù)理論知識(shí),我們必須先開啟Nginx的SSL配置,即啟用https。這個(gè)過程較為簡單,目前有l(wèi)et's encrypt這種免費(fèi)的證書方案,再也不用發(fā)愁自己搭建CA自簽了。申請免費(fèi)證書的過程略過,直接貼啟用https的配置:
server { listen 80; listen 443 ssl http2; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # 只有Nginx >= 1.13.0 版本才支持TLSv1.3協(xié)議 # ssl_protocols TLSv1.3; # Nginx低于1.13.0版本用這個(gè)配置 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_dhparam dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 ssl_session_timeout 10m; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; # Requires nginx >= 1.5.9 ssl_stapling on; # Requires nginx >= 1.3.7 ssl_stapling_verify on; # Requires nginx => 1.3.7 resolver 223.5.5.5 114.114.114.114 valid=300s; resolver_timeout 5s; # 啟用HSTS的配置,如果你的域名下還有非標(biāo)準(zhǔn)端口訪問的http應(yīng)用,請勿啟用HSTS # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; # 下面這個(gè)配置會(huì)拒絕Frame標(biāo)簽內(nèi)容,請確認(rèn)你的網(wǎng)站沒有frame / iframe add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # 為了let's encrypt續(xù)期用,不用let's encrypt不需要這個(gè)location location /.well-known { root /usr/share/nginx/html; } ... SNIP ... # 強(qiáng)制http跳轉(zhuǎn)為https if ($scheme != "https") { return 301 https://$http_host$request_uri; } }
以上那一大堆ssl的配置參考來自于: https://cipherli.st/ 加強(qiáng)SSL的安全性配置
特別注意最后的強(qiáng)制https跳轉(zhuǎn),我們的目的是SSL雙向認(rèn)證,不走h(yuǎn)ttps無任何意義,所以必須強(qiáng)制跳轉(zhuǎn)https。
第二步 生成客戶端證書并簽證(腳本)
這個(gè)過程詳細(xì)描述的文章太多了,這里就不啰嗦介紹openssl和簽證過程了,本篇內(nèi)容是快速生成雙向認(rèn)證配置的證書,所以直接貼腳本就行了,命令都是參考互聯(lián)網(wǎng)上各種openssl雙向配置文檔,在此基礎(chǔ)之上進(jìn)行了命令上的簡化與非交互式的支持。
整個(gè)目錄結(jié)構(gòu)如圖:
# tree /etc/nginx/ssl_certs/ /etc/nginx/ssl_certs/ ├── create_ca_cert.sh ├── create_client_cert.sh ├── revoke_cert.sh 0 directories, 3 files
自行創(chuàng)建/etc/nginx/ssl_certs/
,放入三個(gè)腳本,分別用于生成CA證書以及CA目錄(create_ca_cert.sh
腳本的作用,只有第一次需要運(yùn)行),創(chuàng)建客戶端證書,并用CA證書簽證(create_client_cert.sh
腳本的作用,必須先生成CA證書),revoke_cert.sh
腳本用于吊銷證書,需要收回權(quán)限的時(shí)候可以使用。
每個(gè)腳本內(nèi)容如下:
create_ca_cert.sh
#!/bin/bash -e # 創(chuàng)建CA根證書 # 非交互式方式創(chuàng)建以下內(nèi)容: # 國家名(2個(gè)字母的代號) C=CN # 省 ST=Shannxi # 市 L=Xian # 公司名 O=My Company # 組織或部門名 OU=技術(shù)部 # 服務(wù)器FQDN或頒發(fā)者名 CN=www.example.com # 郵箱地址 emailAddress=admin@example.com mkdir -p ./demoCA/{private,newcerts} touch ./demoCA/index.txt [ ! -f ./demoCA/seria ] && echo 01 > ./demoCA/serial [ ! -f ./demoCA/crlnumber ] && echo 01 > ./demoCA/crlnumber [ ! -f ./demoCA/cacert.pem ] && openssl req -utf8 -new -x509 -days 36500 -newkey rsa:2048 -nodes -keyout ./demoCA/private/cakey.pem -out ./demoCA/cacert.pem -subj "/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${emailAddress}" [ ! -f ./demoCA/private/ca.crl ] && openssl ca -crldays 36500 -gencrl -out "./demoCA/private/ca.crl"
create_client_cert.sh
#!/bin/bash -e show_help() { echo "$0 [-h|-?|--help] [--ou ou] [--cn cn] [--email email]" echo "-h|-?|--help 顯示幫助" echo "--ou 設(shè)置組織或部門名,如: 技術(shù)部" echo "--cn 設(shè)置FQDN或所有者名,如: 馮宇" echo "--email 設(shè)置FQDN或所有者郵件,如: fengyu@example.com" } while [[ $# -gt 0 ]] do case $1 in -h|-\?|--help) show_help exit 0 ;; --ou) OU="${2}" shift ;; --cn) CN="${2}" shift ;; --email) emailAddress="${2}" shift ;; --) shift break ;; *) echo -e "Error: $0 invalid option '$1'\nTry '$0 --help' for more information.\n" >&2 exit 1 ;; esac shift done # 創(chuàng)建客戶端證書 # 非交互式方式創(chuàng)建以下內(nèi)容: # 國家名(2個(gè)字母的代號) C=CN # 省 ST=Shannxi # 市 L=Xian # 公司名 O=My Company # 組織或部門名 OU=${OU:-測試部門} # 服務(wù)器FQDN或授予者名 CN=${CN:-demo} # 郵箱地址 emailAddress=${emailAddress:-demo@example.com} mkdir -p "${CN}" [ ! -f "${CN}/${CN}.key" ] && openssl req -utf8 -nodes -newkey rsa:2048 -keyout "${CN}/${CN}.key" -new -days 36500 -out "${CN}/${CN}.csr" -subj "/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${emailAddress}" [ ! -f "${CN}/${CN}.crt" ] && openssl ca -utf8 -batch -days 36500 -in "${CN}/${CN}.csr" -out "${CN}/${CN}.crt" [ ! -f "${CN}/${CN}.p12" ] && openssl pkcs12 -export -clcerts -CApath ./demoCA/ -inkey "${CN}/${CN}.key" -in "${CN}/${CN}.crt" -certfile "./demoCA/cacert.pem" -passout pass: -out "${CN}/${CN}.p12"
revoke_cert.sh
#!/bin/bash -e # 吊銷一個(gè)簽證過的證書 openssl ca -revoke "${1}/${1}.crt" openssl ca -gencrl -out "./demoCA/private/ca.crl"
簡單分析一波腳本,首先是創(chuàng)建CA,對于Ubuntu系統(tǒng)來說,/etc/ssl/openssl.cnf
配置中默認(rèn)的CA路徑就是./demoCA
,為了不改動(dòng)默認(rèn)配置,直接按照默認(rèn)配置的內(nèi)容創(chuàng)建這些目錄和文件即可。還有就是openssl子命令非常多,但是也和git一樣,可以合并命令,比如用一條命令同時(shí)生成私鑰和簽證請求openssl req -nodes -newkey rsa:2048 -keyout client.key -new -out client.csr
,在req
的同時(shí)就做了genrsa
。由于創(chuàng)建CA腳本只是第一次運(yùn)行需要,因此把證書配置直接寫死在腳本中就完事了。
接下來是創(chuàng)建客戶端證書,為了簡化用戶的使用,在服務(wù)端幫助用戶生成證書并簽證,把簽證過的證書下發(fā)給用戶就可以了。由于用戶可能是不同部門,不同姓名,不同郵件地址,因此將這三個(gè)參數(shù)外部化,做一下參數(shù)解析,加上友好的命令行提示防止遺忘。這個(gè)腳本特別注意最后一行,會(huì)生成一個(gè)PKCS12
格式的證書。openssl
默認(rèn)產(chǎn)生的證書格式都是PEM的,會(huì)將公鑰和私鑰分開,但是瀏覽器導(dǎo)入的時(shí)候需要將這些內(nèi)容合并起來形成證書鏈,所以需要將簽證過的證書和私鑰文件合并成一個(gè)PKCS12
格式的證書,直接將這個(gè).p12
格式的證書交給用戶就可以了。
最后是吊銷證書了,當(dāng)希望收回某個(gè)用戶的訪問權(quán)限時(shí),直接運(yùn)行這個(gè)腳本跟上目錄名就可以了。
接下來運(yùn)行創(chuàng)建CA的腳本:
./create_ca_cert.sh
Generating a 2048 bit RSA private key .......................+++ ........................................................................................................+++ writing new private key to './demoCA/private/cakey.pem' ----- Using configuration from /usr/ssl/openssl.cnf
此時(shí)產(chǎn)生的./demoCA目錄結(jié)構(gòu)如下:
demoCA/ ├── cacert.pem ├── crlnumber ├── crlnumber.old ├── index.txt ├── newcerts ├── private │ ├── ca.crl │ └── cakey.pem └── serial 2 directories, 7 files
此時(shí)就可以配置nginx了,在上面單向ssl的配置中,追加以下配置:
ssl_client_certificate ssl_certs/demoCA/cacert.pem; ssl_crl ssl_certs/demoCA/private/ca.crl; ssl_verify_client on;
ssl_client_certificate
就是客戶端證書的CA證書了,代表此CA簽發(fā)的證書都是可信的,ssl_verify_client on;
代表強(qiáng)制啟用客戶端認(rèn)證,非法客戶端(無證書,證書不可信)都會(huì)返回400錯(cuò)。
特別注意ssl_crl
這個(gè)配置,代表Nginx會(huì)讀取一個(gè)CRL(Certificate Revoke List)文件,之前說過,可能會(huì)有收回用戶權(quán)限的需求,因此我們必須有吊銷證書的功能,產(chǎn)生一個(gè)CRL文件讓Nginx知道哪些證書被吊銷了即可。
注意: Nginx配置都是靜態(tài)的,讀取配置文件之后都會(huì)加載到內(nèi)存中,即使文件內(nèi)容變化也不會(huì)重新讀取。因此當(dāng)CRL文件發(fā)生變更之后,Nginx并不能意識(shí)到有新的證書被吊銷了,所以必須使用
reload
指令讓Nginx重新讀取配置文件:service nginx reload
或nginx -s reload
此時(shí)重啟Nginx服務(wù),就可以完成SSL雙向認(rèn)證配置了。
我們簽發(fā)一個(gè)證書看看:
./create_client_cert.sh --ou 財(cái)務(wù)部 --cn 財(cái)務(wù)經(jīng)理 --email cy@example.com Generating a 2048 bit RSA private key ................................+++ .............................................................................+++ writing new private key to '財(cái)務(wù)經(jīng)理/財(cái)務(wù)經(jīng)理.key' ----- Using configuration from /usr/ssl/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 1 (0x1) Validity Not Before: Jun 14 16:03:46 2018 GMT Not After : May 21 16:03:46 2118 GMT Subject: countryName = CN stateOrProvinceName = Shannxi organizationName = My Company organizationalUnitName = \U8D22\U52A1\U90E8 commonName = \U8D22\U52A1\U7ECF\U7406 emailAddress = cy@example.com X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: B5:91:0B:1F:FC:25:3B:2A:F9:EF:39:39:51:E3:1F:64:78:8A:C3:75 X509v3 Authority Key Identifier: keyid:86:55:76:15:A3:F5:58:CB:8F:39:A3:56:8E:FF:18:97:AE:27:60:0F Certificate is to be certified until May 21 16:03:46 2118 GMT (36500 days) Write out database with 1 new entries Data Base Updated tree 財(cái)務(wù)經(jīng)理/ 財(cái)務(wù)經(jīng)理/ ├── 財(cái)務(wù)經(jīng)理.crt ├── 財(cái)務(wù)經(jīng)理.csr ├── 財(cái)務(wù)經(jīng)理.key └── 財(cái)務(wù)經(jīng)理.p12 0 directories, 4 files
這個(gè)腳本生成了私鑰文件key
,簽證請求文件csr
,經(jīng)過CA簽證后的證書文件crt
(里面沒有私鑰),以及將crt文件和key進(jìn)行bundle之后的PKCS12
格式的證書文件p12
,將p12
文件下載到本地,雙擊一路Next導(dǎo)入證書即可。
注: 由于CA的證書文件不會(huì)發(fā)生變化,因此簽證新的客戶端證書不需要restart或reload nginx
這次打開我們的網(wǎng)站https://www.example.com,瀏覽器就會(huì)提示我們選擇一個(gè)已有的客戶端證書進(jìn)行認(rèn)證了,沒問題就可以看到網(wǎng)站內(nèi)容了
注: 每次導(dǎo)入新的證書之后,必須重啟瀏覽器才能提示使用新的證書文件
按照這種方式,有多少人需要授權(quán),就可以用這個(gè)腳本簽發(fā)多少個(gè)這樣的證書,用戶將p12
證書導(dǎo)入本地就可以正常訪問網(wǎng)站了。
當(dāng)我們需要收回某人的權(quán)限的時(shí)候(比如離職了),我們需要吊銷他的證書:
./revoke_cert.sh 財(cái)務(wù)經(jīng)理
Using configuration from /usr/ssl/openssl.cnf Revoking Certificate 01. Data Base Updated Using configuration from /usr/ssl/openssl.cnf service nginx reload
這個(gè)腳本會(huì)自動(dòng)吊銷他的簽證文件crt
,并且自動(dòng)更新CRL
文件。特別注意需要reload或restart nginx才能讓nginx重新加載CRL。這樣被吊銷的證書將無法訪問網(wǎng)站了。
小結(jié)
本文我們通過Nginx配置SSL雙向認(rèn)證實(shí)現(xiàn)對客戶端的加密認(rèn)證,我們使用了簡易的腳本幫助我們快速生成各種證書與簽證,免除記憶繁瑣openssl命令行,簡化使用。
當(dāng)然這只是一個(gè)最小可用集,當(dāng)規(guī)模比較大的時(shí)候可能需要做很多改進(jìn),比如加入CA的web ui,直接可以操作簽證和吊銷證書,并且可以自動(dòng)重啟nginx。
再比如CRL這種靜態(tài)配置文件不適合你的場景,希望的動(dòng)態(tài)更新吊銷證書列表,那么可以考慮OCSP方案,這個(gè)Nginx也是支持的,通過ssl_stapling_responder配置指定一個(gè)OCSP地址,這樣將不需要每次吊銷證書的時(shí)候都去重啟nginx了,openssl也提供了ocsp服務(wù)端的功能,這里就不贅述了,可以自行查找相關(guān)資料。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- nginx配置https的雙向認(rèn)證方式
- https如何通過nginx完成雙向認(rèn)證轉(zhuǎn)發(fā)
- Nginx配置Https安全認(rèn)證的實(shí)現(xiàn)
- Nginx+SSL實(shí)現(xiàn)雙向認(rèn)證的示例代碼
- nginx環(huán)境下配置ssl加密(單雙向認(rèn)證、部分https)
- Nginx中配置用戶服務(wù)器訪問認(rèn)證的方法示例
- 使用Lua編寫Nginx服務(wù)器的認(rèn)證模塊的方法
- Nginx用戶認(rèn)證配置方法詳解(域名/目錄)
- 使用LDAP實(shí)現(xiàn)Nginx用戶認(rèn)證的示例
相關(guān)文章
HTTP 499 狀態(tài)碼 nginx下 499錯(cuò)誤的解決辦法
HTTP狀態(tài)碼出現(xiàn)499錯(cuò)誤有多種情況,499錯(cuò)誤是什么?Nginx 499錯(cuò)誤的原因及解決方法,下面跟著腳本之家小編一起學(xué)習(xí)吧2016-06-06nginx 如何實(shí)現(xiàn)if嵌套的方法示例
這篇文章主要介紹了nginx 如何實(shí)現(xiàn)if嵌套的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11nginx并發(fā)數(shù)限制limit_conn基本語法
這篇文章主要為大家介紹了nginx并發(fā)數(shù)限制limit_conn基本語法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Nginx IP封禁及自動(dòng)封禁IP的實(shí)現(xiàn)
本文主要介紹了Nginx IP封禁及自動(dòng)封禁IP的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01nginx實(shí)現(xiàn)請求轉(zhuǎn)發(fā)
本文給大家分享的是使用nginx實(shí)現(xiàn)代理(請求轉(zhuǎn)發(fā))的教程及簡單示例,非常實(shí)用,有需要的小伙伴可以參考下2017-07-07