亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

  • 1 TLS/SSL 的原理
  • 2 對(duì)應(yīng)用透明的加密通道的方案
  • 3 mTLS
  • 4 解決方案
技術(shù)大佬教你如何使用Nginx在公網(wǎng)上搭建加密數(shù)據(jù)通道?

 


最近在跨機(jī)房做一個(gè)部署,因?yàn)闄C(jī)房之間暫時(shí)沒(méi)有專線,所以流量需要經(jīng)過(guò)公網(wǎng)。對(duì)于經(jīng)過(guò)公網(wǎng)的流量,我們一般需要做以下的安全措施:

  1. 只能允許已知的 IP 來(lái)訪問(wèn);
  2. 流量需要加密。

第一項(xiàng)很簡(jiǎn)單,一般的防火墻,或者 Iptables 都可以做到。

對(duì)于加密的部分,最近做了一些實(shí)驗(yàn)和學(xué)習(xí),這篇文章總結(jié)加密的實(shí)現(xiàn)方案,假設(shè)讀者沒(méi)有 TLS 方面的背景知識(shí),會(huì)簡(jiǎn)單介紹原理和所有的代碼解釋。

1 TLS/SSL 的原理

TLS 是加密傳輸數(shù)據(jù),保證數(shù)據(jù)在傳輸?shù)倪^(guò)程中中間的人無(wú)法解密,無(wú)法修改。(本文中將 TLS 與 SSL 作為同義詞。所以提到 SSL 的時(shí)候,您可以認(rèn)為和 TLS 沒(méi)有區(qū)別。)

傳輸?shù)募用懿⒉皇呛芾щy,比如雙方用密碼加密就可以。但是這樣一來(lái),問(wèn)題就到了該怎么協(xié)商這個(gè)密碼。顯然使用固定的密碼是不行的,比如每個(gè)人都要訪問(wèn)一個(gè)網(wǎng)站,如果網(wǎng)站使用固定的密碼,那么和沒(méi)有密碼也沒(méi)有什么區(qū)別了,每個(gè)人都可以使用這個(gè)密碼去偽造網(wǎng)站。

TLS 要解決的問(wèn)題就是,能證明你,是你。現(xiàn)在使用的是非對(duì)稱加密的技術(shù)。非對(duì)稱加密會(huì)有兩個(gè)秘鑰,一個(gè)是公鑰,一個(gè)是私鑰。公鑰會(huì)放在互聯(lián)網(wǎng)上公開(kāi),私鑰不公開(kāi),只有自己知道。只有你有私鑰,我才相信你是你。非對(duì)稱加密的兩個(gè)秘鑰提供了一下功能(本文不會(huì)詳細(xì)介紹這部分原理,只簡(jiǎn)單提到理解后續(xù)內(nèi)容需要的知識(shí)):

  1. 公鑰加密的數(shù)據(jù),只有用私鑰可以解密;
  2. 私鑰可以對(duì)數(shù)據(jù)進(jìn)行簽名,公鑰拿到數(shù)據(jù)之后可以驗(yàn)證數(shù)據(jù)是否由私鑰的所有者簽名的。

有了這兩點(diǎn),網(wǎng)站就可以和訪問(wèn)者構(gòu)建一個(gè)加密的數(shù)據(jù)通道。

首選,網(wǎng)站將公鑰公開(kāi)(即我們經(jīng)常說(shuō)的“證書(shū)”),訪客連接到網(wǎng)站的服務(wù)器第一件事就是下載網(wǎng)站的證書(shū)。因?yàn)樽C書(shū)是公開(kāi)的,每個(gè)人都能下載到此網(wǎng)站的證書(shū),那么怎么確定對(duì)方就是此證書(shū)的所有者呢?客戶端會(huì)生成一個(gè)隨機(jī)數(shù),并使用公鑰進(jìn)行加密,發(fā)送給服務(wù)器:請(qǐng)解密這段密文。

