我已经在开发环境中非常喜欢HTTP反向代理,并且发现基于DNS的虚拟主机反向代理非常有用。在防火墙上仅打开一个端口(和标准端口)可以简化管理。
我想找到类似的方法来进行SSH连接,但是运气不高。我不想简单地使用SSH隧道,因为这需要打开标准端口以外的端口范围。有什么可以做到的吗?
HAProxy可以这样做吗?
我已经在开发环境中非常喜欢HTTP反向代理,并且发现基于DNS的虚拟主机反向代理非常有用。在防火墙上仅打开一个端口(和标准端口)可以简化管理。
我想找到类似的方法来进行SSH连接,但是运气不高。我不想简单地使用SSH隧道,因为这需要打开标准端口以外的端口范围。有什么可以做到的吗?
HAProxy可以这样做吗?
Answers:
在过去的16个月里,我一直在寻找解决此问题的解决方案。但是每次我看时,似乎都不可能使用相关RFC中指定并由主要实现方式实现的SSH协议来做到这一点。
但是,如果您愿意使用经过稍微修改的SSH客户端,并且愿意以设计时未完全预期的方式使用协议,则可以实现。在下面的更多内容。
为什么不可能
客户端不会将主机名作为SSH协议的一部分发送。
它可能会将主机名作为DNS查找的一部分发送,但是可能会被缓存,并且从客户端到解析器的路径到权威服务器的路径可能不会穿过代理,即使这样做,也没有可靠的方式将特定的DNS查找与特定的SSH客户端。
SSH协议本身也无法实现。您必须选择一台服务器,甚至没有从客户端看到SSH版本标语。您必须先将标语发送给客户端,然后才能将任何内容发送给代理。来自服务器的标语可能会有所不同,并且您没有机会猜测,哪一个是正确使用的。
即使未加密发送此横幅,您也无法对其进行修改。连接建立期间将验证该横幅的每一部分,因此您可能会导致连接失败。
对我来说,结论很清楚,必须在客户端进行一些更改才能使这种连接正常工作。
大多数解决方法是将SSH流量封装在其他协议中。还可以想象到SSH协议本身的增加,其中客户端发送的版本标语包括主机名。这可以与现有服务器保持兼容,因为横幅的一部分当前被指定为自由格式标识字段,并且尽管客户端通常在发送自己的横幅之前先等待服务器的版本横幅,但协议确实允许客户端发送横幅第一。某些最新版本的ssh客户端(例如Ubuntu 14.04上的ssh客户端)的确发送了横幅,而无需等待服务器横幅。
我不知道任何客户端,该客户端已采取措施在此横幅中包含服务器的主机名。我已经向OpenSSH邮件列表发送了补丁,以添加这样的功能。但这是因为不希望向侦听SSH流量的任何人透露主机名而被拒绝。由于秘密主机名从根本上与基于名称的代理的操作不兼容,因此不要指望很快就可以看到SSH协议的正式SNI扩展。
真正的解决方案
最适合我的解决方案实际上是使用IPv6。
使用IPv6,我可以为每个服务器分配一个单独的IP地址,因此网关可以使用目标IP地址来查找将数据包发送到哪个服务器。SSH客户端有时可能在网络上运行,而要获得IPv6地址的唯一方法是使用Teredo。已知Teredo是不可靠的,但是仅当连接的本机IPv6端使用公共Teredo中继时才如此。只需将Teredo中继放在运行代理的网关上即可。Miredo可以在不到五分钟的时间内安装和配置为中继。
解决方法
您可以使用跳转主机/堡垒主机。此方法适用于您不想将单个服务器的SSH端口直接公开到公共Internet的情况。它确实具有减少SSH所需的面向外部IP地址的数量的额外好处,这就是在这种情况下可以使用它的原因。出于安全原因,它是旨在添加另一层保护的解决方案,这一事实并不能阻止您在不需要增加的安全性时使用它。
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
如果真正的解决方案(IPv6)不在您的范围之内,则可以尝试使之工作
我将要描述的hack只能用作绝对不得已的手段。在您甚至没有考虑使用此hack之前,我强烈建议您为要通过SSH从外部访问的每个服务器获取一个IPv6地址。将IPv6用作访问SSH服务器的主要方法,并且仅在需要从仅支持IPv4的网络运行SSH客户端(对部署IPv6没有任何影响)时才使用此技巧。
这个想法是,客户端和服务器之间的流量必须是完全有效的SSH流量。但是代理只需要对数据包流有足够的了解就可以识别主机名。由于SSH没有定义发送主机名的方法,因此您可以考虑使用其他确实提供这种可能性的协议。
HTTP和HTTPS都允许客户端在服务器发送任何数据之前发送主机名。现在的问题是,是否可以构造同时作为SSH流量以及HTTP和HTTPS有效的字节流。HTTPs几乎是一个入门,但HTTP是可能的(对于HTTP足够自由的定义)。
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
对您来说,这看起来像SSH还是HTTP?它是SSH且完全符合RFC(除了一些二进制字符被SF渲染弄乱了一点)。
SSH版本字符串包含一个注释字段,该字段中的值为/ HTTP/1.1
。在换行符之后,SSH具有一些二进制数据包数据。第一个数据包是MSG_SSH_IGNORE
客户端发送的,服务器忽略的消息。要忽略的有效载荷是:
:
Host: example.com
如果HTTP代理在接受的内容上足够自由,那么相同的字节序列将被解释为HTTP方法SSH-2.0-OpenSSH_6.6.1
,而忽略消息开始处的二进制数据将被解释为HTTP标头名称。
代理不会理解HTTP方法或第一个标头。但是它可以理解Host
标题,这是找到后端所需要的。
为了使此工作正常进行,必须按以下原则设计代理:只需要了解足够的HTTP即可找到后端,一旦找到后端,代理将简单地传递原始字节流并保留实际终止HTTP连接由后端完成。
对HTTP代理进行如此多的假设听起来有点费力。但是,如果您愿意安装旨在支持SSH的新软件,那么对HTTP代理的要求听起来不会太糟糕。
就我自己而言,我发现此方法可以在已安装的代理上工作,而无需更改代码,配置或其他方式。这是针对只使用HTTP而没有考虑SSH编写的代理。
概念客户和代理的证明。(免责声明代理是我提供的服务。一旦确认有任何其他代理支持此用法,请随时替换链接。)
此hack的警告
the gateway can use the destination IP address to find out which server to send the packet to
IPv6解决方案中的含义?
-J
,因此您可以使用:ssh -J user1 @ front user2 @ target
我不相信这是可能的,至少按照您的描述,尽管我很想证明自己是错误的。客户端似乎没有发送它希望连接的主机名(至少是明文形式的)。SSH连接的第一步似乎是设置加密。
此外,您还会遇到主机密钥验证问题。SSH客户端将根据IP地址和主机名验证密钥。您将拥有多个具有不同密钥的主机名,但是您要连接的IP地址相同。
可能的解决方案是拥有一个“堡垒”主机,客户端可以在其中插入该计算机,获取常规的(或需要的话受限制的)shell,然后可以从那里将其装入内部主机。
街上有个新孩子。SSH管道程序将根据预定义的用户名路由您的连接,该用户名应映射到内部主机。这是反向代理上下文中的最佳解决方案。
我的第一个测试确认,SSH和SFTP都可以工作。
debug1: Sending subsystem: sftp
。事实证明,前衬衫不支持子系统。您支持SSh吹笛者的下摆吗?
我想知道是否不能修改具有代理模式的Honeytrap(低交互蜜罐)来实现这一目标。
此蜜罐能够将任何协议转发到另一台计算机。添加基于名称的虚拟主机系统(由Apache实施)可以使其成为任何协议的理想反向代理。
我没有达到目标的技能,但也许这可能是一个伟大的项目。