前言
大家好,我是老馬。很高興遇到你。
我們為 java 開發者實現了 java 版本的 nginx
https://github.com/houbb/nginx4j
如果你想知道 servlet 如何處理的,可以參考我的另一個項目:
手寫從零實現簡易版 tomcat minicat
nginx 系列
從零手寫實現 nginx-01-為什么不能有 java 版本的 nginx?
從零手寫實現 nginx-02-nginx 的核心能力
從零手寫實現 nginx-03-nginx 基于 Netty 實現
從零手寫實現 nginx-04-基于 netty http 出入參優化處理
從零手寫實現 nginx-05-MIME類型(Multipurpose Internet Mail Extensions,多用途互聯網郵件擴展類型)
Nginx 實戰-01-nginx ubuntu 安裝筆記
Nginx 實戰-01-nginx windows 安裝筆記
Nginx 實戰-02-nginx proxy_pass 服務代理訪問 使用筆記 ubuntu nodejs
Nginx 實戰-03-nginx 負載均衡
Nginx 實戰-04-nginx 不同的地址訪問不同的服務
Nginx 實戰-05-nginx 反向代理實現域名到指定的 ip
Nginx-01-聊一聊 nginx
Nginx-01-Nginx 是什么
Nginx-02-為什么使用 Nginx
Nginx-02-Nginx Ubuntu 安裝 + windows10 + WSL ubuntu 安裝 nginx 實戰筆記
Nginx-02-基本使用
Nginx-03-Nginx 項目架構
Nginx-04-Docker Nginx
Nginx-05-nginx 反向代理是什么?windows 下如何配置使用 nginx
Nginx-06-nginx 匯總入門介紹
NGINX SSL 終止
終止來自客戶端的 HTTPS 流量,減輕上游的網絡和應用服務器的 SSL/TLS 加密的計算負載。
本節描述了如何在 NGINX 和 NGINX Plus 上配置 HTTPS 服務器。
設置 HTTPS 服務器
要設置 HTTPS 服務器,在您的 nginx.conf 文件中,將 ssl 參數包含在 server 塊中的 listen 指令中,然后指定服務器證書和私鑰文件的位置:
server {listen 443 ssl;server_name www.example.com;ssl_certificate www.example.com.crt;ssl_certificate_key www.example.com.key;ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers HIGH:!aNULL:!MD5;#...
}
服務器證書是一個公共實體。它會發送給連接到 NGINX 或 NGINX Plus 服務器的每個客戶端。私鑰是一個安全實體,應存儲在受限制的訪問文件中。但是,NGINX 主進程必須能夠讀取此文件。或者,私鑰可以存儲在與證書相同的文件中:
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;
在這種情況下,重要的是要限制對文件的訪問。請注意,雖然在這種情況下證書和密鑰存儲在一個文件中,但只有證書會發送給客戶端。
當建立連接時,ssl_protocols 和 ssl_ciphers 指令可以用來要求客戶端僅使用 SSL/TLS 的強版本和密碼。
自版本 1.9.1 起,NGINX 使用以下默認值:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
有時在舊密碼的設計中會發現漏洞,我們建議在現代 NGINX 配置中禁用它們(不幸的是,默認配置由于對現有 NGINX 部署的向后兼容性而無法輕易更改)。請注意,CBC 模式密碼可能對多種攻擊(特別是 BEAST 攻擊,如 CVE-2011-3389 中所描述的)容易受到攻擊,并且我們建議不要使用 SSLv3,除非您需要支持舊版客戶端。
客戶端證書的 OCSP 驗證
NGINX 可以配置為使用在線證書狀態協議(OCSP)在客戶端提供的 X.509 證書提交時檢查其有效性。將客戶端證書狀態的 OCSP 請求發送到 OCSP 響應器,響應器檢查證書的有效性并返回帶有證書狀態的響應:
- Good - 證書未被吊銷
- Revoked - 證書已被吊銷
- Unknown - 關于客戶端證書的信息不可用
要啟用 SSL 客戶端證書的 OCSP 驗證,請在 ssl_verify_client 指令后面指定 ssl_ocsp 指令,后者啟用證書驗證:
server {listen 443 ssl;ssl_certificate /etc/ssl/foo.example.com.crt;ssl_certificate_key /etc/ssl/foo.example.com.key;ssl_verify_client on;ssl_trusted_certificate /etc/ssl/cachain.pem;ssl_ocsp on; # 啟用 OCSP 驗證#...
}
NGINX 將 OCSP 請求發送到嵌入在客戶端證書中的 OCSP URI,除非使用 ssl_ocsp_responder 指令定義了不同的 URI。僅支持 http:// OCSP 響應器:
#...
ssl_ocsp_responder http://ocsp.example.com/;
#...
要在所有工作進程共享的單個內存區域中緩存 OCSP 響應,請使用 ssl_ocsp_cache 指令定義區域的名稱和大小。
除非 OCSP 響應中的 nextUpdate 值指定了不同的值,否則響應將被緩存 1 小時:
#...
ssl_ocsp_cache shared:one:10m;
#...
客戶端證書驗證的結果可在 $ssl_client_verify 變量中獲取,包括 OCSP 失敗的原因。
HTTPS 服務器優化
SSL 操作消耗額外的 CPU 資源。最消耗 CPU 資源的操作是 SSL 握手。有兩種方法可以減少每個客戶端的這些操作次數:
- 啟用 keepalive 連接,通過一個連接發送多個請求
- 重用 SSL 會話參數,避免并行和后續連接的 SSL 握手
會話存儲在 SSL 會話緩存中,在工作進程之間共享,并由 ssl_session_cache 指令進行配置。1 兆字節的緩存包含約 4000 個會話。默認緩存超時時間為 5 分鐘。可以使用 ssl_session_timeout 指令增加此超時時間。以下是一個針對具有 10 兆字節共享會話緩存的多核系統進行優化的示例配置:
worker_processes auto;http {ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;server {listen 443 ssl;server_name www.example.com;keepalive_timeout 70;ssl_certificate www.example.com.crt;ssl_certificate_key www.example.com.key;ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers HIGH:!aNULL:!MD5;#...}
}
SSL 證書鏈
一些瀏覽器可能會抱怨由知名證書頒發機構簽署的證書,而其他瀏覽器可能會接受該證書而無需任何問題。這是因為頒發機構使用一個中間證書簽署了服務器證書,該中間證書不在特定瀏覽器分發的知名可信證書頒發機構的基礎上。在這種情況下,頒發機構提供了一組鏈接證書,應將其與簽名的服務器證書串聯起來。服務器證書必須在組合文件中的鏈接證書之前出現:
cat www.example.com.crt bundle.crt > www.example.com.chained.crt
將得到的文件用于 ssl_certificate 指令:
server {listen 443 ssl;server_name www.example.com;ssl_certificate www.example.com.chained.crt;ssl_certificate_key www.example.com.key;#...
}
如果服務器證書和 bundle 的順序被串聯錯誤,NGINX 將無法啟動,并顯示以下錯誤消息:
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed(SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
發生錯誤是因為 NGINX 嘗試使用 bundle 的第一個證書而不是服務器證書的私鑰。
通常,瀏覽器會存儲它們收到的中間證書,并由受信任的機構簽署。因此,經常使用的瀏覽器可能已經擁有所需的中間證書,并且可能不會抱怨發送了未鏈接包的證書。為確保服務器發送完整的證書鏈,可以使用 openssl 命令行實用程序:
$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc/OU=MIS Department/CN=www.GoDaddy.com/serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=079692871 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority2 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authorityi:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
...
在此示例中,www.GoDaddy.com 服務器證書 #0 的主題(“s”)由簽發者(“i”)簽署,后者本身是證書 #1 的主題。
證書 #1 由其簽發者簽署,后者本身是證書 #2 的主題。但是,此證書由知名的簽發者 ValiCert, Inc. 簽署,其證書存儲在瀏覽器中。
如果未添加證書包,只顯示服務器證書 (#0)。
單一 HTTP/HTTPS 服務器
可以通過在同一個虛擬服務器中放置一個帶有 ssl 參數和一個不帶有的 listen 指令來配置同時處理 HTTP 和 HTTPS 請求的單個服務器:
server {listen 80;listen 443 ssl;server_name www.example.com;ssl_certificate www.example.com.crt;ssl_certificate_key www.example.com.key;#...
}
在 NGINX 版本 0.7.13 及之前,無法針對單個監聽套接字有選擇地啟用 SSL,如上所示。SSL 只能通過使用 ssl 指令來為整個服務器啟用,因此無法設置單個 HTTP/HTTPS 服務器。為了解決這個問題,版本 0.7.14 及更高版本中添加了 listen 指令的 ssl 參數。因此,ssl 指令在版本 0.7.14 及之后被棄用。
基于名稱的 HTTPS 服務器
當兩個或更多個 HTTPS 服務器配置為在單個 IP 地址上監聽時,通常會出現一個常見問題:
server {listen 443 ssl;server_name www.example.com;ssl_certificate www.example.com.crt;#...
}server {listen 443 ssl;server_name www.example.org;ssl_certificate www.example.org.crt;#...
}
使用這種配置,瀏覽器接收默認服務器的證書。在這種情況下,無論請求的服務器名稱是什么,都是 www.example.com。這是由 SSL 協議本身的行為引起的。SSL 連接在瀏覽器發送 HTTP 請求之前建立,NGINX 不知道請求的服務器名稱。因此,它只能提供默認服務器的證書。
解決此問題的最佳方法是為每個 HTTPS 服務器分配一個單獨的 IP 地址:
server {listen 192.168.1.1:443 ssl;server_name www.example.com;ssl_certificate www.example.com.crt;#...
}server {listen 192.168.1.2:443 ssl;server_name www.example.org;ssl_certificate www.example.org.crt;#...
}
請注意,還有一些特定的代理設置用于 HTTPS 上游(proxy_ssl_ciphers、proxy_ssl_protocols 和 proxy_ssl_session_reuse),可用于在 NGINX 和上游服務器之間進行 SSL 的精細調整。您可以在 HTTP 代理模塊文檔中了解更多信息。
具有多個名稱的 SSL 證書
有其他方法可以在多個 HTTPS 服務器之間共享單個 IP 地址。但是,所有這些方法都有缺點。
一種方法是在 SubjectAltName 證書字段中使用具有多個名稱的證書,例如,www.example.com 和 www.example.org。但是,SubjectAltName 字段的長度是有限的。
另一種方法是使用帶有通配符名稱的證書,例如,*.example.org。通配符證書可保護指定域的所有子域,但僅限于一個級別。
該證書匹配 www.example.org,但不匹配 example.org 或 www.sub.example.org。這兩種方法也可以結合使用。
證書可以在 SubjectAltName 字段中包含精確和通配符名稱。
例如,example.org 和 *.example.org。
最好將具有多個名稱的證書文件及其私鑰文件放置在配置的 http 級別,以便它們在所有服務器中繼承單個內存副本:
ssl_certificate common.crt;
ssl_certificate_key common.key;server {listen 443 ssl;server_name www.example.com;#...
}server {listen 443 ssl;server_name www.example.org;#...
}
服務器名稱指示 (SNI)
在單個 IP 地址上運行多個 HTTPS 服務器的更通用的解決方案是 TLS 服務器名稱指示 (SNI) 擴展 (RFC 6066),它允許瀏覽器在 SSL 握手期間傳遞請求的服務器名稱。通過此解決方案,服務器將知道應該使用哪個證書進行連接。然而,SNI 的瀏覽器支持有限。目前,它受到以下瀏覽器版本的支持:
- Opera 8.0
- MSIE 7.0 (但僅限于 Windows Vista 或更高版本)
- Firefox 2.0 和其他使用 Mozilla Platform rv:1.8.1 的瀏覽器
- Safari 3.2.1 (Windows 版本支持 Vista 或更高版本上的 SNI)
- Chrome (Windows 版本也支持 Vista 或更高版本上的 SNI) 只有域名可以在 SNI 中傳遞。但是,如果請求包含文字 IP 地址,一些瀏覽器將傳遞服務器的 IP 地址作為其名稱。最好不要依賴此功能。
為了在 NGINX 中使用 SNI,必須在構建 NGINX 二進制文件的 OpenSSL 庫中支持它,以及在運行時與之動態鏈接的庫中支持它。如果 OpenSSL 在構建時使用配置選項 --enable-tlsext,則支持 SNI 自版本 0.9.8f 起。從 OpenSSL 版本 0.9.8j 起,默認啟用此選項。如果 NGINX 是使用 SNI 支持構建的,則在使用 -V 開關運行 NGINX 時,NGINX 將顯示以下內容:
$ nginx -V
...
TLS SNI support enabled
...
但是,如果啟用 SNI 的 NGINX 在運行時動態鏈接到不支持 SNI 的 OpenSSL 庫,則 NGINX 將顯示警告:
NGINX was built with SNI support, however, now it is linked
dynamically to an OpenSSL library which has no tlsext support,
therefore SNI is not available
兼容性注意事項:
- 自版本 0.8.21 和 0.7.62 起,可以使用 -V 開關顯示 SNI 支持狀態。
- 自版本 0.7.14 起支持 listen 指令的 ssl 參數。在版本 0.8.21 之前,它只能與 default 參數一起指定。
- 自版本 0.5.23 起支持 SNI。
- 自版本 0.5.6 起支持共享 SSL 會話緩存。
- 版本 1.9.1 及更高版本:默認的 SSL 協議是 TLSv1、TLSv1.1 和 TLSv1.2(如果 OpenSSL 庫支持)。
- 自版本 0.7.65 和 0.8.19 及更高版本起,默認的 SSL 協議是 SSLv3、TLSv1、TLSv1.1 和 TLSv1.2(如果 OpenSSL 庫支持)。
- 在版本 0.7.64 和 0.8.18 及更早版本中,默認的 SSL 協議是 SSLv2、SSLv3 和 TLSv1。
- 自版本 1.0.5 起,默認的 SSL 密碼是 HIGH:!aNULL:!MD5。
- 自版本 0.7.65 和 0.8.20 及更高版本起,默認的 SSL 密碼是 HIGH:!ADH:!MD5。
- 自版本 0.8.19 起,默認的 SSL 密碼是 ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM。
- 自版本 0.7.64、0.8.18 和更早版本起,默認的 SSL 密碼是 ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP。