Using Proxy for SSH

如果我们直接使用ssh连接国外的服务器,会被GFW检测出,出现干扰、丢包等问题。因此使用proxy进行ssh连接变得很有必要。

背景

最近在使用UtahCloudlab,由于提供的服务器部署在美国,在使用ssh连接时会经常断掉,甚至长时间连接不上,对工作形成了很大的干扰,于是萌生了让ssh走proxy的想法。

环境

  • 系统:macOS 13.2.1 (22D68)
  • ssh version: OpenSSH_9.0p1, LibreSSL 3.3.6
  • 使用的proxy方案:ClashX (Version 1.96.2) & 机场

做法

命令

查询资料,得到使用代理的ssh命令如下:

ssh -o ProxyCommand="nc -X 5 -x 127.0.0.1:7890 %h %p" user@server

参数解释

翻看文档

太长不看可以跳到参数总结

man ssh,我们可以得到相关参数信息:

-o option
             Can be used to give options in the format used in the
             configuration file.  This is useful for specifying options for
             which there is no separate command-line flag.  For full details
             of the options listed below, and their possible values, see
             ssh_config(5).

man ssh_config,得到:

ProxyCommand
             Specifies the command to use to connect to the server.  The
             command string extends to the end of the line, and is executed
             using the user's shell ‘exec’ directive to avoid a lingering
             shell process.

             Arguments to ProxyCommand accept the tokens described in the
             TOKENS section.  The command can be basically anything, and
             should read from its standard input and write to its standard
             output.  It should eventually connect an sshd(8) server running
             on some machine, or execute sshd -i somewhere.  Host key
             management will be done using the Hostname of the host being
             connected (defaulting to the name typed by the user).  Setting
             the command to none disables this option entirely.  Note that
             CheckHostIP is not available for connects with a proxy command.

             This directive is useful in conjunction with nc(1) and its proxy
             support.  For example, the following directive would connect via
             an HTTP proxy at 192.0.2.0:

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

man nc

-X proxy_version
             Requests that nc should use the specified protocol when talking
             to the proxy server.  Supported protocols are “4” (SOCKS v.4),
             “5” (SOCKS v.5) and “connect” (HTTPS proxy).  If the protocol is
             not specified, SOCKS version 5 is used.

-x proxy_address[:port]
             Requests that nc should connect to hostname using a proxy at
             proxy_address and port.  If port is not specified, the well-known
             port for the proxy protocol is used (1080 for SOCKS, 3128 for
             HTTPS).

总结:

  • -o参数用于提供配置选项。
  • ProxyCommand指定用于连接Server的命令。
  • nc是一个可以用来在网络上读写数据的命令行工具。
    • -X指定与proxy使用的protocol,4代表SOCKS V.4,5代表SOCKS v.5,connect代表使用HTTPS proxy。实际上,该参数的默认值是5。
    • -x指定proxy的addr与port。

由于使用的是ClashX,默认的混合代理端口为7890。在使用时,请根据你的实际情况填写代理端口。

这就结束了?

如果你在Google搜索“如何让ssh走代理”,那么教程多半在你得到这条命令后就结束了。于是你兴冲冲的去terminal尝试,结果被现实狠狠打脸:

kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535

可是你已经在网上找了各种“如何让ssh走代理”的教程,你发现得到的内容与上面的命令都别无二致。那么究竟是哪里出错了?

回到端口

由于历史原因,ssh的默认端口为22。如果你使用的是机场代理,那么很有可能是机场由于自身原因关闭了22端口。
网上关于上述报错的场景多在于访问Github仓库失败,解决方案为改为使用443端口访问ssh.github.com。如果你想访问的就是Github,那么这或许能解决你的问题。
但很遗憾,我访问的不是Github,并不能直接用443端口去ssh连接远程的服务器。所以我们还有一个方法:修改远程服务器的ssh端口。

修改ssh端口

进入服务器,打开/etc/ssh/sshd_config,找到

#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

这里Port 22被注释掉,因为原本22就是默认端口。将这里修改为

Port 22
Port 10022
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

这里10022仅做示例,你可以修改为你想要设置的其他端口,但是请注意端口不要冲突。22端口同样打开,这样你可以同时在这两个端口中选择使用进行ssh连接。
重启ssh服务:

sudo systemctl restart sshd

现在我们将命令修改成如下

ssh -o ProxyCommand="nc -X 5 -x 127.0.0.1:7890 %h %p" user@server -p 10022

指定了10022端口,发现连接成功!

VS code

如果你使用的是VS code,你可以在~/.ssh/config中作如下配置:

Host server
  HostName server
  User user
  Port 10022
  ProxyCommand nc -X 5 -x 127.0.0.1:7890 %h %p

注意这里PortProxyCommand的设置。

总结

由于需要连接到国外的服务器,为了ssh连接的稳定性,我们想到使用代理来协助ssh连接。根据查找资料,我们得到了让ssh使用本机ClashX代理的命令。但是代理提供商可能会把22端口封掉,导致连接失败。此时我们可以修改服务器的ssh端口,然后重新用新的端口进行连接。如果使用VS code进行远程连接,也可以修改.ssh目录下的config文件从而不用反复输入命令。

Done!