如何在 TCP 模式下使用 haproxy 传递自定义 SNI

How to pass the custom SNI with haproxy in TCP mode

我有一个 public ssl 端点 something.example.com,它需要使用 SNI 扩展。不能直接访问,所以想通过haproxy(设置在可以上网的服务器)

这不起作用: openssl s_client -connect something.example.com:443

这工作正常: openssl s_client -connect something.example.com:443 -servername something.example.com

我的 haproxy 配置(版本 1.8.24):

frontend my_frontend
    bind *:443
    mode tcp
    log global
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }
    use_backend my_backend

backend my_backend
    mode         tcp
    server       my-server something.example.com:443 sni str(something.example.com)

登录haproxy服务器后,我可以做:

curl -v https://something.example.com/可以看到SSL连接已经建立

但是在做 curl -v https://127.0.0.1/ 时我面对:

curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL 连接到 127.0.0.1:443

怎么了?我已经尝试终止 ssl,替换主机 header 但没有成功。我的理解是 SNI 本身与加密请求是分开的,并且通过上述配置我应该能够连接到后端。

PS。后台服务器不是我管理的。


编辑:如评论中所述,不可能在 TCP 模式配置中传递自定义 SNI。可以为 http 模式完成以下操作,使用 TLS 终止,以下工作正常:

frontend log_frontend
    bind *:443 ssl crt /etc/pki/tls/private/mycert.pem
    mode http
    log global
    option httplog
    option forwardfor
    use_backend log_backend

backend log_backend
    mode         http
    server       my-server something.example.com:443 ssl verify none sni str(something.example.com)

My understanding is that SNI itself is separated from ciphered request ...

SNI 是 SSL/TLS 握手的一部分,特别是客户端在握手开始时发送的 ClientHello。 不可能替换TLS握手的任何部分,包括SNI。相反,需要终止 TLS(这意味着需要适当的证书等),然后必须使用预期的 SNI 集创建新的 TLS 会话。

好吧,我假设您在调用 curl -v https://127.0.0.1/.

中也会收到类似“证书与主机名不匹配”的信息

我强烈建议告诉 curl 如何解决 something.example.com

curl -v --resolve something.example.com:443:127.0.0.1 https://something.example.com/

这篇博客有更详细的解释post https://daniel.haxx.se/blog/2018/04/05/curl-another-host/

顺便说一句:我强烈建议更新到最新的 haproxy 2.4 LTS 版本