這就是上文提到的功能1,即公鑰加密的數(shù)據(jù),只有私鑰才能解密。服務(wù)器解密之后發(fā)回來(lái)(當(dāng)然,并不是明文發(fā)回來(lái)的,詳細(xì)的 TLS 握手過(guò)程,見(jiàn)這里[1]),客戶端就相信對(duì)方的確是這個(gè)證書(shū)的所有者。后續(xù)就可以通過(guò)非對(duì)稱加密協(xié)商一個(gè)密碼,然后使用此密碼進(jìn)行對(duì)稱加密傳輸(性能快)。

但是這樣就足夠驗(yàn)證對(duì)方身份了嗎?假設(shè)這樣一種情況,我并不是 google.com 這個(gè)域名的所有者,但是我生成了一對(duì)證書(shū),然后自己部署,將用戶訪問(wèn) google.com 的流量劫持到自己這里來(lái),是不是也能使用自己的證書(shū)和用戶進(jìn)行加密傳輸呢?

所以就有了另一個(gè)問(wèn)題:訪客不僅要驗(yàn)證對(duì)方是證書(shū)的真實(shí)所有者,還要驗(yàn)證對(duì)方的證書(shū)的合法性。即 google.com 的證書(shū)只有 Google 公司可以擁有,我的博客的證書(shū)只有我的博客可以擁有。私自簽發(fā)的證書(shū)不合法。

為了解決這個(gè)問(wèn)題,就需要有一個(gè)權(quán)威的機(jī)構(gòu),做如下的保證:只有網(wǎng)站的所有者,才能擁有網(wǎng)站的證書(shū)。然后訪客只要信任這個(gè)“權(quán)威的機(jī)構(gòu)”就可以了。

技術(shù)大佬教你如何使用Nginx在公網(wǎng)上搭建加密數(shù)據(jù)通道?

 

CA 扮演的角色

CA 的全稱是 Certification Authority,是一個(gè)第三方機(jī)構(gòu),在上述加密的流程中,扮演的角色同時(shí)被訪客和網(wǎng)站所信任。

網(wǎng)站需要去 CA 申請(qǐng)證書(shū),而 CA 要對(duì)自己頒發(fā)(簽名)的證書(shū)負(fù)責(zé),即確保證書(shū)頒發(fā)給了對(duì)方,頒發(fā)證書(shū)之前要驗(yàn)證你是你。申請(qǐng)證書(shū)的時(shí)候,CA 一般會(huì)要求你完成一個(gè) Challenge 來(lái)證明身份,比如,要求你將某個(gè) URL 返回特定內(nèi)容,或者要求你將 DNS 的某個(gè) text record 返回特定內(nèi)容來(lái)證明你的確擁有此域名(詳見(jiàn) validation standards[2])。只有你證明了你是你,CA 才會(huì)簽證書(shū)給你。

訪客是怎么驗(yàn)證證書(shū)的呢?這就用到了上文提到的功能2:“私鑰可以對(duì)數(shù)據(jù)進(jìn)行簽名,公鑰拿到數(shù)據(jù)之后可以驗(yàn)證數(shù)據(jù)是否由私鑰的所有者簽名的。” CA 也有自己的一套私鑰公鑰,CA 使用私鑰對(duì)網(wǎng)站的證書(shū)進(jìn)行簽名(擔(dān)保),訪客拿到網(wǎng)站的證書(shū)之后,使用 CA 的公鑰校驗(yàn)簽名即可驗(yàn)證這個(gè)“擔(dān)保”的有效性。

那么 CA 的公鑰是怎么來(lái)的呢?答案是直接存儲(chǔ)在客戶端的。linux 一般存儲(chǔ)在 /etc/ssl/certs。由此可見(jiàn),CA 列表更新通常意味著要升級(jí)系統(tǒng),一個(gè)新的 CA 被廣泛接受是一個(gè)漫長(zhǎng)的過(guò)程。新 CA 簽發(fā)的證書(shū)可能有一些老舊的系統(tǒng)依然不信任。

比如 letsencrypt 的 CA[3],之前就是使用交叉簽名的方式工作,即已有的 CA 為我做擔(dān)保,我可以給其他的網(wǎng)站簽發(fā)證書(shū)。這也是中級(jí)證書(shū)的工作方式。每天有這么多網(wǎng)站要申請(qǐng)證書(shū),CA 怎么簽發(fā)的過(guò)來(lái)呢?于是 CA 就給很多中級(jí)證書(shū)簽名,中級(jí)證書(shū)給網(wǎng)站簽名。這就是“信任鏈”。訪客既然信任 CA,也就信任 CA 簽發(fā)的中級(jí),也就信任中級(jí)簽發(fā)的證書(shū)。

