SSH over HTTPS

长话短说:要通过 HTTPS 传输 SSH,需要对客户端和服务器端进行如下调整:

比如,在~/.ssh/config 中:

# $ cat .ssh/config
Host ssh-via-https
        ProxyCommand ~/.ssh/https-tunnel.bash
        # some firewalls aggressively close idle TCP connections
        ServerAliveInterval 30

我们在上面使用的 ~/.ssh/https-tunnel.bash 辅助脚本:

#!/usr/bin/env bash
{ printf "CONNECT ssh-server:22 HTTP/1.0\r\n\r\n"; cat; } | socat - SSL:https-server:443

apache2 HTTPS 服务器需要的修改:

# $ cat /etc/httpd/httpd.conf
LoadModule proxy_connect_module .../modules/mod_proxy_connect.so
# ...
AllowCONNECT 22
<Proxy *>
    Order deny,allow
    Deny from all
</Proxy>
<Proxy ssh-server>
    Order deny,allow
    Allow from all
</Proxy>

在这里,我们允许每个人在托管在https-server的服务器端使用CONNECT HTTP方法,只针对一个目标:ssh-server主机。

而在客户端,我们使用 socat 创建 TLS 连接,并以发送的 CONNECT 方法作为头信息。

现在,你可以使用 $ ssh ssh-via-https 访问 ssh-server。

下面还有更多内容:

背景介绍

我为什么需要它?

我需要在医院呆 1-2 天,不打算使用笔记本电脑。但现在我被困在这里两个星期了,想远程修补一些小东西。医院有免费的 Wi-fi 接入。

但需要注意的是,医院会阻止大多数连接类型。对于大多数主机,它只允许进行 HTTP(TCP 端口 80)和 HTTPS(TCP 端口 443)连接。DNS(UDP 端口 53)和 DoT(TCP 端口 853)似乎也能正常工作,至少对于知名的 DNS 提供商来说是这样。

但 SSH(TCP 22 端口或大多数其他自定义端口)完全被阻止。

我想知道通过 HTTP 或 HTTPS 传输 SSH 有多难。我有一个 GSM 后备服务器,因此可以重新配置远程服务器并尝试各种解决方案。

选项

这里有各种探索途径:

1、在单一端口上共同托管 SSH 和 HTTPS 协议,并以透明方式进行调度。sslh 项目就是这样做的。

它的特点(或限制)之一是不会将一种协议封装到另一种协议中:而是使用各种启发式方法猜测协议,并将其重定向到实际的后端:SSH 服务器、HTTPS 或列表中任何其他受支持的协议。

明显的优点是:无需特殊的 ssh 客户端配置:只需指定非默认端口即可。

些许的缺点是:和外的服务设置,HTTPS 也必须通过 sslh,这可能会隐藏后端连接的源地址。这对日志记录(可能还有性能?)

2、将一种协议完全封装到另一种协议中。

openssh 支持 ProxyCommand 指令,允许用户提供 ssh 协议使用的任何传输方式:

指定用于连接到服务器的命令。命令字符串延伸到行尾,并使用用户的shell ‘exec’指令执行,以避免延迟shell进程。

ProxyCommand的参数接受TOKENS部分中描述的令牌。该命令基本上可以是任何东西,并且应该从其标准输入读取并写入其标准输出。它最终应该连接在某台机器上运行的sshd(8)服务器,或者在某处执行sshd -i。主机密钥管理将使用正在连接的主机的主机名(默认为用户键入的名称)完成。将命令设置为none将完全禁用此选项。请注意,CheckHostIP不适用于使用代理命令的连接。

此指令与nc(1)及其代理支持一起使用非常有用。例如,以下指令将通过HTTP代理连接到192.0.2.0:

ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p

优势明显:

将一种协议完全封装在另一种协议中:无需启发式方法,更难被深度包检测软件拦截。HTTPS 本身不会受到影响或被重定向

缺点可能也很大:

双重加密可能导致性能下降。需要客户端配置才能使用代理。

SSH over HTTP

我选择了 :将 SSH 协议封装为 TLS。

在此之前,我尝试了更简单的通过 HTTP 传输 SSH 协议。

我将 apache2 配置为允许对单一目标 ssh-server:22 使用 CONNECT 方法:

$ cat /etc/httpd/httpd.conf
LoadModule proxy_connect_module .../modules/mod_proxy_connect.so
# ...
AllowCONNECT 22
<Proxy *>
    Order deny,allow
    Deny from all
</Proxy>
<Proxy ssh-server>
    Order deny,allow
    Allow from all
</Proxy>

客户端只需使用 socat 即可进行调整:

$ cat .ssh/config
Host ssh-via-http
        Hostname ssh-server
        Port 22
        ProxyCommand socat - PROXY:http-server:%h:%p,proxyport=80
        ServerAliveInterval 30

使用上述 $ ssh ssh-via-http 命令就可以使用 80 端口连接 SSH。

我使用 ServerAliveInterval 来解决 HTTP 连接在传输过程中关闭的问题:我让 openssh 客户端定期向服务器发送探测,使 HTTP 连接看起来还活着。这样,空闲会话就能保持较长的存活时间。

默认情况下,httpd 使用默认的 TimeOut 60 无 I/O 超时作为关闭隧道的信号。

SSH over HTTPS

由于某些原因,socat 不支持通过 CONNECT 方法进行 HTTPS 代理,而只支持 HTTP。是一个小疏忽?还是我对 socat 的理解有误?

幸运的是,socat 确实支持 TLS 封装。基于 CONNECT 的方法很容易手动实现。我是通过单行脚本实现的:

$ cat ~/.ssh/https-tunnel.bash
#!/usr/bin/env bash
{ printf "CONNECT ssh-server:22 HTTP/1.0\r\n\r\n"; cat; } | socat - SSL:https-server:443

现在,我们可以将此脚本用于通过 HTTPS 进行 SSH 隧道传输:

$ cat .ssh/config
Host ssh-via-https
        ProxyCommand ~/.ssh/https-tunnel.bash
        # some firewalls aggressively close idle TCP connections
        ServerAliveInterval 30

而 $ ssh ssh-via-https 也能达到预期效果。

这样,我就可以进入我的家用电脑,撰写这篇博文了。

临别赠言

无处不在的 HTTPS 使您可以通过限制性极强的中间件传递数据!

CONNECT 方法看似是过去的遗留物,但却是将任何 TCP 有效载荷流封装为 TLS 主机流的有用方法。

如果底层传输对空闲连接不友好,openssh 的 ServerAliveInterval 可以让你保持连接。

玩得开心!

阅读余下内容
 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号