使用IO::Socket::SSL经HTTP隧道代理连接HTTPS服务的可行方案
软件环境
自行编译部署的Perl-5.34.1(非perlbrew环境)
Net-SSLeay 1.92
IO-Socket-SSL 2.074
抱歉无法提供pastebin链接,当前位置无相关访问权限
当前使用的“代理隧道”为HTTP协议,但本地防火墙规则要求所有连接必须以HTTPS方式发起。
此前尝试两种方案规避问题:一是通过IO::Socket::INET建立普通连接后升级为SSL连接,二是创建SSL套接字时不立即握手、延后执行握手流程,两种方案均无法解决代理隧道连接异常,因此推测该场景需要使用独立套接字实现。
问题
如何使用IO::Socket::SSL(可搭配其他CPAN模块)实现HTTP代理隧道场景下的HTTPS服务正常连接?希望尽可能避免使用LWP::UserAgent,目前暂未在CPAN上找到可解决该问题的相关模块。
测试示例
(1) curl --verbose 测试
在RHEL6平台尝试通过内网防火墙连接目标HTTPS服务,该服务为用于验证SSL连通性的“hello world”测试服务,内网其他主机上的Firefox浏览器可正常访问(与本次IO::Socket::SSL测试主机非同台设备)。使用curl连接失败,返回代理隧道相关报错(主机名、IP已做脱敏处理),执行日志如下:
curl --verbose 'https://foo.bar.net/bim/bam' * Trying 10.10.10.10... * Connected to 10.10.10.10 (10.10.10.10) port 80 (#0) * Establish HTTP proxy tunnel to foo.bar.net:443 > CONNECT foo.bar.net:443 HTTP/1.1 > Host: foo.bar.net:443 > User-Agent: curl/7.44.0 > Proxy-Connection: Keep-Alive > < HTTP/1.1 503 Service Unavailable < Cache-Control: no-cache < X-XSS-Protection: 1 < Connection: close < Content-Type: text/html; charset=utf-8 < Content-Length: 750 < Pragma: no-cache < Set-Cookie: frobnicate; path=/; Httponly < * Received HTTP code 503 from proxy after CONNECT * Closing connection 0 curl: (56) Received HTTP code 503 from proxy after CONNECT
(2) 单独使用IO::Socket::SSL测试
代理隧道并非透明模式,直接创建IO::Socket::SSL套接字连接失败,调试日志如下:
$IO::Socket::SSL::DEBUG = 3; IO::Socket::SSL->new ( PeerAddr => $auth , PeerPort => 'https' )
对应调试输出:
DB<1> DEBUG: .../IO/Socket/SSL.pm:2961: new ctx 54179840 DEBUG: .../IO/Socket/SSL.pm:704: socket not yet connected DEBUG: .../IO/Socket/SSL.pm:706: socket connected DEBUG: .../IO/Socket/SSL.pm:729: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:771: using SNI with hostname foo.bar.com DEBUG: .../IO/Socket/SSL.pm:806: request OCSP stapling DEBUG: .../IO/Socket/SSL.pm:836: call Net::SSLeay::connect DEBUG: .../IO/Socket/SSL.pm:2852: did not get stapled OCSP response DEBUG: .../IO/Socket/SSL.pm:2805: ok=0 [0] /C=US/O=DigiCert Inc/CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1/C=US/ST=New York/L=Someplace/O=Foo Bar, Inc./CN=foo.bar.com DEBUG: .../IO/Socket/SSL.pm:839: done Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:842: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:842: local error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: .../IO/Socket/SSL.pm:845: fatal SSL error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: ...inux/IO/Socket.pm:50: ignoring less severe local error 'IO::Socket::IP configuration failed', keep 'SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed' DEBUG: .../IO/Socket/SSL.pm:3010: free ctx 54179840 open= DEBUG: .../IO/Socket/SSL.pm:3014: free ctx 54179840 callback DEBUG: .../IO/Socket/SSL.pm:3021: OK free ctx 54179840 not ok 1 - Direct: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed.
IO::Socket::SSL的POD文档中提供了两种可能规避该问题的方案,分别测试如下:
(3) INET套接字升级为SSL方案
方案示例代码:
my $sock = IO::Socket::INET->new(...) or die $!; IO::Socket::SSL->start_SSL($sock,%sslargs) or die $SSL_ERROR; $sock->stop_SSL or die $SSL_ERROR;
实际测试代码:
my $sock = IO::Socket::INET->new ( PeerAddr => $auth , PeerPort => 'https' ) or die "INET: $IO::Socket::errstr\n"; IO::Socket::SSL->start_SSL ( $sock , 'PeerPort' => 'https' ) or die "SSL: $SSL_ERROR\n";
对应调试日志:
DB<1> DEBUG: .../IO/Socket/SSL.pm:2961: new ctx 58865232 DEBUG: .../IO/Socket/SSL.pm:1561: start handshake DEBUG: .../IO/Socket/SSL.pm:729: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:774: not using SNI because hostname is unknown DEBUG: .../IO/Socket/SSL.pm:806: request OCSP stapling DEBUG: .../IO/Socket/SSL.pm:836: call Net::SSLeay::connect DEBUG: .../IO/Socket/SSL.pm:2852: did not get stapled OCSP response DEBUG: .../IO/Socket/SSL.pm:2805: ok=0 [0] /C=US/O=DigiCert Inc/CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1/C=US/ST=New York/L=Edgewood/O=Foo Bar, Inc./CN=foo.bar.com DEBUG: .../IO/Socket/SSL.pm:839: done Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:842: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:842: local error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: .../IO/Socket/SSL.pm:845: fatal SSL error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: .../IO/Socket/SSL.pm:1984: downgrading SSL only, not closing socket DEBUG: .../IO/Socket/SSL.pm:3010: free ctx 58865232 open= DEBUG: .../IO/Socket/SSL.pm:3014: free ctx 58865232 callback DEBUG: .../IO/Socket/SSL.pm:3021: OK free ctx 58865232 BRCC::FM::Test::(bin/socket-check:55):
(4) 延迟SSL握手方案
创建SSL套接字时设置不立即握手,连接建立后手动触发握手,测试代码如下:
my $sock = IO::Socket::SSL->new ( PeerAddr => $auth , PeerPort => 'https' , SSL_startHandshake => 0 ) or die "SSL: $SSL_ERROR"; $sock->connect_SSL or die "Connect: $SSL_ERROR";
对应调试日志:
DB<1> DEBUG: .../IO/Socket/SSL.pm:2961: new ctx 43293280 DEBUG: .../IO/Socket/SSL.pm:704: socket not yet connected DEBUG: .../IO/Socket/SSL.pm:706: socket connected BRCC::FM::Test::(bin/socket-check:50): 50: $sock->connect_SSL 51: or die "Connect: $SSL_ERROR"; DB<1> DEBUG: .../IO/Socket/SSL.pm:729: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:771: using SNI with hostname foo.bar.com DEBUG: .../IO/Socket/SSL.pm:806: request OCSP stapling DEBUG: .../IO/Socket/SSL.pm:836: call Net::SSLeay::connect DEBUG: .../IO/Socket/SSL.pm:2852: did not get stapled OCSP response DEBUG: .../IO/Socket/SSL.pm:2805: ok=0 [0] /C=US/O=DigiCert Inc/CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1/C=US/ST=New York/L=Edgewood/O=Foo Bar, Inc./CN=foo.bar.com DEBUG: .../IO/Socket/SSL.pm:839: done Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:842: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:842: local error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: .../IO/Socket/SSL.pm:845: fatal SSL error: SSL connect attempt failed error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed DEBUG: .../IO/Socket/SSL.pm:3010: free ctx 43293280 open= DEBUG: .../IO/Socket/SSL.pm:3014: free ctx 43293280 callback DEBUG: .../IO/Socket/SSL.pm:3021: OK free ctx 43293280
(5) 关闭证书验证+自动握手测试
设置SSL_verify_mode为0关闭证书验证、开启自动握手,仍未连接成功,测试代码如下:
my $sock = IO::Socket::SSL->new ( PeerAddr => $auth , PeerPort => 'https' , SSL_startHandshake => 1 , SSL_verify_mode => 0 ) or die "SSL: $SSL_ERROR";
对应调试日志:
DB<1> DEBUG: .../IO/Socket/SSL.pm:2537: new ctx 50291120 DEBUG: .../IO/Socket/SSL.pm:529: socket not yet connected DEBUG: .../IO/Socket/SSL.pm:531: socket connected DEBUG: .../IO/Socket/SSL.pm:553: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:586: using SNI with hostname foo.bar.com DEBUG: .../IO/Socket/SSL.pm:653: Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:1769: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:1774: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: .../IO/Socket/SSL.pm:659: fatal SSL error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: .../IO/Socket/SSL.pm:1758: IO::Socket::INET configuration failed DEBUG: .../IO/Socket/SSL.pm:2570: free ctx 50291120 open=50291120 DEBUG: .../IO/Socket/SSL.pm:2582: OK free ctx 50291120
(6) 关闭证书验证+延迟握手测试
关闭证书验证同时使用延迟握手逻辑,返回相同错误,测试代码如下:
my $sock = IO::Socket::SSL->new ( PeerAddr => $auth , PeerPort => 'https' , SSL_startHandshake => 0 , SSL_verify_mode => 0 ) or die "SSL: $SSL_ERROR"; $sock->connect_SSL or die "Connect: $SSL_ERROR";
对应调试日志:
DB<1> DEBUG: .../IO/Socket/SSL.pm:2537: new ctx 42963376 DEBUG: .../IO/Socket/SSL.pm:529: socket not yet connected DEBUG: .../IO/Socket/SSL.pm:531: socket connected BRCC::FM::Test::(bin/socket-check:51): 51: $sock->connect_SSL 52: or die "Connect: $SSL_ERROR"; DB<1> DEBUG: .../IO/Socket/SSL.pm:553: ssl handshake not started DEBUG: .../IO/Socket/SSL.pm:586: using SNI with hostname foo.bar.net DEBUG: .../IO/Socket/SSL.pm:653: Net::SSLeay::connect -> -1 DEBUG: .../IO/Socket/SSL.pm:1769: SSL connect attempt failed DEBUG: .../IO/Socket/SSL.pm:1774: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: .../IO/Socket/SSL.pm:659: fatal SSL error: SSL connect attempt failed error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure DEBUG: .../IO/Socket/SSL.pm:2570: free ctx 42963376 open=42963376 DEBUG: .../IO/Socket/SSL.pm:2582: OK free ctx 42963376
内容的提问来源于stack exchange,提问作者lembark