被信任很漫長(zhǎng),被不信任很簡(jiǎn)單。

CA (以及中級(jí)證書(shū)機(jī)構(gòu))有著非常大的權(quán)利。舉例,CA 假如給圖謀不軌的人簽發(fā)了 Google 的證書(shū),那么攻擊者就可以冒充 Google。即使 Google 和這個(gè) CA 并沒(méi)有任何業(yè)務(wù)往來(lái),但是自己的用戶還是被這個(gè) CA 傷害了。所以 CA 必須做好自己的義務(wù):

  1. 保護(hù)自己的私鑰不被泄漏;
  2. 做好驗(yàn)證證書(shū)申請(qǐng)者身份的義務(wù);
  3. 如果(2)有了疏忽,對(duì)于錯(cuò)誤簽發(fā)的證書(shū)要及時(shí)吊銷(xiāo)。

案例:賽門(mén)鐵克證書(shū)占了活躍證書(shū)的 30% – 45%(當(dāng)時(shí)[4]),但是被 Google 發(fā)現(xiàn)其錯(cuò)誤頒發(fā)了 3 萬(wàn)個(gè)證書(shū),發(fā)現(xiàn)后卻不作為。因此逐步在后續(xù)的 Chrome 版本中吊銷(xiāo)了賽門(mén)鐵克的證書(shū)。

案例2:let’sencrypt 今年 1 月份發(fā)現(xiàn)自己的 TLS-ALPN-01 chanllege 有問(wèn)題,于是按照規(guī)定,在 5 天后吊銷(xiāo)了這期間通過(guò) TLS-ALPN-01 頒發(fā)的所有證書(shū)。

說(shuō)道這里我想繼續(xù)跑一個(gè)題。我以前給博客部署證書(shū)的時(shí)候(2017年[5])就想:CA 給我發(fā)一個(gè)證書(shū)居然要收我的錢(qián)?這個(gè)不是零成本的東西嗎?他們想發(fā)多少就發(fā)多少??吹浆F(xiàn)在讀者應(yīng)該明白了,這并不是一個(gè)零成本的事情:簽發(fā)證書(shū)的驗(yàn)證服務(wù)需要花錢(qián),而 CA Root key 的保護(hù)要花更多的錢(qián)。整個(gè) CA 公司(組織)的核心資產(chǎn)就是一個(gè) key,如果這個(gè) key 暴露了,后果不堪設(shè)想。

所以,一個(gè)無(wú)比重要卻要一直使用的 key 在一個(gè)上千萬(wàn)人的組織里怎么被使用而不暴露給任何一個(gè)人呢?這是要花很多錢(qián)的。Root key 的生成會(huì)有一個(gè)儀式(Key ceremony),全程錄像,有 20 多個(gè)不同組織的代表會(huì)現(xiàn)場(chǎng)參加并監(jiān)督,會(huì)有 3000 多個(gè)人觀看實(shí)時(shí)錄像,確保 key 的生成是標(biāo)準(zhǔn)流程。

在 Root key 的保存和使用上,Root key 只會(huì)簽中級(jí) CA,以減少使用次數(shù)以及 Root key 需要被 revoke(代價(jià)太大)的風(fēng)險(xiǎn)。Root Key 保存在一個(gè)特殊的硬件中(HSM,Hardware security module),完全離線保存,HSM 也放在特殊的機(jī)房中,7×24 有人看守,并離線錄像,機(jī)房有 Class 5 Alarm System,有多把鎖,沒(méi)有一個(gè)人可以單獨(dú)進(jìn)入。

使用這個(gè) Root Key 必須物理上進(jìn)入這個(gè)機(jī)房,使用過(guò)程全程錄像,并且記錄使用過(guò)程,如果有問(wèn)題可以很快地將 Root Key 簽的內(nèi)容 revoke。這里有一個(gè)視頻介紹 Key Signing Ceremony[6],非常有趣。所以說(shuō) CA 機(jī)構(gòu)并不是一個(gè)搖錢(qián)樹(shù),Let’s Encrypt 這種組織簡(jiǎn)直就是慈善機(jī)構(gòu)。

