autossh+launchd 实现全自动 ssh隧道转发 Socks5代理

在 Windows 下有非常好用的 xshell 用于管理远程 Linux 服务器,并且可以非常方便的设置隧道转发以实现 socks5 代理的功能,而且 xshell 有断线重连的功能,简直不要太贴心。

但是在 mac 环境下试了很多 ssh 客户端,效果都不尽人意,试过最好的就是 FinalShell 了,也可以设置 ssh 隧道实现 socks5 代理,但就是没有断线自动重连的功能,用起来是有点憋屈的感觉。

最终还是采用了 autossh+launchd 的方案,让 mac 一开机就自动创建 socks5 代理,并且实现断线重连。

launchd: Mac OS X 系统下的用来管理整个系统的 services 和 processes 的工具。如果你需要一个程序开机自动运行,或者由于错误停止后自动重新运行,那么就可以用 launchd 来管理。

autossh: 是用来保持 ssh 一直处于链接状态的工具。可以在链接断开后自动重新链接。

一、首先需要设置免密登录远程 ssh 主机

如果你还没有创建 rsa 密钥对的话,可以使用ssh-keygen命令创建一组密钥对,注意不用设置证书密码,否则使用密钥对时还会要求你输入密码。

ssh-keygen #回车

Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):      #回车或设置密钥的存储路径
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):                   #回车或设置密钥的密码
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
92:9e:ae:cd:eb:40:a8:7c:ad:ac:af:89:c2:ce:16:fa root@wluat
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|                 |
|                 |
|   .   .         |
|  . . o S        |
|.o ... o         |
|+.....o          |
|=o+ .=           |
|=BE+.o*.         |
+-----------------+

然后需要将密钥对的公钥文件发送到远程服务器上去,让远程服务器自动识别本机身份

ssh-copy-id -i ~/.ssh/id_rsa.pub -p 3222 username@58.52.98.74 #回车

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
username@58.52.98.74\'s password: #需要你输入远程主机密码

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh -p '3222' 'username@58.52.98.74'"
and check to make sure that only the key(s) you wanted were added.

这样公钥就写到了远程主机的"~/.ssh/authorized_keys"文件中

关于ssh-copy-id指令的参数解释如下

  • -i 即 identity_file,指定公钥文件
  • -p 即 port,指定 ssh 端口,如果是默认 22 端口可以省略
  • 最后username@58.52.98.74是连接远程主机的用户名和主机地址

公钥发送到远程主机后立即就可以用 ssh 免密连接试试,如果报错UNPROTECTED PRIVATE KEY FILE,是指本地私钥文件权限太开放,需要收紧权限。因为我是将自己常用的一组证书文件直接拷贝到~/.ssh/目录下面的,所以权限不正确,需要更改权限。

chmod 700 ~/.ssh/id_rsa

二、安装 autossh

brew install autossh

安装完成后,在终端下试试直接使用 autossh

autossh -M 0 -nNT -D 0.0.0.0:10800 -i ~/.ssh/id_rsa -p 3222 username@58.52.98.74
# mac自带ssh命令本身就支持socks5转发,autossh很多参数是透传给底层ssh指令,不安装autossh可以临时用下ssh
# ssh -NT -D 0.0.0.0:10800 -p 3222 username@58.52.98.74

注意-D 0.0.0.0:10800,表示监听本机所有 IP 地址的 10800 端口,这样可以让局域网内其他机器可以通过本机使用代理

autossh 参数解释

  • -M 服务器 echo 机制使用的端口(autossh 专用的参数,默认 ssh 没有此参数)
  • -C 压缩传输数据
  • -D 本机最终的 socks5 端口
  • -R 将远程主机(服务器)的某个端口转发到本地端指定机器的指定端口
  • -L 将本地机(客户机)的某个端口转发到远端指定机器的指定端口
  • -f 后台运行
  • -T 不占用 shell,不需要远程分配终端
  • -n 关闭标准输入,配合 -f 参数使用
  • -N 不执行远程命令
  • -q 安静模式运行;忽略提示和错误

-nNT 这个几个参数最好保持,另外最好不要增加-f 参数,如果增加了 launchd 就有可能不能进行正确管理。 -i 是指定 rsa 私钥文件,如果是使用当前登录用户的私钥时可以省略,但是如果后面我们配置 launchd 是在本机用户未登录的状态下就进行连接,则必须要指定。 -p 是指定 ssh 端口

执行上面命令之后我们要达到的效果是:没有返回结果,并且命令不结束,socks5 可用,可以用 SwitchySharp for Chrome 测试。

测试成功后可以按 Ctrl + c 退出刚才状态。

三、配置 launchd 开机启动 autossh,自动连接远程主机

编辑启动配置文件ssh-socks5.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
      <string>ssh-socks5</string>
    <key>ProgramArguments</key>
      <array>
        <string>/opt/homebrew/bin/autossh</string>
        <string>-M</string>
        <string>0</string>
        <string>-nNT</string>
        <string>-D</string>
        <string>0.0.0.0:10800</string>
        <string>-i</string>
        <string>/Users/root/.ssh/id_rsa</string>
        <string>-p</string>
        <string>3222</string>
        <string>username@58.52.98.74</string>
      </array>
    <key>KeepAlive</key>
      <true/>
    <key>RunAtLoad</key>
      <true/>
    <key>StandardOutPath</key>
      <string>/Users/root/logs/ssh-socks5-out.log</string>
    <key>StandardErrorPath</key>
      <string>/Users/root/logs/ssh-socks5-err.log</string>
  </dict>
</plist>

这个 launchd 启动配置文件可以放在 4 个地方,区别如下

  • /Library/LaunchDaemons/ 由管理员定义的守护进程任务项,开机就会启动
  • /Library/LaunchAgents/ 由管理员为用户定义的任务项,开机就会启动
  • ~/Library/LaunchAgents/ 由用户自己定义的任务项,需要用户登录后才会启动
  • /System/Library/LaunchAgents 由 Mac OS X 为用户定义的任务项,开机就会启动

编辑好启动配置文件后,可以立即加载进行测试,launchd 的加载和卸载命令如下

launchctl load ~/Library/LaunchAgents/ssh-socks5.plist
launchctl unload ~/Library/LaunchAgents/ssh-socks5.plist

如果 load 加载后发现 socks5 连接不通,可以用launchctl list | grep ssh-socks5查看启动状态,正常状态是 0,如果是其他状态,则需要查阅资料自行排查。

需要注意的是,StandardOutPathStandardErrorPath指定的日志路径必须是当前用户的可写路径,否则启动会失败,状态码是 78

到目前为止,我们就可以很轻松的使用代理了,而且不用操心代理是否掉线的问题,系统会自动去帮你重连。如果出现问题,可以根据ssh-socks5.plist配置文件中指定的日志文件查看有无相关错误。