大部分公司都会有开发/测试/生产几套环境使用,来了这家公司以后发现代码居然无法在本地跑通(环境、服务等因素),开发的兄弟都是本地修改代码后,将代码上传到生产服务器,然后在生产环境中通过命令运行程序观察日志来测试。因为没有IDE完全无法DEBUG,实在理解不了这种骚操作。
Pycharm可以远程调试,但是公司只能通过堡垒机(jumpserver.org)连接生产服务器,需要再做一层端口转发,遂研究了一个下午记录之。
ssh隧道
-
首先配置免密登录
这块不用说,为jumpserver添加自己的公钥即可,值得注意的是jumpserver的Web端支持导入自己的公钥,无需登录主机操作。 -
建立ssh隧道
ssh -N -f -L 6000:<内网服务器ip>:22 -p <跳板机端口> username@<跳板机ip> -o TCPKeepAlive=yes
除了-o TCPKeepAlive=yes之外,还有-o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes,相关参数,后续继续查阅。
此命令意义为通过跳板机,将内网服务器的22端口映射到本地的6000端口,即可通过本地的6000端口ssh到目标机器。
ps. 在自有的机器上转发并无问题,但是在jumpserver上无法转发,原因未知。报错:ssh_exchange_identification: Connection closed by remote host,看起来是和授权有关系。
相关参数的解释:
-f Fork into background after authentication.
后台认证用户/密码,通常和-N连用,不用登录到远程主机。
-L port:host:hostport
将本地机(客户机)的某个端口转发到远端指定机器的指定端口. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 同时远程主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有 root 才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport
-R port:host:hostport
将远程主机(服务器)的某个端口转发到本地端指定机器的指定端口. 工作原理是这样的, 远程主机上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转向出去, 同时本地主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有用 root 登录远程主机才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport
-D port
指定一个本地机器 “动态的’’ 应用程序端口转发. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 根据应用程序的协议可以判断出远程主机将和哪里连接. 目前支持 SOCKS4 协议, 将充当 SOCKS4 服务器. 只有 root 才能转发特权端口. 可以在配置文件中指定动态端口的转发.
-C Enable compression.
压缩数据传输。
-N Do not execute a shell or command.
不执行脚本或命令,通常与-f连用。
-g Allow remote hosts to connect to forwarded ports.
在-L/-R/-D参数中,允许远程主机连接到建立的转发的端口,如果不加这个参数,只允许本地主机建立连接。注:这个参数我在实践中似乎始终不起作用。
ProxyCommand
通过配置~/.ssh/config来实现多层跳转
Host tiaoban
Hostname jump.XXX.cn
Port 2222
User da2vin
Host mubiaoji
Hostname xx.xx.xx.xx
Port 22
User data
ProxyCommand ssh.exe -q -x -W %h:%p tiaoban
Host *
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedKeyTypes +ssh-rsa
有趣的是通过ssh tiaoban即可直接进入目标机器,无需输入一大堆ip,端口号。值得注意的是ProxyCommand中的ssh.exe,需要加上后缀。若只写ssh则有报错:
CreateProcessW failed error:2
posix_spawn: No such file or directory
重用 SSH 连接
修改 ~/.ssh/config 文件,添加如下配置
Host *
ControlPersist yes
ControlMaster auto
ControlPath ~/.ssh/%n:%p
Host * 这一行表示下面这些配置和规则影响到的 host,* 表示所有的远程 host 都生效。如果要指定某个(些)特定的 host,可以使用类似 Host *.example.com 的配置。
ControlMaster auto 这个选项告诉 SSH 客户端尝试重用现有的连接(master connection)。
ControlPath 指定了这个连接的 socket 保存的路径,这里配置的是在 /tmp 目录,实际上可以在任何有读写权限的路径下。/tmp/ssh_mux_%h_%p_%r 配置了 socket 文件名,%h 表示远程主机名(host),%p 表示远程 SSH 服务器的端口(port),%r 表示登录的远程用户名(remote user name)。这些 socket 可以随时删掉(rm),删除后首次会话又会创建新的 master 连接。曾经遇到过这种情况,本地断网了,打开的几个远程终端都卡死,网络恢复后也一直这样,甚至打开新的终端也登录不上。这个时候只需要把之前的 socket 文件都删掉,重新登录就可以了。
ControlPersist 这个选项比较重要,表示在创建首个连接(master connection)的会话退出后,master 连接仍然在后台保留,以便其他复用该连接的会话不会出现问题。这个特性在使用 Git 的时候就非常有用,在频繁提交和拉代码的时候,每次 SSH 会话都是很短暂的,如果 master 连接能保持在后台,后续的操作就会如丝般顺滑。
单台机器配置多个私钥
修改 ~/.ssh/config 文件,添加如下配置
Host gitee.com
HostName gitee.com
IdentityFile ~/.ssh/密钥文件名
如果我们不进行配置的话,会默认搜索id_rsa,若配置的话会根据host来匹配私钥。
具体配置参考:https://blog.csdn.net/qq_26189301/article/details/111051245
结果
本次未解决问题,可能是因为对jumpserver的原理不了解导致的。
其余参考链接
https://www.cnblogs.com/jimlau/p/13301772.html
https://www.cnblogs.com/William-Guozi/p/ssh_tunnel.html
https://blog.51cto.com/qchenz/3570400
https://www.jianshu.com/p/ad5aa9663d37
https://www.cnblogs.com/jimlau/p/13301772.html
http://liyangliang.me/posts/2015/03/reuse-ssh-connection/