以上就是 TLS,證書(shū),CA 大致的工作原理,稍稍有些跑題,有了這些知識(shí)我們就可以利用 TLS 來(lái)建立一個(gè)加密的數(shù)據(jù)通道了。后續(xù)幾乎都是實(shí)際的操作。筆者對(duì)這部分也不是精通,如果有錯(cuò)誤,歡迎指出。

2 對(duì)應(yīng)用透明的加密通道的方案

上文是通過(guò)網(wǎng)站部署 HTTPS 來(lái)講的 TLS 的工作原理。其實(shí)網(wǎng)站部署 HTTPS 還算是比較簡(jiǎn)單:你只需要找一個(gè) CA,申請(qǐng)證書(shū),完成 CA 的驗(yàn)證,部署證書(shū),就可以了。

現(xiàn)在要解決的問(wèn)題更加復(fù)雜一些:我們的兩個(gè)組件之間是通過(guò)自己研發(fā)的協(xié)議通訊(基于 TCP),現(xiàn)在要分別部署在兩個(gè)機(jī)房,通過(guò)公網(wǎng)進(jìn)行通訊。

我們的方案要對(duì)通訊的兩邊做好安全防護(hù):

  1. 數(shù)據(jù)要進(jìn)行加密傳輸;
  2. 要對(duì)兩邊做身份驗(yàn)證,比如 A 向 B 發(fā)起連接,A 要驗(yàn)證 B 的身份,B 也要驗(yàn)證 A 的身份;
  3. 最好對(duì)于應(yīng)用來(lái)說(shuō)透明,即應(yīng)用完全不修改代碼,依然按照原來(lái)的方式工作,但是我們將中間的流量進(jìn)行加密。

3 mTLS

mTLS 的全稱是 Mutual TLS,即雙向的 TLS 驗(yàn)證。HTTPS 只是訪客驗(yàn)證了網(wǎng)站的身份,網(wǎng)站并沒(méi)有驗(yàn)證訪客的身份。其實(shí)要驗(yàn)證也是可以的,網(wǎng)站發(fā)送證書(shū)之后可以跟訪客說(shuō):“現(xiàn)在該輪到你出示你的證書(shū)了”。如果訪客不能提供有效的證書(shū),網(wǎng)站可以拒絕服務(wù)。

其實(shí),ssh 方式就是一個(gè)雙向驗(yàn)證的過(guò)程。我們都知道通過(guò) ssh key 登錄 server 的時(shí)候,需要讓 server 信任你的 key(即將你的 pubkey 放到 server 上去)。但是還有一個(gè)過(guò)程容易被忽略掉,在第一次通過(guò) ssh 連接服務(wù)器的時(shí)候,ssh 客戶端會(huì)給你展示 server 的 pubkey,問(wèn)你是否信任。如果之后這個(gè) key 變了,說(shuō)明有可能你連接到的并不是目的服務(wù)器。

技術(shù)大佬教你如何使用Nginx在公網(wǎng)上搭建加密數(shù)據(jù)通道?

 

第一次連接到服務(wù)器的提示

如果之后這個(gè) key 變了,ssh 客戶端就會(huì)拒絕連接。

技術(shù)大佬教你如何使用Nginx在公網(wǎng)上搭建加密數(shù)據(jù)通道?

 

Git 也是通過(guò)走 ssh 協(xié)議的,所以也是一個(gè)雙向認(rèn)證。你在使用 Github 的時(shí)候要互相信任對(duì)方:

  • Github 信任你的方式是:你將自己的 pubkey 上傳到 GitHub (設(shè)置,profile,keys)
  • 你信任 GitHub 的方式是:GitHub 將自己的 pubkey 公布在網(wǎng)上[7]。

4 解決方案

