我有一台装有多个 lxc 容器的主机。我试图根据域名直接授予容器 ssh 访问权限。为此,我尝试设置 HAProxy。可以使用 http 模式下的 ACL 轻松实现此目的。当我尝试使用基于 acls 的 ssh 的 tcp 模式进行相同操作时,我无法直接授予容器 ssh 访问权限。以下是我在 haproxy.cfg 中使用的代码片段。
listen SSHD :2200
mode tcp
acl is_apple hdr_dom i apple
acl is_orange hdr_dom -i orange
use_backend apple if is_apple
use_backend orange if is_orange
backend apple
mode tcp
server apple 10.0.3.221:22
backend orange
mode tcp
server orange 10.0.3.222:22
其中 apple.myhost.com 和 orange.myhost.com 是用于访问每个容器的域名。HTTP 代理可以很好地与这些 acl 配合使用,但我遇到了 SSH 流量问题。
我收到以下错误。
ssh_exchange_identification:远程主机关闭连接
答案1
我正在使用在 pfSense 上运行的 HAproxy 实例来实现您所寻找的目的。
我在这里写了详细的描述: https://julian.pawlowski.me/geeking-out-with-haproxy-on-pfsense-the-ultimate/
我将进一步完善此设置:端口 443 由 SSH、SSL/TLS 和 OpenVPN 流量共享,同时使用 X.509 客户端证书保护 SSH:
- 正常 HTTPS 流量(充当保护网络流量的正常反向代理)
- 使用 X509 用户证书身份验证的正常 HTTPS 流量
- OpenVPN 拨入流量
- TLS 隧道 SSH 流量,包括 X509 用户证书认证(SSLH 网关)
这还可以防止对 SSH 入口点的端口扫描。此外,它还可以帮助实现 IPv4 到 IPv6(反之亦然)的转换、灵活的协作和管理员的家庭办公解决方案等。
我知道有一个很棒的小工具 SSLH,但是由于 HAproxy 的强大功能,这个解决方案更加灵活。
这是 pfSense 根据我的博客文章创建的 haproxy.cfg 文件,供您参考:
global
maxconn 2000
stats socket /tmp/haproxy.socket level admin
uid 80
gid 80
nbproc 1
chroot /tmp/haproxy_chroot
daemon
tune.ssl.default-dh-param 2048
# Modern browser compatibility only as mentioned here:
# https://wiki.mozilla.org/Security/Server_Side_TLS
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
# Time-to-first-Byte (TTFB) value needs to be optimized based on
# the actual public certificate chain
# see https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
tune.ssl.maxrecord 1370
listen HAProxyLocalStats
bind 127.0.0.1:2200 name localstats
mode http
stats enable
stats admin if TRUE
stats uri /haproxy_stats.php?haproxystats=1
timeout client 5000
timeout connect 5000
timeout server 5000
frontend HTTP_redirect
bind 0.0.0.0:80 name 0.0.0.0:80
mode http
log global
option http-keep-alive
timeout client 30000
default_backend _ssl-redirect_http_ipvANY
frontend LAN_HTTPS
bind 10.108.2.1:443 name 10.108.2.1:443 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/LAN_HTTPS.pem
mode http
log global
option http-keep-alive
option forwardfor
acl https ssl_fc
reqadd X-Forwarded-Proto:\ http if !https
reqadd X-Forwarded-Proto:\ https if https
timeout client 30000
# Remove headers that expose security-sensitive information.
rspidel ^Server:.*$
rspidel ^X-Powered-By:.*$
rspidel ^X-AspNet-Version:.*$
default_backend gwsch01_http_ipvANY
frontend WAN_443-merged
bind 178.26.150.88:443 name 178.26.150.88:443
mode tcp
log global
timeout client 7200000
tcp-request inspect-delay 5s
# block SSLv3 as early as possible
acl sslv3 req.ssl_ver 3
tcp-request content reject if sslv3
tcp-request content accept if { req.ssl_hello_type 1 } or !{ req.ssl_hello_type 1 }
acl aclusr_custom_req.ssl_hello_type_201 req.ssl_hello_type 1
acl aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com req.ssl_sni -m end -i .ssh.example.com
acl aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com req.ssl_sni -m end -i .vpn.example.com
acl aclusr_custom_req.len_200 req.len 0
use_backend _WAN_HTTPS_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 !aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com !aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com
use_backend _WAN_HTTPS_auth_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.vpn.example.com
use_backend _openvpn_tcp_ipvANY if aclusr_custom_req.len_200 aclusr_custom_req.ssl_hello_type_201
use_backend _WAN_SSLH_tcp_ipvANY if aclusr_custom_req.ssl_hello_type_201 aclusr_custom_req.ssl_sni_20-m_20end_20-i_20.ssh.example.com
default_backend _none_tcp_ipvANY
frontend WAN_HTTPS
bind 127.0.0.1:2043 name 127.0.0.1:2043 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_HTTPS.pem accept-proxy npn http/1.1
mode http
log global
option http-keep-alive
option forwardfor
acl https ssl_fc
reqadd X-Forwarded-Proto:\ http if !https
reqadd X-Forwarded-Proto:\ https if https
timeout client 7200000
# Remove headers that expose security-sensitive information.
rspidel ^Server:.*$
rspidel ^X-Powered-By:.*$
rspidel ^X-AspNet-Version:.*$
default_backend _none_http_ipvANY
frontend WAN_HTTPS_auth-merged
bind 127.0.0.1:2044 name 127.0.0.1:2044 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_HTTPS_auth.pem ca-file /var/etc/haproxy/clientca_WAN_HTTPS_auth.pem verify required accept-proxy npn http/1.1
mode http
log global
option http-keep-alive
option forwardfor
acl https ssl_fc
reqadd X-Forwarded-Proto:\ http if !https
reqadd X-Forwarded-Proto:\ https if https
timeout client 7200000
# Remove headers that expose security-sensitive information.
rspidel ^Server:.*$
rspidel ^X-Powered-By:.*$
rspidel ^X-AspNet-Version:.*$
acl aclusr_host_matches_gwsch01.vpn.example.com hdr(host) -i gwsch01.vpn.example.com
use_backend gwsch01_http_ipvANY if aclusr_host_matches_gwsch01.vpn.example.com
default_backend _none_http_ipvANY
frontend WAN_SSLH-merged
bind 127.0.0.1:2022 name 127.0.0.1:2022 ssl no-sslv3 no-tls-tickets no-tlsv10 no-tlsv11 crt /var/etc/haproxy/WAN_SSLH.pem ca-file /var/etc/haproxy/clientca_WAN_SSLH.pem verify required accept-proxy npn ssh/2.0
mode tcp
log global
timeout client 7200000
acl aclusr_custom_ssl_fc_sni_reg_20gwsch01.ssh.example.com ssl_fc_sni_reg gwsch01.ssh.example.com
acl aclusr_custom_ssl_fc_npn_20-i_20ssh_2f2.0 ssl_fc_npn -i ssh/2.0
use_backend SSH_gwsch01_https_ipvANY if aclusr_custom_ssl_fc_sni_reg_20gwsch01.ssh.example.com aclusr_custom_ssl_fc_npn_20-i_20ssh_2f2.0
default_backend _none_https_ipvANY
backend _ssl-redirect_http_ipvANY
mode http
timeout connect 30000
timeout server 30000
retries 3
option httpchk
redirect scheme https code 301
backend gwsch01_http_ipvANY
mode http
rspadd Strict-Transport-Security:\ max-age=31536000;
rspirep ^(Set-Cookie:((?!;\ secure).)*)$ \1;\ secure if { ssl_fc }
timeout connect 3000
timeout server 7200000
retries 2
option httpchk
server gwsch01 127.0.0.1:8443 ssl verify none
backend _none_tcp_ipvANY
mode tcp
timeout connect 30000
timeout server 30000
retries 3
option httpchk OPTIONS /
server none 127.0.0.1:61235 check inter 1000 disabled
backend _WAN_HTTPS_tcp_ipvANY
mode tcp
timeout connect 30000
timeout server 7200000
retries 3
option httpchk
server WAN_HTTPS 127.0.0.1:2043 check-ssl verify none send-proxy
backend _WAN_HTTPS_auth_tcp_ipvANY
mode tcp
timeout connect 30000
timeout server 7200000
retries 3
option httpchk
server WAN_HTTPS_auth 127.0.0.1:2044 check-ssl verify none send-proxy
backend _openvpn_tcp_ipvANY
mode tcp
timeout connect 3000
timeout server 7200000
retries 2
option httpchk
server openvpn1 127.0.0.1:1194
backend _WAN_SSLH_tcp_ipvANY
mode tcp
timeout connect 30000
timeout server 7200000
retries 3
option httpchk
server WAN_SSLH 127.0.0.1:2022 check-ssl verify none send-proxy
backend _none_http_ipvANY
mode http
timeout connect 30000
timeout server 30000
retries 3
option httpchk OPTIONS /
server none 127.0.0.1:61235 check inter 1000 disabled
backend _none_https_ipvANY
mode tcp
timeout connect 30000
timeout server 30000
retries 3
option httpchk OPTIONS /
server none 127.0.0.1:61235 check inter 1000 disabled
backend SSH_gwsch01_https_ipvANY
mode tcp
timeout connect 3000
timeout server 7200000
retries 2
option httpchk
server ssh_gwsch01 127.0.0.1:22
答案2
这是不可能的。HTTP 协议不同,因为存在“虚拟主机”概念,并且 HAProxy 可以使用“Host:”标头区分不同的主机。SSH 没有类似的东西,因此 lxc-host 无法知道您尝试连接的容器。
但是您可以使用另一个 SSH 功能,称为“SSH 网关”。里面~/.ssh/authorized_keys
有一个command=
选项。首先从您的 lxc-host 设置基于密钥的 ssh 到 apple 和 orange。然后将这些行放入 lxc 的authorized_keys
文件中:
command="ssh -q -t user@apple $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAsomeB3N...== user@client
command="ssh -q -t user@orange $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAanotherB3N...== user@client
现在 lxs 主机可以根据客户端密钥自动连接到apple
和。orange
查看更多:
答案3
恐怕这是不可能的。SSH 协议不支持主机名。它只是连接到 IP(当然是在解析之后)并设置加密连接。没有“虚拟主机”的概念。
答案4
HAProxy 的博客提供了一种有趣的方法:使用 HAProxy 路由 SSH 连接 | 2020 年 12 月 21 日
为了将 SSH 连接路由到不同的服务器,您必须知道用户想要访问哪个服务器。如前所述,HAProxy 不会分析 SSH 协议,而且无论如何,该协议不会提供有关目的地的任何提示。因此,我们必须将连接包装在另一个有助于这一点的协议中。
我们将使用 TLS 协议及其 SNI 扩展以及 SSH ProxyCommand 功能。或者换句话说,我们将使用 TLS 包装我们的连接,但我们这样做只是为了利用 SNI,以便客户端可以告诉我们他们想要连接到哪个服务器。我们不会使用 TLS 的加密功能。
[...]
从您的客户端,您可以使用以下命令访问您的 SSH 服务器:
$ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server1" dummyName1 $ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server2" dummyName2 $ ssh -o ProxyCommand="openssl s_client -quiet -connect 172.16.0.10:2222 -servername server3" dummyName3
请注意,ssh 命令要求您发送要连接的服务器的名称。我们将其设置为 dummyName,因为我们改用 ProxyCommand 字段指定服务器名称。servername 开关允许您设置 SNI 字段内容。为了使此命令更短,请考虑创建 bash 别名或脚本。