Nginx SSL快速雙向認(rèn)證配置(腳本)

原文鏈接

目前遇到一個項目有安全性要求欲逃,要求只有個別用戶有權(quán)限訪問。本著能用配置解決就絕不用代碼解決的原則,在Nginx上做一下限制和修改即可翁巍。

這種需求其實實現(xiàn)方式很多,經(jīng)過綜合評估考慮休雌,覺得SSL雙向認(rèn)證方案對用戶使用最簡單灶壶,遂決定用此方案。

: 本方案在Ubuntu Server 16.04 LTS實施杈曲,其他操作系統(tǒng)請酌情修改

SSL雙向認(rèn)證

絕大多數(shù)SSL應(yīng)用都以單向認(rèn)證為主驰凛,即客戶端只要信任服務(wù)端胸懈,就可以使用服務(wù)端的公鑰加密后向服務(wù)端發(fā)起請求,由服務(wù)端的私鑰解密之后獲得請求數(shù)據(jù)恰响。

如果這個過程反過來趣钱,讓服務(wù)端信任客戶端,服務(wù)端使用客戶端的公鑰加密之后將數(shù)據(jù)返回給客戶端渔隶,其實也是可以做到的羔挡,原理和實現(xiàn)跟單向認(rèn)證都差不多洁奈。

服務(wù)端信任客戶端的操作往往也會伴隨著客戶端認(rèn)證服務(wù)端的過程间唉,所以讓服務(wù)端信任客戶端的SSL認(rèn)證方式往往也被稱為SSL雙向認(rèn)證,并且要配置SSL雙向認(rèn)證必須先開啟服務(wù)端SSL利术,先配置客戶端信任服務(wù)端呈野。

Nginx的SSL雙向認(rèn)證配置

第一步 開啟https訪問

根據(jù)理論知識,我們必須先開啟Nginx的SSL配置印叁,即啟用https被冒。這個過程較為簡單,目前有l(wèi)et's encrypt這種免費的證書方案轮蜕,再也不用發(fā)愁自己搭建CA自簽了昨悼。申請免費證書的過程略過,直接貼啟用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版本用這個配置
  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";
  # 下面這個配置會拒絕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不需要這個location
  location /.well-known {
    root /usr/share/nginx/html;
  }

   ... SNIP ...

  # 強制http跳轉(zhuǎn)為https
  if ($scheme != "https") {
    return 301 https://$http_host$request_uri;
  }
}

以上那一大堆ssl的配置參考來自于: https://cipherli.st/ 加強SSL的安全性配置

特別注意最后的強制https跳轉(zhuǎn)葱蝗,我們的目的是SSL雙向認(rèn)證,不走h(yuǎn)ttps無任何意義细燎,所以必須強制跳轉(zhuǎn)https两曼。

第二步 生成客戶端證書并簽證(腳本)

這個過程詳細(xì)描述的文章太多了,這里就不啰嗦介紹openssl和簽證過程了玻驻,本篇內(nèi)容是快速生成雙向認(rèn)證配置的證書悼凑,所以直接貼腳本就行了,命令都是參考互聯(lián)網(wǎng)上各種openssl雙向配置文檔璧瞬,在此基礎(chǔ)之上進行了命令上的簡化與非交互式的支持佛析。

整個目錄結(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/,放入三個腳本彪蓬,分別用于生成CA證書以及CA目錄(create_ca_cert.sh腳本的作用寸莫,只有第一次需要運行),創(chuàng)建客戶端證書档冬,并用CA證書簽證(create_client_cert.sh腳本的作用膘茎,必須先生成CA證書)桃纯,revoke_cert.sh腳本用于吊銷證書,需要收回權(quán)限的時候可以使用披坏。

每個腳本內(nèi)容如下:

  • create_ca_cert.sh
#!/bin/bash -e

# 創(chuàng)建CA根證書
# 非交互式方式創(chuàng)建以下內(nèi)容:
# 國家名(2個字母的代號)
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個字母的代號)
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

# 吊銷一個簽證過的證書

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谜诫,為了不改動默認(rèn)配置,直接按照默認(rèn)配置的內(nèi)容創(chuàng)建這些目錄和文件即可攻旦。還有就是openssl子命令非常多喻旷,但是也和git一樣,可以合并命令牢屋,比如用一條命令同時生成私鑰和簽證請求openssl req -nodes -newkey rsa:2048 -keyout client.key -new -out client.csr且预,在req的同時就做了genrsa。由于創(chuàng)建CA腳本只是第一次運行需要烙无,因此把證書配置直接寫死在腳本中就完事了锋谐。

接下來是創(chuàng)建客戶端證書,為了簡化用戶的使用截酷,在服務(wù)端幫助用戶生成證書并簽證涮拗,把簽證過的證書下發(fā)給用戶就可以了。由于用戶可能是不同部門合搅,不同姓名多搀,不同郵件地址,因此將這三個參數(shù)外部化灾部,做一下參數(shù)解析康铭,加上友好的命令行提示防止遺忘。這個腳本特別注意最后一行赌髓,會生成一個PKCS12格式的證書从藤。openssl默認(rèn)產(chǎn)生的證書格式都是PEM的,會將公鑰和私鑰分開锁蠕,但是瀏覽器導(dǎo)入的時候需要將這些內(nèi)容合并起來形成證書鏈夷野,所以需要將簽證過的證書和私鑰文件合并成一個PKCS12格式的證書,直接將這個.p12格式的證書交給用戶就可以了荣倾。