為了實(shí)現(xiàn)對(duì)應(yīng)用透明的加密通訊,我們?cè)趦蓚€(gè)機(jī)房各搭建一個(gè) Nginx,這里兩個(gè) Nginx 之間通過(guò) mTLS 相互認(rèn)證對(duì)方。應(yīng)用將請(qǐng)求明文發(fā)給同機(jī)房的 Nginx,然后 Nginx 負(fù)責(zé)加密發(fā)給對(duì)方。對(duì)于應(yīng)用來(lái)說(shuō),對(duì)方機(jī)房的組件就如同和自己工作在相同機(jī)房一樣。最終搭建起來(lái)如下圖所示。

技術(shù)大佬教你如何使用Nginx在公網(wǎng)上搭建加密數(shù)據(jù)通道?

 

搭建過(guò)程

因?yàn)橛?HTTP 流量來(lái)搭建,相關(guān)的工具和日志會(huì)更友好一些。所以我們會(huì)先用 HTTP 將這個(gè)通道搭建起來(lái),然后換成 tcp steam。

準(zhǔn)備證書(shū)

我們一共需要兩套證書(shū),一套給 Client,一套給 Server。

因?yàn)槲覀冞@里主要要解決的問(wèn)題內(nèi)部互相信任的問(wèn)題,不需要開(kāi)給外面的用戶,所以這里我們采用 self signed certificate,即,我們自己做 CA,給自己簽發(fā)證書(shū)。自簽發(fā)證書(shū)的好處是很靈活,方便,壞處是有一些安全隱患(畢竟不像權(quán)威機(jī)構(gòu)那樣專業(yè))。所以我把這個(gè)過(guò)程寫(xiě)在博客上,請(qǐng)大家?guī)兔纯戳鞒逃袥](méi)有問(wèn)題。

首先我們創(chuàng)建一個(gè) CA 的 key,即私鑰。CA 的 key 最好給一個(gè)密碼保護(hù),每次使用這個(gè) CA 簽發(fā)證書(shū)的時(shí)候,都需要輸入密碼。

生成 key 的命令:

openssl genrsa -des3 -out ca.key 4096

輸出(其中按照提示輸入密碼):

Generating RSA private key, 4096 bit long modulus (2 primes)
.............................................................++++
....................................................................................................................................................................................++++
e is 65537 (0x010001)
Enter pass phrase for ca.key:<passphrase>
Verifying - Enter pass phrase for ca.key:<passphrase>

命令的解釋:

  • openssl:cert 和 key 相關(guān)的操作我們都用 openssl 來(lái)完成
  • genrsa:生成 RSA 私鑰
  • -des3:生成的 key,使用 des3 進(jìn)行加密,如果不加這個(gè)參數(shù),就不會(huì)提示讓你輸入密碼
  • 4096:生成 key 的長(zhǎng)度

這里我們假設(shè)所使用的密碼是 hello。

然后我們來(lái)生成 CA 的公鑰部分,即證書(shū)。

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

這時(shí)會(huì)詢問(wèn)你一些信息,比如地區(qū),組織名字之類的。其中,Organization Name 和 Common Name 需要留意。CA 的這一步填什么都可以。Common Name 又簡(jiǎn)稱 CN,就是證書(shū)簽發(fā)給哪一個(gè)域名(也可以是 IP)的意思。

輸出會(huì)是如下所示:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:CertAuth
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

命令的解釋:

  • req:創(chuàng)建證書(shū)請(qǐng)求;
  • -new:產(chǎn)生新的證書(shū);
  • -x509:直接使用 x509 產(chǎn)生新的自簽名證書(shū),如果不加這個(gè)參數(shù),會(huì)產(chǎn)生一個(gè)“證書(shū)簽名請(qǐng)求”而不是一個(gè)證書(shū)。
  • -days 365:證書(shū)1年之后過(guò)期,也可以省略這個(gè)參數(shù),設(shè)置為永不過(guò)期;
  • key:創(chuàng)建公共證書(shū)的私鑰,會(huì)被提示輸入私鑰的密碼;
  • -out:生成的證書(shū)。

到這里,我們有了一對(duì) CA 證書(shū),ca.key 和 ca.crt 兩個(gè)文件。接下來(lái)申請(qǐng) server 端的證書(shū)。

Server 端證書(shū)依然是先生成一個(gè) key,這里就不需要密碼保護(hù)了:

openssl genrsa -out server.key 4096

