一个叫木头,一个叫马尾

Advanced SSH Usage

本文介绍一些我工作中经常用的ssh技巧。

什么是ssh

摘自维基百科:Secure Shell (SSH) is a cryptographic network protocol for operating network services securely over an unsecured network. Typical applications include remote command-line login and remote command execution, but any network service can be secured with SSH.(https://en.wikipedia.org/wiki/Secure_Shell)

即,Secure Shell(简称SSH) 是一种加密的网络协议,用于在不安全地网络环境下操作网络服务。典型的应用包括远程登录、远程命令执行。除此外,任何其它的网络服务都可以由SSH来加强安全防护。

登录远程服务器

假设你在阿里云上有一台服务器A,其公网ip是 66.66.66.66,内网ip是 172.16.181.10,用户名是app

要在自己电脑上登录这台服务器,可以在终端执行 ssh app@66.66.66.66

假设你还有另一台服务器B,其没有公网ip,内网ip是 172.16.181.11,和服务器A的内网是互通的,用户名也是app

因为服务器B是没有公网ip的,所以我们无法在自己的电脑上直接登录。而服务器AB内网互通,所以我们可以以先登录A,再通过A登录B的方式达到目的。

实际上,ssh命令有一个-J参数,用来指定一个(或多个逗号隔开的)中转服务器(又叫跳板机),达到跳转登录的目的:

ssh -J app@66.66.66.66 app@172.16.181.11

在自己的电脑上运行以上命令,可以直接连到服务器B的控制界面。

服务器的ip地址不容易记住,ssh支持给服务器设置别名,通过别名来操作服务器。编辑自己电脑上的~/.ssh/config文件, 加入如下内容:

# 服务器A
Host server_a 
HostName 66.66.66.66
User app

# 服务器B
Host server_b
HostName 172.16.181.11
User app
# 通过该跳板机登录
ProxyJump server_a

保存,然后在本机终端上执行 ssh server_a 可以登录服务器A;执行 ssh server_b 可以登录服务器B

关于sshconfig文件,还有很多参数可调,建议读者从网上自行学习。

远程命令执行

接着上文,假设我们想在自己的电脑上查看 服务器A 的 ``/tmp` 目录下有哪些文件,我们只需要在终端执行:

ssh server_a ls /tmp

假设命令有多行,还可以利用shell的 heredoc 特性:

ssh server_a <<EOF
cd workspace
ls
EOF

再给一个现实的例子:服务器A上有一个 spring boot 写的web应用,我们想在自己的电脑上写一个脚本,其功能是允许我们在服务器上对项目进行编译、重启。

脚本可以这样写:

ssh server_a <<EOF

# 进入到项目源码目录
cd /path/to/project_source_dir

# 拉取最新的代码
git pull

# 重新编译打包
mvn clean package

# 将jar包拷贝到部署目录
cp target/your_web.jar /path/to/your_deploy_dir

# 进入部署目录
cd /path/to/your_deploy_dir

# 杀死之前的项目进程
jps -ml|grep your_web |awk '{print \$1}'|xargs kill -9

# 重新启动项目
nohup java -jar your_web.jar &

EOF

端口转发

文章的开头我们提到任何其它的网络服务都可以由SSH来加强安全防护,而端口转发便是其防护方式。

端口转发有3类使用场景:

  1. 将访问受限的远程服务,通过中转服务器暴露出来,方便自己访问(比如远程服务器上的MySQL只监听了内网地址,外网无法访问时);

  2. 将自己电脑上的服务通过一台中转服务器暴露出来,方便外部人员访问(比如在杭州的小明,其电脑上部了一个web服务,需要给远在北京的小强访问);

  3. 基于用户的需要,动态地将请求信息从中转服务器转发给不同的远程服务(比如给电脑配置全局代理)。

我们再介绍一下,中转服务器,也叫跳板机(jump host), 是一台可以通过自己电脑访问的远程服务器,该远程服务器可以访问一些受限的资源(通常这些资源自己的电脑无法直接访问)。而ssh端口转发的用途,就是让中转服务器作为中介,为用户的电脑和访问受限的资源开立一个通道,使之互通。

本地端口转发

应用场景:将访问受限的远程服务,通过中转服务器暴露出来,方便自己访问(用户 -> 跳板机 -> 受限资源)

命令格式:

ssh -L [local_addr:]local_port:remote_addr:remote_port -N jump_server

解释:通过 jump_server, 将 remote_addr:remote_port 对应的服务暴露出来,允许用户在自己电脑上通过 local_addr:local_port 的形式访问。

示例:服务器B上有一个mysql实例,因为没有公网ip,我的电脑无法访问它。利用本地端口转发,可以通过服务器A在我的电脑和服务器B之间建立一个安全的访问通道:

ssh -L 127.0.0.1:3307:172.16.181.11:3306 -N server_a

其中 -L表明是本地端口转发,-N用来禁止远程命令执行,server_a是我们为服务器A配置的别名,172.16.181.11是服务器B的ip地址,3306是MySQL的监听端口,3307是我自己电脑的监听端口。

运行完上述命令后,打开一个新的终端窗口,输入 mysql --host 127.0.0.1 --port 3307 并回车,你会发现可以像访问本地数据库一样来访问之前受限制的远程数据库。

远程端口转发

注意: 要实现远程端口转发,需要在 /etc/ssh/sshd_config 中配置 GatewayPorts yes 并重启 ssh server

应用场景:将自己电脑上的服务通过一台中转服务器暴露出来,方便外部人员访问。

命令格式:

ssh -R [remote_addr:]remote_port:host_addr:host_port -N jump_server

解释:将 host_addr:host_port 对应的服务,通过jump_server暴露出来,允许用户使用 remote_addr:remote_port 的形式来访问。

示例:我的电脑上有一个端口为8080的web服务,在外地出差的小明现在要访问它,已知小明可以访问服务器A。利用远程端口转发,将小明的电脑、服务器A和我的电脑打通:

ssh -R 66.66.66.66:8090:127.0.0.1:8080 -N server_a

其中-R表示远程端口转发,整个命令的效果是,访问66.66.66.66:8090,请求会被转发到我的电脑上,即,和访问我电脑上的127.0.0.1:8080效果一样。

动态socks代理

和之前在http代理中介绍的类似,动态socks代理也是根据用户请求,动态地将请求转发到不同的目标服务器。因为连接是加密的,所以socks代理比http代理更安全。

命令格式:

ssh -D 1337 -q -C -N -f server_a

解释:把对本机1337端口的请求,通过跳板机server_a转发给相应的目标服务器

其中:

  1. -D: 后跟 portip:port。建立一个 socket 来监听此端口,当有请求发向此端口时,请求将被转发到远程机器上;

  2. -q: quiet mode,即安静模式,隐藏 warning 和 debug 信息;

  3. -C: 启用压缩;

  4. -N: 禁止执行远程命令,端口转发时有用;

  5. -f: 让 ssh 在后台运行。

执行上述命令后,在自己电脑终端上运行 curl -x socks://localhost:1337 httpbin.org/ip 会发现输出的ip地址是 66.66.66.66。这是因为httpbin.org所在的目标服务器看到的是代理服务器server_a的地址。

Bonus(额外赠送)

可以为电脑配置一个全局的动态代理,这样电脑的所有请求都会经过代理服务器发出去,可以达到隐藏自身ip的目的。而如果你的代理服务器也能访问国外网站,你的电脑就能顺便番个强了(你懂的)。

Mac电脑的socks代理,配置截图如下:

如果你会写pac文件,动态代理结合pac, 可以实现更加灵活的代理配置。参见: https://gist.github.com/dusenberrymw/850151bda3253453e5244d6a33c7cd2d

总结

ssh允许我们以更加安全的方式登录服务器、执行远程命令或进行端口转发。尤其是端口转发,能帮我们穿过网络的限制,借助跳板机,实现2个被隔离环境的安全互通。

最后,提1个问题供读者思考:

目前为止,我们讨论的范围都是 自己的电脑+跳板机+受限服务器。那么,能否通过端口转发,在自己的电脑执行ssh命令,将局域网内其他同事电脑上的web服务暴露出去?动手试试吧。