客户端访问Nginx HTTPS出现ERR_CONNECTION_CLOSED

背景

最近在Nginx接入了一个域名,配置好https证书之后,在本地浏览器访问发现提示ERR_CONNECTION_CLOSED。把排查过程大概记录一下,主要是贴一下这里的一些报错。如果之后有人也遇到了,搜两行日志也许能找到这篇文章,省下点时间。

太长不看版

在服务器本地用curl测试能否访问,若不能,则不是本文要解决的问题;若能,则有可能是云服务提供商发送了TCP重置包阻断了连接。是不是没有备案?是不是存在攻击行为被封禁了?

排查过程

Nginx配置好之后,先用Chrome访问一下,发现提示ERR_CONNECTION_CLOSED;用Safari访问一下,发现提示“Safari无法与服务器建立安全连接”。奇怪啊,用curl测试:

foo@macbook ~ % curl -v "https://www.mysite.com/"       
*   Trying 47.93.**.**:443...
* Connected to www.mysite.com (47.93.**.**) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* OpenSSL SSL_connect: Connection reset by peer in connection to www.mysite.com:443 
* Closing connection 0
curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to www.mysite.com:443 

看了一下连接的IP没问题,DNS没问题,端口没问题。再用openssl测一下(一定要带-servername否则默认不发送SNI):