然后這里下一步不是直接生成證書(shū),而是生成一個(gè)證書(shū)請(qǐng)求。但是那些問(wèn)題依然是要回答一遍的。

openssl req -new -key server.key -out server.csr

回答問(wèn)題的時(shí)候要注意兩個(gè)地方:

  • Organization Name: 不能和 CA 的一樣;
  • Common Name: 必須要寫(xiě)一個(gè),可以寫(xiě)一個(gè)不存在的域名,比如 proxy.example.com。否則,會(huì)有錯(cuò)誤:“* SSL: unable to obtain common name from peer certificate”。

否則證書(shū)無(wú)法使用。

到這里其實(shí)也可以看出,CA 的證書(shū)和其他的證書(shū)沒(méi)有什么不同,也是一個(gè)普通的證書(shū)而已。

這個(gè) .csr 文件是 Ceritifcate Signing Request,即請(qǐng)求簽名。接下來(lái)我們使用我們的 CA 給這個(gè) Server 證書(shū)簽名(作擔(dān)保!)。

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

這個(gè)命令需要輸入 CA key 的密碼,就是剛剛說(shuō)的 hello。

命令的解釋:

  • x509:公有證書(shū)的標(biāo)準(zhǔn)格式;
  • -CA:使用 CA 對(duì)其簽名;
  • -CAkey:CA key(沒(méi)有這個(gè)豈不是人人可以用 CA 證書(shū)簽名了?);
  • -set_serial 01:簽發(fā)的序列號(hào),如果證書(shū)有過(guò)期時(shí)間的話,過(guò)期之后,可以直接用這個(gè) .csr 修改序列號(hào)重新簽一個(gè),不需要重新生成 .csr 文件;

如此,就得到了 server.crt 文件。

我們可以使用這條命令驗(yàn)證生成的證書(shū)是 ok 的:

# openssl verify -verbose -CAfile ca.crt server.crt 
server.crt: OK

重復(fù)此流程再簽發(fā)一個(gè) client 端的證書(shū)。

結(jié)束后,我們有以下內(nèi)容:

  • ca.key
  • ca.crt
  • CA 的密碼,需要保存
  • server.key
  • server.crt
  • server.csr:部署不需要用到,可以只保存在安全的地方即可;
  • Server 證書(shū)簽發(fā)序列:只保存即可;
  • client.key
  • client.crt
  • client.csr:部署不需要用到,可以只保存在安全的地方即可;
  • Client 證書(shū)簽發(fā)序列:只保存即可;

然后接下來(lái)就可以部署起來(lái)了。

搭建遠(yuǎn)程 Server 端的 Nginx

為了模擬轉(zhuǎn)發(fā)到后端應(yīng)用的場(chǎng)景,這里的 Nginx 不使用靜態(tài)文件,而是用一個(gè) fastapi 寫(xiě)的樣例程序來(lái)做后端:

from typing import Optionalfrom fastapi import FastAPIApp = FastAPI()@app.get("/")def read_root():    return {"Hello": "World"}

啟動(dòng)的命令是:

uvicorn app:app

程序默認(rèn)會(huì)運(yùn)行在 8000 端口。

然后修改 Nginx 的配置,nginx.conf 不變,我們只修改 default 的配置,將 default rename 成 remote_server,然后修改成成如下配置:

server {        listen 443 default_server ssl;        listen [::]:443 default_server ssl;        server_name _;        ssl_certificate /home/vagrant/cert/server.crt;        ssl_certificate_key /home/vagrant/cert/server.key;        location / {                proxy_pass http://127.0.0.1:8000;        }

這就是一個(gè)很簡(jiǎn)單的 Nginx HTTPS 配置,證書(shū)配置上了我們剛剛自己簽發(fā)的證書(shū):

  • ssl_certificate:告訴 Nginx 使用哪一個(gè)公有證書(shū);
  • ssl_certificate_key:此證書(shū)對(duì)用的私鑰是什么,服務(wù)器需要有私鑰才能工作。

證書(shū)已經(jīng)配置好了。這時(shí)候我們?nèi)?cURL 443 端口會(huì)出現(xiàn)錯(cuò)誤:“curl: (60) SSL: unable to obtain common name from peer certificate”,cURL 不信任這個(gè)服務(wù)器的證書(shū)。這是當(dāng)然了,因?yàn)檫@個(gè)證書(shū)是我們自己作為 CA 簽的。

