前言
某些场景下,目标服务器只能通过特定的堡垒机(Bastion)或者叫跳板机(Jump Host)进行访问,有可能还涉及到多个跳板机。在这篇文章中,我们将演示如何使用跳板机进行 ssh 登录 与 Ansible 使用跳板机来访问目标服务器。
原理
我们可以通过一个或多个跳板机连接到另一台主机,以便客户端可以像直接连接一样。man ssh
文档有相关介绍
-J destination
Connect to the target host by first making a ssh connection to the jump host described by destination and then establishing a TCP forward‐ing to the ultimate destination from there. Multiple jump hops may be specified separated by comma characters. This is a shortcut to specify a ProxyJump configuration directive. Note that configuration directives supplied on the command-line generally apply to the desti‐nation host and not any specified jump hosts. Use ~/.ssh/config to specify configuration for jump hosts.
主要方法是使用 SSH 连接,使用 ProxyJump 指令,通过一台或多台跳转主机将 SSH 协议转发到目标主机上运行的 SSH 服务器。这是最安全的方法,因为加密是端到端的。除了进行任何其他加密之外,链的末端还会加密和解密彼此的流量。因此通过中间跳板主机的流量始终是加密的。但如果中间主机拒绝端口转发,则无法使用此方法。
当端口转发可用时,最简单的方法是在配置文件中(~/.ssh/config)使用 ProxyJump
或 -J
作为运行时参数。-J
用法的一个示例是:
bash
ssh -J user1@jump_host root@target_host
不过使用命令行这种方式,当跳板机和目标主机 key 不一样时会失败或者有其他特殊配置的时候,目前没有找到资料可以适配任何场景,建议还是使用配置文件方式。
在老的SSH版本中,-J
不可用。在这种情况下,可以使用 -W
建立隧道方式来"反弹"通过跳板机的连接。
bash
ssh -o ProxyCommand="ssh -W %h:%p user1@jump_host" root@target_host
搭建环境
我使用一台 vps 充当 Jump Host,在 vps 上部署两个 sshd docker 容器充当 Server 1 和Server 2 来简单模拟下面2个场景。
- 创建 Server 1,Server 2 相关文件目录
ssh
mkdir ${PWD}/s1/{ssh,keys}/ -pv
mkdir ${PWD}/s2/{ssh,keys}/ -pv
- 创建 Server 1,Server 2 ssh-key
ruby
root@zzwade:/data/ssh/s2/ssh# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): /data/ssh/s2/ssh/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /data/ssh/s2/ssh/id_rsa
Your public key has been saved in /data/ssh/s2/ssh/id_rsa.pub
The key fingerprint is:
SHA256:hKjimWc9D780ziBR0UdQCSKpPAqCZ8GuEgQyzjz3fCk root@zzwade
The key's randomart image is:
+---[RSA 3072]----+
|=. ..o.o=o. |
|=oo...oo o |
|+*.o... o |
|=+*.+ .. |
|==o. E oS |
|=.o o o |
|.+ + = o |
| o . X . |
| *. |
+----[SHA256]-----+
- 创建motd文件,该文件的主要作用是在用户登录系统时向其显示一些重要信息。这些信息可以是系统管理员自定义的消息、版权声明、系统状态信息等。
bash
echo "Welcome to S2 container! " > s2/motd
echo "Welcome to S1 container! " > s1/motd
目录结构
ruby
root@zzwade:/data/ssh# tree s2
s2
├── keys
├── motd
└── ssh
├── id_rsa
└── id_rsa.pub
3 directories, 3 files
- 创建 docker-compose 文件,这里选用的 sshd 容器镜像如下,更多参数可参考:github.com/panubo/dock...,Server 1和 Server 2 ssh 端口暴露到宿主机(跳板机)22221 和 22222 端口,都设置成root用户直接登录
yaml
services:
s1:
image: quay.io/panubo/sshd:1.6.0
# restart: always
ports:
- "22221:22"
volumes:
- ${PWD}/s1/ssh/id_rsa.pub:/root/.ssh/authorized_keys:ro
- ${PWD}/s1/keys/:/etc/ssh/keys
- ${PWD}/s1/motd:/etc/motd
environment:
- SSH_ENABLE_ROOT=true
s2:
image: quay.io/panubo/sshd:1.6.0
# restart: always
ports:
- "22222:22"
volumes:
- ${PWD}/s2/ssh/id_rsa.pub:/root/.ssh/authorized_keys:ro
- ${PWD}/s2/keys/:/etc/ssh/keys
- ${PWD}/s2/motd:/etc/motd
environment:
- SSH_ENABLE_ROOT=true
- 启动服务,测试Server 1和 Server 2 ssh 登录
使用跳板机登录
配置 User ~/.ssh/config
文件,配置文件中的 IdentityFile
配置项是登录到对应ssh 服务器的私钥,这些私钥文件都是在 User 服务器上。ProxyJump
就是上面说到的配置跳板机登录,可以配置多个跳板机,用逗号分隔。
bash
Host jump
HostName x.zwade.top
User ubuntu
IdentityFile /data/tmp_keys/x_key
Host s1
HostName localhost
User root
Port 22221
ProxyJump jump
IdentityFile /data/tmp_keys/s1_key
Host s2
HostName localhost
User root
Port 22222
ProxyJump jump
IdentityFile /data/tmp_keys/s2_key
User -> Jump Host -> s1,s2
查看 s1, s2 ip 地址
ruby
root@zzwade:/data/ssh# docker network inspect ssh_default
[
{
"Name": "ssh_default",
...
"Containers": {
"c6477d969c75ce2e8fa529c53244249492f6f14c48930e7f5f10e59ae4855597": {
"Name": "ssh-s2-1",
"EndpointID": "d9f3e125521d50578ddfa2c2c58ef8ecda43c740a8c9f833245030e099860b9c",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"fd7f5250e0ce6684cc75ac8860252c5b59686c547f096d1ff2b1788bbb8ee4b9": {
"Name": "ssh-s1-1",
"EndpointID": "fc0ee601a5e4f485a03aba28bb0b8157ee4c1d7a5c7325e9b146b9e8a8cd4810",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
...
}
]
创建另一个 ssh config 文件tmp_config
,来实现 User -> Jump Host -> s1 -> s2
bash
[root@z2024 tmp_keys]# cat tmp_config
Host jump
HostName x.zwade.top
User ubuntu
IdentityFile /data/tmp_keys/x_key
Host s1
HostName 172.18.0.2
User root
Port 22
ProxyJump jump
IdentityFile /data/tmp_keys/s1_key
Host s2
HostName s2
User root
Port 22
ProxyJump s1
IdentityFile /data/tmp_keys/s2_key
使用-F
指定 ssh config 配置文件,测试下可否通过多个跳板机登录到 s2 上面是因为 s1
容器 sshd
没有打开 AllowTcpForwarding
配置,一般 linux sshd 服务默认是打开的。
配置下 s1
容器 ,添加TCP_FORWARDING=true
环境变量,然后重启 s1
容器
bash
services:
s1:
image: quay.io/panubo/sshd:1.6.0
# restart: always
ports:
- "22221:22"
volumes:
- ${PWD}/s1/ssh/id_rsa.pub:/root/.ssh/authorized_keys:ro
- ${PWD}/s1/keys/:/etc/ssh/keys
- ${PWD}/s1/motd:/etc/motd
environment:
- SSH_ENABLE_ROOT=true
- TCP_FORWARDING=true
打开 AllowTcpForwarding
配置后,就可以登录了。
不同主机使用不同跳板机
在 ssh config 配置文件中可以使用Match
指令根据不同主机来使用不同跳板机,host 支持通配符
sql
Match host 192.168.1.*
ProxyJump jump1
Match host 192.168.2.*
ProxyJump jump2
Ansible 使用跳板机
同样的,Ansible 工具也可以利用 ssh jump host 去管理需要登录跳板机的主机,下面的例子复用上面使用tmp_config
配置文件,先配置下 ansible host 文件。
ini
(pyenv) root@vm1:/data/tmp_keys# cat my_ansible_host
[vps]
jump ansible_ssh_user=ubuntu
[container]
s1 ansible_ssh_user=root
s2 ansible_ssh_user=root
使用 ansible ping 模块测试,可以加上 --ssh-common-args='-F tmp_config'
指定ansible 使用自定义的ssh config 文件
报错提示是找不到 Python 程序,因为容器没有安装 Python ,我们在容器上安装好 Python ,重新测试下。
可以看到 ansible ping 执行成功
总结
以上介绍了SSH 通过跳板机连接服务器和 Ansible 通过跳板机连接目标机器,这种跳板机只是普通的 ssh 转发,缺少了像商业或者开源堡垒机的一些很重要的功能,比如审计,权限管控,图形化等。如果您在阅读过程中发现了任何问题,或者有任何可以改进的地方,欢迎留言讨论,也可以关注我的微信公众号运维小猪,谢谢!