轉自:https://maoxian.de/2017/12/1471.html
這兩天在檢查一台 Nginx 配置的時候,遇到了一個極端詭異的問題。一段很通用的配置,配在這個服務器上,就會 100% 導致 Chrome 報 ERR_SSL_PROTOCOL_ERROR 。但是這段配置非常的通用,是用 Mozilla 提供的工具生成的。
而且在 iPhone 的 Safari 上訪問又是完全正常的,服務器日志也看不到任何錯誤。看到的請求相應碼也是完全正確的 200 。
先貼出配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
listen 443 ssl http2;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# modern configuration. tweak to your needs.
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
|
可以看到是在 Mozilla 網站上選擇 和 Modern 生成出來的配置。
在測試過程中,排查了各種問題,包括但不限於 SSL 證書問題,HTTP Basic Auth 問題,http2 問題等等,然而都沒有解決這個現象。
一次偶然的嘗試,發現只要注釋掉我給這個 server 特殊配置的這段邏輯,使用服務器通用的 ssl.ngx 文件中的 SSL 配置,就不會出現問題。於是開始先使用 ssl.ngx 文件中的配置,然后逐行替換,來查找具體出現問題的配置。
終於,當我將配置中的這行加上時,問題出現了:
1
|
ssl_session_tickets off;
|
於是以這個配置作為關鍵字搜索,找到了這么一篇文章:
https://community.letsencrypt.org/t/errors-from-browsers-with-ssl-session-tickets-off-nginx/18124/5
I’m posting this here both because this question was recently asked and because it took me many hours of troubleshooting to figure out the issue as while I found several references to the problem on Google, no one seemed to have a real solution. So here it is:
ssl_session_tokens off breaks if it’s not set the same for all ssl-enabled server{} blocks. So if you have 2 server configurations and and you have ssl_server_tokens set to on in one (which is the default so it counts even if you omit it) and set to off in another, it will break the one where it’s set to off in certain browsers. The easiest way to resolve this, unless you have multiple http{} blocks, is to just set it to off in the http{} block. I have not tested to see if you you can have different settings in different http{} blocks as I haven’t had need to set up more than one http{} block.
For others looking for this issue, I want to add that Chrome will respond with: ERR_SSL_PROTOCOL_ERROR while Firefox responds with: SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET and curl responds with: gnutls_handshake() failed: An unexpected TLS packet was received. IE seemed to work, surprisingly.
簡單翻譯一下,這里是說,如果你的 nginx 開了多個 https 的 server,其中某些 server 沒有配置ssl_server_tokens off; ,而有些 server 配置了這個選項,那么就會導致沒有手動 off 的 server 采用默認值 on,而手動 off 掉的 server 采用 off。這種情況會導致 nginx 和瀏覽器之間的握手出現問題,從而導致 Chrome 報出 ERR_SSL_PROTOCOL_ERROR ,FireFox 則會報出 SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET 。
那么解決方法也很簡單,只要在所有的 server 塊統一這個配置就好了。要么都設置為 on,要么都設置為 off,問題解決。目前沒有嘗試多個 http 塊隔離兩個 server,建議還是將這個配置統一一下。
https://maoxian.de/