要正常訪問(wèn),必須使用 cURL --ca ./ca.cert 來(lái)告訴 cURL 我們信任這個(gè) CA (所簽發(fā)的所有證書(shū))。

另外還要注意的是,記得我們之前的 Server 證書(shū)是簽發(fā)給 proxy.example.com 的嗎?我們這里必須要訪問(wèn)這個(gè)域名才行。需要這樣使用:

curl -v https://proxy.example.com --cacert ./ca.crt --connect-to proxy.example.com:443:127.0.0.1:443

--connect-to 的意思是,所有發(fā)往這個(gè)域名的請(qǐng)求,都直接發(fā)給這個(gè) IP。

Client 對(duì) Server 的驗(yàn)證就配置好了,接下來(lái)再配置 Server 對(duì) Client 的驗(yàn)證。

我們只需要將上面的配置文件改成如下即可:

server {        listen 443 default_server ssl;        listen [::]:443 default_server ssl;        server_name _;        ssl_certificate /home/vagrant/cert/server.crt;        ssl_certificate_key /home/vagrant/cert/server.key;        ssl_verify_client on;        ssl_client_certificate /home/vagrant/cert/ca.crt;        location / {                proxy_pass http://127.0.0.1:8000;        }}

添加的內(nèi)容的含義:

  • ssl_verify_client:需要驗(yàn)證客戶端的證書(shū);
  • ssl_client_certificate:我們信任這個(gè) CA 所簽發(fā)的所有證書(shū)。

這里有一個(gè)小插曲:Nginx 的文檔上說(shuō),ssl_trusted_certificate 和 ssl_client_certificate 這兩個(gè)配置效果都是一樣的,唯一的區(qū)別是 ssl_client_certificate 會(huì)將信任的 CA 列表發(fā)送給客戶端,但是 ssl_trusted_certificate 不會(huì)發(fā)。

發(fā)送是合理的,因?yàn)榭蛻舳巳绻泻芏嘧C書(shū),讓客戶端一個(gè)一個(gè)去嘗試哪一個(gè)能建連是沒(méi)有意義并且很浪費(fèi)的。ssl_trusted_certificate 的作用是驗(yàn)證 OCSP Response。但是我嘗試了 ssl_trusted_certificate,Nginx 會(huì)直接 fail 掉語(yǔ)法檢查:

The server fails to start with error: nginx: [emerg] no ssl_client_certificate for ssl_verify_clientb

這里發(fā)現(xiàn)一個(gè) ticket 詢問(wèn)和我一樣的問(wèn)題:https://trac.nginx.org/nginx/ticket/1902,不過(guò)至今沒(méi)有回復(fù)。我以為是 Nginx 版本的 Bug,然后嘗試了最新的版本依然是一樣的結(jié)果。如果讀者知道可以指點(diǎn)一下,謝謝。

這樣配置之后 reload Nginx,就開(kāi)啟了對(duì)客戶端的證書(shū)驗(yàn)證了。這時(shí)候我們繼續(xù)使用上面那個(gè) cURL,就無(wú)法得到響應(yīng)。

<head><title>400 No required SSL certificate was sent</title></head>

Nginx 會(huì)要求你提供證書(shū)。

如下的 cURL,帶上證書(shū),就可以正常拿到響應(yīng)。

curl -v https://proxy.example.com --cacert ./ca.crt --connect-to proxy.example.com:443:127.0.0.1:443 --cert client.crt --key client.key

這樣,遠(yuǎn)端的 Nginx 就配置好了,它會(huì)提供證書(shū)證明自己的身份,也會(huì)要求客戶端提供證書(shū)進(jìn)行驗(yàn)證。

接下來(lái)搭建本地的 Nginx,將明文請(qǐng)求加密對(duì)接到遠(yuǎn)端的 Nginx。

搭建本地 Client 端的 Nginx

本地機(jī)房開(kāi)啟一個(gè) Nginx,監(jiān)聽(tīng) 80 端口,轉(zhuǎn)發(fā)到遠(yuǎn)程的 443 端口。

配置如下:

upstream remote{ server 127.0.0.1:443;}server {        listen 80 default_server;        listen [::]:80 default_server;        server_name _;        location / {                proxy_pass https://remote;                proxy_ssl_trusted_certificate /home/vagrant/cert/ca.crt;                proxy_ssl_verify on;                proxy_ssl_server_name on;                proxy_ssl_name proxy.example.com;                proxy_ssl_certificate /home/vagrant/crt/client.crt;                proxy_ssl_certificate_key /home/vagrant/cert/client.key;        }}

這個(gè)配置可以分成兩部分看,第一部分,是要驗(yàn)證對(duì)方的證書(shū):

  • proxy_ssl_verify:需要對(duì)方提供證書(shū);
  • proxy_ssl_trusted_certificate:我們只信任這個(gè) CA 簽發(fā)的所有證書(shū);
  • proxy_ssl_server_name:不像 cURL 的 --connect-to 選項(xiàng),這里我們直接指定目標(biāo) IP 轉(zhuǎn)發(fā),但是我們使用 SNI 功能來(lái)告訴對(duì)方我們要連接哪一個(gè) domain,來(lái)驗(yàn)證相關(guān) domain 的證書(shū);
  • proxy_ssl_name:我們需要哪一個(gè) domain 的證書(shū)。

然后第二部分是提供自己的證書(shū):

  • proxy_ssl_certificate:我的證書(shū);
  • proxy_ssl_certificate_key:我的私鑰,不會(huì)發(fā)送給對(duì)方,只是本地 Nginx 自己使用。

然后就可以 cURL 本地的 80 端口了:

curl http://127.0.0.1 -v *   Trying 127.0.0.1:80...* TCP_NODELAY set* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)> GET / HTTP/1.1> Host: 127.0.0.1> User-Agent: curl/7.68.0> Accept: */*> * Mark bundle as not supporting multiuse< HTTP/1.1 200 OK< Server: nginx/1.18.0 (Ubuntu)< Date: Wed, 16 Mar 2022 03:49:05 GMT< Content-Type: application/json< Content-Length: 17< Connection: keep-alive< * Connection #0 to host 127.0.0.1 left intact{"Hello":"World"}

可以看到我們從客戶端(cURL)發(fā)出明文 HTTP 請(qǐng)求,到服務(wù)端(fastapi)收到明文 HTTP 請(qǐng)求,兩邊都不知道中間流量加密過(guò)程,但是走公網(wǎng)的部分已經(jīng)被加密了。就實(shí)現(xiàn)了本文開(kāi)頭的需求。

代理 TCP steam

以上是 HTTP 的配置,將其換成 TCP Steam 的代理也很簡(jiǎn)單,相應(yīng)的配置修改一下就可以。這里我們以 redis 服務(wù)為例來(lái)展示一下配置。

/etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
}

stream {

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

Remote Server 的配置:/etc/nginx/sites-enabled/remote_server

server {
        listen 443 ssl;
        proxy_pass 127.0.0.1:6379;

        ssl_certificate /home/vagrant/cert/server.crt;
        ssl_certificate_key /home/vagrant/cert/server.key;

        ssl_verify_client on;
        ssl_client_certificate /home/vagrant/cert/ca.crt;
}

local_client 的配置:/etc/nginx/sites-enabled/client_server

upstream remote{
 server 127.0.0.1:443;
}

server {
        listen 80;
        listen [::]:80;

        proxy_pass remote;
        proxy_ssl_trusted_certificate /home/vagrant/cert/ca.crt;
        proxy_ssl_verify on;
        proxy_ssl_server_name on;
        proxy_ssl_name config.example.com;

        proxy_ssl on;
        proxy_ssl_certificate /home/vagrant/cert/client.crt;
        proxy_ssl_certificate_key /home/vagrant/cert/client.key;
}

基本上就是把 HTTP 代理?yè)Q成了 TCP 代理指令。

這樣配置好之后,我們就可以用 redis-cli 去連接本地的 80 端口了。

redis-cli -p 80
127.0.0.1:80> get foo
"bar"

分享到:
標(biāo)簽:加密 數(shù)據(jù)
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定