我已经非常喜欢在我们的开发环境中使用 HTTP 反向代理,并且发现基于 DNS 的虚拟主机反向代理非常有用。在防火墙上只打开一个端口(和标准端口)使管理变得更加容易。
我想找到类似的东西来做 SSH 连接,但运气不太好。我不想简单地使用 SSH 隧道,因为这需要打开标准以外的端口范围。有什么可以做到这一点吗?
HAProxy 可以做到这一点吗?
我已经非常喜欢在我们的开发环境中使用 HTTP 反向代理,并且发现基于 DNS 的虚拟主机反向代理非常有用。在防火墙上只打开一个端口(和标准端口)使管理变得更加容易。
我想找到类似的东西来做 SSH 连接,但运气不太好。我不想简单地使用 SSH 隧道,因为这需要打开标准以外的端口范围。有什么可以做到这一点吗?
HAProxy 可以做到这一点吗?
鉴于协议的工作方式,我不相信基于名称的 SSH 是可能的。
这里有一些替代方案。
您可以做的是将响应端口 22 的主机设置为网关。然后可以配置ssh服务器根据key向里面转发请求。带有密钥的SSH网关示例
您可以调整您的客户端以将该主机用作代理。也就是说,它会通过 ssh 连接到网关主机,然后使用该主机与内部主机建立连接。带有客户端配置的SSH 代理。
您还可以在边缘设置一个简单的 http 代理。然后使用它来允许传入连接。通过HTTP 代理的SSH 。
显然,以上所有内容,确保正确配置和锁定网关非常重要。
在过去的 16 个月里,我一直在寻找解决这个问题的方法。但是每次我看,似乎不可能使用相关 RFC 中指定并由主要实现实现的 SSH 协议来做到这一点。
但是,如果您愿意使用稍作修改的 SSH 客户端,并且愿意以设计时不完全预期的方式使用协议,那么就有可能实现。更多关于这下面。
为什么不可能
客户端不会将主机名作为 SSH 协议的一部分发送。
它可能会将主机名作为 DNS 查找的一部分发送,但这可能会被缓存,并且从客户端通过解析器到权威服务器的路径可能不会穿过代理,即使这样做了,也没有将特定 DNS 查找与特定的 SSH 客户端。
您也无法对 SSH 协议本身做任何花哨的事情。您必须在没有看到客户端的 SSH 版本横幅的情况下选择服务器。您必须先向客户端发送横幅,然后它才会向代理发送任何内容。来自服务器的横幅可能会有所不同,您无法猜测,哪一个是正确的。
即使此横幅未加密发送,您也无法对其进行修改。该横幅的每一点都将在连接设置期间进行验证,因此您可能会导致连接失败。
对我来说,结论很清楚,必须在客户端进行一些更改才能使这种连接正常工作。
大多数变通方法都是将 SSH 流量封装在不同的协议中。还可以想象 SSH 协议本身的附加内容,其中客户端发送的版本横幅包含主机名。这可以与现有服务器保持兼容,因为部分横幅当前被指定为自由格式标识字段,并且虽然客户端通常在发送自己的版本横幅之前等待来自服务器的版本横幅,但协议确实允许客户端发送他们的横幅第一的。一些最新版本的 ssh 客户端(例如 Ubuntu 14.04 上的客户端)会在不等待服务器横幅的情况下发送横幅。
我不知道有任何客户端已采取措施在此横幅中包含服务器的主机名。我已经向OpenSSH 邮件列表发送了一个补丁来添加这样的功能。但它被拒绝了,因为它不想向任何窥探 SSH 流量的人透露主机名。由于秘密主机名从根本上与基于名称的代理的操作不兼容,因此不要指望很快就会看到 SSH 协议的官方 SNI 扩展。
真正的解决方案
最适合我的解决方案实际上是使用 IPv6。
使用 IPv6,我可以为每个服务器分配一个单独的 IP 地址,因此网关可以使用目标 IP 地址来找出将数据包发送到哪个服务器。SSH 客户端有时可能在网络上运行,其中获取 IPv6 地址的唯一方法是使用 Teredo。众所周知,Teredo 不可靠,但仅当连接的本机 IPv6 端使用公共 Teredo 中继时。可以简单地在网关上放置一个 Teredo 中继,您将在其中运行代理。Miredo 可以在不到五分钟的时间内安装和配置为继电器。
一种解决方法
您可以使用跳转主机/堡垒主机。此方法适用于您不希望将单个服务器的 SSH 端口直接暴露给公共 Internet 的情况。它确实具有减少 SSH 所需的面向外部的 IP 地址数量的额外好处,这就是它在这种情况下可用的原因。它是一种旨在出于安全原因添加另一层保护的解决方案这一事实并不能阻止您在不需要增加的安全性时使用它。
如果真正的解决方案 (IPv6) 超出您的能力范围,那么很难破解以使其工作
我将要描述的 hack 只能用作绝对的最后手段。在您考虑使用此 hack 之前,我强烈建议为您希望通过 SSH 从外部访问的每台服务器获取一个 IPv6 地址。使用 IPv6 作为访问 SSH 服务器的主要方法,并且仅当您需要从纯 IPv4 网络运行 SSH 客户端时才使用此 hack,而您对部署 IPv6 没有任何影响。
这个想法是客户端和服务器之间的流量需要是完全有效的 SSH 流量。但是代理只需要对数据包流有足够的了解就可以识别主机名。由于 SSH 没有定义发送主机名的方法,因此您可以考虑其他提供这种可能性的协议。
HTTP 和 HTTPS 都允许客户端在服务器发送任何数据之前发送主机名。现在的问题是,是否有可能构造一个字节流,它同时作为 SSH 流量和 HTTP 和 HTTPS 都有效。HTTPs 它几乎是一个非入门者,但 HTTP 是可能的(对于 HTTP 的足够自由的定义)。
对您来说,这看起来像 SSH 还是 HTTP?它是 SSH 并且完全符合 RFC(除了一些二进制字符被 SF 渲染弄乱了一点)。
SSH 版本字符串包含一个注释字段,上面的值为
/ HTTP/1.1
。换行后 SSH 有一些二进制数据包数据。第一个数据包是MSG_SSH_IGNORE
客户端发送的消息,被服务器忽略。要忽略的有效负载是:如果 HTTP 代理在其接受的内容上足够自由,那么相同的字节序列将被解释为调用的 HTTP 方法
SSH-2.0-OpenSSH_6.6.1
,而忽略消息开头的二进制数据将被解释为 HTTP 标头名称。代理既不理解 HTTP 方法也不理解第一个标头。但它可以理解
Host
标头,这是它找到后端所需的全部内容。为了让它工作,代理的设计原则是它只需要理解足够的 HTTP 来找到后端,一旦找到后端,代理将简单地传递原始字节流并离开真正的终止由后端完成的 HTTP 连接。
对 HTTP 代理做这么多假设听起来有点牵强。但是,如果您愿意安装一个旨在支持 SSH 的新软件,那么对 HTTP 代理的要求听起来还不错。
在我自己的情况下,我发现这种方法可以在已经安装的代理上工作,而无需更改代码、配置或其他方式。这是针对仅使用 HTTP 而没有考虑 SSH 编写的代理。
概念证明客户端和代理。(免责声明代理是我运营的服务。一旦确认任何其他代理支持此使用,请随时更换链接。)
这个黑客的注意事项
街区里来了一个新孩子。SSH piper 将根据预定义的用户名路由您的连接,该用户名应映射到内部主机。这是反向代理环境下的最佳解决方案。
SSH 吹笛者在 github
我的第一个测试证实,SSH 和 SFTP 都可以工作。
我不相信这是可能的,至少按照你描述的方式,尽管我很想被证明是错误的。客户端似乎没有发送它希望连接的主机名(至少是明确的)。SSH 连接的第一步似乎是设置加密。
此外,您还会遇到主机密钥验证问题。SSH 客户端将根据 IP 地址和主机名验证密钥。您将拥有多个具有不同密钥的主机名,但您连接的 IP 相同。
一个可能的解决方案是拥有一个“堡垒”主机,客户端可以通过 ssh 进入该机器,获得一个正常的(或如果需要的话受到限制)shell,然后可以从那里通过 ssh 进入内部主机。
我想知道是否无法修改具有代理模式的 Honeytrap(低交互蜜罐)来实现这一目标。
这个蜜罐能够将任何协议转发到另一台计算机。添加基于名称的虚拟主机系统(由 Apache 实现)可以使其成为任何协议的完美反向代理,不是吗?
我没有实现这一目标的技能,但也许这可能是一个伟大的项目。
由于 ssh 的工作方式,我认为这是不可能的。与 https 类似,您必须为不同的主机提供不同的(外部)IP,因为网关不知道您要连接到哪里,因为 ssh 中的所有内容都已加密
您可以在服务器端使用 HAProxy,在客户端使用 ssh + openssl 通过 HTTPS 进行 SSH。此方法使用 HTTPS SNI 来识别目标服务器。
客户端命令示例:
ssh -o ProxyCommand="openssl s_client -quiet -connect <proxy IP>:2222 -servername server1" dummyName1
dummyName1
在上面的命令中纯粹用作占位符,因为 ssh 需要主机名,但 openssl 在这里完成所有连接工作基本 HAProxy 配置:
更多细节和安全改进可以在源代码中找到