foo@macbook ~ % openssl s_client -debug -connect 47.93.**.**:443 -servername www.mysite.com
CONNECTED(00000003)
write to 0x600003010000 [0x157017c00] (320 bytes => 320 (0x140))
0000 - 16 03 01 01 3b 01 00 01-37 03 03 64 7e 74 e6 ed   ....;...7..d~t..
0010 - 54 8a f1 4e b9 86 bd 0f-96 6c 4a 6a ef 85 72 19   T..N.....lJj..r.
0020 - 4e ee 8d 47 7f e5 d8 68-42 6e b7 20 35 bb 2c 50   N..G...hBn. 5.,P
0030 - 56 6e c3 74 eb 3d c9 ea-37 e2 40 c8 a5 59 f2 4a   [email protected]
0040 - 44 61 53 d8 71 6a 77 71-f6 ed ab 46 00 3e 13 02   DaS.qjwq...F.>..
0050 - 13 03 13 01 c0 2c c0 30-00 9f cc a9 cc a8 cc aa   .....,.0........
0060 - c0 2b c0 2f 00 9e c0 24-c0 28 00 6b c0 23 c0 27   .+./...$.(.k.#.'
0070 - 00 67 c0 0a c0 14 00 39-c0 09 c0 13 00 33 00 9d   .g.....9.....3..
0080 - 00 9c 00 3d 00 3c 00 35-00 2f 00 ff 01 00 00 b0   ...=.<.5./......
0090 - 00 00 00 13 00 11 00 00-0e 77 77 77 2e 6d 79 73 .........www.mys
00a0 - 69 74 65 2e 63 6f 6d 00-0b 00 04 03 00 01 02 00   ite.com.........
00b0 - 0a 00 16 00 14 00 1d 00-17 00 1e 00 19 00 18 01   ................
00c0 - 00 01 01 01 02 01 03 01-04 00 23 00 00 00 16 00   ..........#.....
00d0 - 00 00 17 00 00 00 0d 00-2a 00 28 04 03 05 03 06   ........*.(.....
00e0 - 03 08 07 08 08 08 09 08-0a 08 0b 08 04 08 05 08   ................
00f0 - 06 04 01 05 01 06 01 03-03 03 01 03 02 04 02 05   ................
0100 - 02 06 02 00 2b 00 09 08-03 04 03 03 03 02 03 01   ....+...........
0110 - 00 2d 00 02 01 01 00 33-00 26 00 24 00 1d 00 20   .-.....3.&.$... 
0120 - 29 4e cf fa 6e ff 7e 4a-8d 0f 0b 2b 65 bf e6 bc   )N..n.~J...+e...
0130 - 87 0d 69 65 26 35 2b 4c-c0 3a 13 a1 5b 31 9b 15   ..ie&5+L.:..[1..
read from 0x600003010000 [0x15700e803] (5 bytes => -1)
write:errno=54
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 320 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
read from 0x600003010000 [0x15600e000] (8192 bytes => 0)

看了一下,连接中断的时候,服务器还没offer证书,刚到Client hello阶段就断了。难道是Nginx配置有问题所以发送了重置包?上服务器打开nginx调试日志,再请求一下:

[foo@bar-server nginx]# tail -fn0 /var/log/nginx/error.log
2022/03/16 14:14:40 [debug] 15709#0: epoll: fd:8 ev:0001 d:0000561CDAD9EE30
2022/03/16 14:14:40 [debug] 15710#0: epoll: fd:8 ev:0001 d:0000561CDAD9EE30
2022/03/16 14:14:40 [debug] 15708#0: epoll: fd:8 ev:0001 d:0000561CDAD9EE30
2022/03/16 14:14:40 [debug] 15708#0: timer delta: 39727
2022/03/16 14:14:40 [debug] 15710#0: timer delta: 39726
2022/03/16 14:14:40 [debug] 15708#0: worker cycle
2022/03/16 14:14:40 [debug] 15710#0: worker cycle
2022/03/16 14:14:40 [debug] 15709#0: timer delta: 39727
2022/03/16 14:14:40 [debug] 15708#0: epoll timer: -1
2022/03/16 14:14:40 [debug] 15710#0: epoll timer: -1
2022/03/16 14:14:40 [debug] 15709#0: worker cycle
2022/03/16 14:14:40 [debug] 15709#0: epoll timer: 60000
2022/03/16 14:14:40 [debug] 15709#0: epoll: fd:17 ev:0001 d:0000561CDAD9F1F0
2022/03/16 14:14:40 [debug] 15709#0: *1 SSL ALPN supported by client: h2
2022/03/16 14:14:40 [debug] 15709#0: *1 SSL ALPN supported by client: http/1.1
2022/03/16 14:14:40 [debug] 15709#0: *1 SSL ALPN selected: h2
2022/03/16 14:14:40 [debug] 15709#0: *1 SSL_do_handshake: -1
2022/03/16 14:14:40 [debug] 15709#0: *1 SSL_get_error: 5
2022/03/16 14:14:40 [info] 15709#0: *1 peer closed connection in SSL handshake (104: Connection reset by peer) while SSL handshaking, client: 100.1.**.**, server: 0.0.0.0:443
2022/03/16 14:14:40 [debug] 15709#0: *1 close http connection: 17
2022/03/16 14:14:40 [debug] 15709#0: *1 event timer del: 17: 3578671
2022/03/16 14:14:40 [debug] 15709#0: *1 reusable connection: 0
2022/03/16 14:14:40 [debug] 15709#0: *1 free: 0000561CDAD85B20, unused: 112
2022/03/16 14:14:40 [debug] 15709#0: timer delta: 22
2022/03/16 14:14:40 [debug] 15709#0: worker cycle
2022/03/16 14:14:40 [debug] 15709#0: epoll timer: -1

日志显示nginx在刚收到SSL ALPN的阶段时,连接被重置。为了进一步排除nginx配置问题,直接在服务器本地用curl测试访问一下,一切正常。

这下确认不是ng的配置问题,那现在服务端客户端都说被重置,难道是有防火墙之类的东西在中间阻断了连接吗?检查了一下iptables/firewalld之类的都没有开启,如果是云端安全组之类的原因,那应该根本连不上,而不是握手握一半。

测试到这里,仿佛陷入僵局,怎么也想不出来了。吃了个饭,回来试了一下不带ssl,直接访问http,提示域名未备案被阻断连接… 瞬间开窍了,应该是阿里云给阻断了...

结论

在TLS握手的client hello阶段,客户端会发送多种信息,其中就包括SNI和ALPN。一般情况下,只要Nginx支持,客户端就会发送SNI。SNI用于在TLS握手之前将要认证的域名明文发送给服务器,便于服务器正确提供不同vhost的证书。然而,在SNI把域名明文发给服务器的过程中,同时也可以被中间人(云服务提供商)监听到。只要这个域名没有备案,他们就会出手断掉。

这次排查花的原因比较久。原因有几个

  1. nginx上本来有别的域名,一开始出问题的时候还以为是SNI的原因。
  2. 这个域名本来是在其他的提供商备过案的,这次解析到阿里云,根本没往备案的方向想。事实上跨提供商备案是不通用的,必须重新接入备案。
  3. 记得以前的各种云好像只封标准HTTP 80端口的备案。有一段时间没有申新域名,不知从什么时候开始,HTTPS竟也开始封了。

后话

事实上,即使禁用SNI,也只能确保客户端不发送明文的域名信息。当服务器出示证书的时候,明文的证书里依然会包含域名(Common Name)。虽然现在有扩展加密SNI(ESNI)的技术,但是并不能完全保护HTTPS域名的信息不泄露。而且这个ESNI因为一些不能说的原因,可能难以快速普及。

参考资料

  1. What happens in a TLS handshake?
  2. A walk-through of an SSL handshake
  3. 阿里云未备案网站的封锁策略是?
  4. 关于云服务商屏蔽未备案域名的问题
  5. 什么是加密的 SNI(ESNI)?

你可能感兴趣的:(客户端访问Nginx HTTPS出现ERR_CONNECTION_CLOSED)