最后是吊銷證書了悯搔,當(dāng)希望收回某個用戶的訪問權(quá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

此時產(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

此時就可以配置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;代表強制啟用客戶端認(rèn)證菊碟,非法客戶端(無證書,證書不可信)都會返回400錯在刺。

特別注意ssl_crl這個配置逆害,代表Nginx會讀取一個CRL(Certificate Revoke List)文件,之前說過蚣驼,可能會有收回用戶權(quán)限的需求魄幕,因此我們必須有吊銷證書的功能,產(chǎn)生一個CRL文件讓Nginx知道哪些證書被吊銷了即可隙姿。

注意: Nginx配置都是靜態(tài)的梅垄,讀取配置文件之后都會加載到內(nèi)存中厂捞,即使文件內(nèi)容變化也不會重新讀取输玷。因此當(dāng)CRL文件發(fā)生變更之后,Nginx并不能意識到有新的證書被吊銷了靡馁,所以必須使用reload指令讓Nginx重新讀取配置文件: service nginx reloadnginx -s reload

此時重啟Nginx服務(wù)欲鹏,就可以完成SSL雙向認(rèn)證配置了。

我們簽發(fā)一個證書看看:

 ./create_client_cert.sh --ou 財務(wù)部 --cn 財務(wù)經(jīng)理 --email cy@example.com
Generating a 2048 bit RSA private key
................................+++
.............................................................................+++
writing new private key to '財務(wù)經(jīng)理/財務(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 財務(wù)經(jīng)理/
財務(wù)經(jīng)理/
├── 財務(wù)經(jīng)理.crt
├── 財務(wù)經(jīng)理.csr
├── 財務(wù)經(jīng)理.key
└── 財務(wù)經(jīng)理.p12

0 directories, 4 files

這個腳本生成了私鑰文件key臭墨,簽證請求文件csr赔嚎,經(jīng)過CA簽證后的證書文件crt(里面沒有私鑰),以及將crt文件和key進行bundle之后的PKCS12格式的證書文件p12胧弛,將p12文件下載到本地尤误,雙擊一路Next導(dǎo)入證書即可。

: 由于CA的證書文件不會發(fā)生變化结缚,因此簽證新的客戶端證書不需要restart或reload nginx

這次打開我們的網(wǎng)站https://www.example.com损晤,瀏覽器就會提示我們選擇一個已有的客戶端證書進行認(rèn)證了,沒問題就可以看到網(wǎng)站內(nèi)容了

: 每次導(dǎo)入新的證書之后红竭,必須重啟瀏覽器才能提示使用新的證書文件

按照這種方式尤勋,有多少人需要授權(quán),就可以用這個腳本簽發(fā)多少個這樣的證書茵宪,用戶將p12證書導(dǎo)入本地就可以正常訪問網(wǎng)站了最冰。

當(dāng)我們需要收回某人的權(quán)限的時候(比如離職了),我們需要吊銷他的證書:

 ./revoke_cert.sh 財務(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

這個腳本會自動吊銷他的簽證文件crt稀火,并且自動更新CRL文件暖哨。特別注意需要reload或restart nginx才能讓nginx重新加載CRL。這樣被吊銷的證書將無法訪問網(wǎng)站了凰狞。

小結(jié)

本文我們通過Nginx配置SSL雙向認(rèn)證實現(xiàn)對客戶端的加密認(rèn)證篇裁,我們使用了簡易的腳本幫助我們快速生成各種證書與簽證箕慧,免除記憶繁瑣openssl命令行,簡化使用茴恰。

當(dāng)然這只是一個最小可用集颠焦,當(dāng)規(guī)模比較大的時候可能需要做很多改進,比如加入CA的web ui往枣,直接可以操作簽證和吊銷證書伐庭,并且可以自動重啟nginx。

再比如CRL這種靜態(tài)配置文件不適合你的場景分冈,希望的動態(tài)更新吊銷證書列表圾另,那么可以考慮OCSP方案,這個Nginx也是支持的雕沉,通過ssl_stapling_responder配置指定一個OCSP地址集乔,這樣將不需要每次吊銷證書的時候都去重啟nginx了,openssl也提供了ocsp服務(wù)端的功能坡椒,這里就不贅述了扰路,可以自行查找相關(guān)資料。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倔叼,一起剝皮案震驚了整個濱河市汗唱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丈攒,老刑警劉巖哩罪,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巡验,居然都是意外死亡际插,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門显设,熙熙樓的掌柜王于貴愁眉苦臉地迎上來框弛,“玉大人,你說我怎么就攤上這事敷硅」χ洌” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵绞蹦,是天一觀的道長力奋。 經(jīng)常有香客問我,道長幽七,這世上最難降的妖魔是什么景殷? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上猿挚,老公的妹妹穿的比我還像新娘咐旧。我一直安慰自己,他們只是感情好绩蜻,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布铣墨。 她就那樣靜靜地躺著,像睡著了一般办绝。 火紅的嫁衣襯著肌膚如雪伊约。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天孕蝉,我揣著相機與錄音屡律,去河邊找鬼。 笑死降淮,一個胖子當(dāng)著我的面吹牛超埋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佳鳖,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼霍殴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腋颠?” 一聲冷哼從身側(cè)響起繁成,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吓笙,失蹤者是張志新(化名)和其女友劉穎淑玫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體面睛,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡絮蒿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叁鉴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片土涝。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖幌墓,靈堂內(nèi)的尸體忽然破棺而出但壮,到底是詐尸還是另有隱情,我是刑警寧澤常侣,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布蜡饵,位于F島的核電站,受9級特大地震影響胳施,放射性物質(zhì)發(fā)生泄漏溯祸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焦辅。 院中可真熱鬧博杖,春花似錦、人聲如沸筷登。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽前方。三九已至跟继,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镣丑,已是汗流浹背舔糖。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莺匠,地道東北人金吗。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像趣竣,于是被迫代替她去往敵國和親摇庙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355