我们有一些只能通过跳转主机访问的远程服务器。在这些远程服务器上运行虚拟机,这些虚拟机托管我想要本地访问的数据库。每个盒子都是一个 Linux 系统。我在本地 PC、远程主机及其虚拟机上只有 su 权限。
这意味着我们的设置如下所示:
My local pc --> jumphost --> remotehost --> vm with mysql on 127.0.0.1:3306
通常我会 ssh 三次,然后使用 mysql cli。
现在我需要从我的本地电脑到托管数据库的盒子建立一个隧道。
最终我想使用类似 python 库的东西ssh-隧道及其类 SSHTunnelForwarder。但我想从 bash 命令开始设置这种代理。
ssh 身份验证基于密码,每次跳转都需要不同的用户。理想情况下,解决方案不需要更改 ~/.ssh/config
我发现这个邮政,我认为选项 3 非常接近我的需要。
举个例子来说明。假设 jumphost 是:[email protected]
,我的远程主机是[email protected]
,带有数据库的虚拟机是[email protected]
我会尝试的是:
ssh -L 9998:172.1.1.1:22 -N [email protected]
从我的本地电脑端口 9998 到远程主机上的端口 22 建立隧道
我的问题就在这里,我该如何指定用于下一个 ssh 隧道的用户。有人能给我指明正确的方向吗?
ssh -L 9999:10.100.1.1:22-N -p 9998 user2@localhost
从我的本地 PC 端口 9999 通过第一个隧道到远程主机上的端口 22
ssh -L 10000:locahost:3306 -N -p 9999 user3@localhost
从本地 PC 端口 10000 通过端口 9999 上的隧道到达虚拟机上的端口 3306。
据我所知,之后我应该能够从本地电脑通过 127.0.0.1:3306 访问虚拟机上的数据库。
我希望这是有意义的,我感谢每一个答案。
答案1
我能解决这个问题,多亏了这个邮政
class TunnelNetwork(object):
def __init__(self, tunnel_info, target_ip, target_port):
self.tunnel_info = tunnel_info
self.target_ip = target_ip
self.target_port = target_port
def start_tunnels(self):
self.tunnels = []
for idx, info in enumerate(self.tunnel_info):
# if we're not the first element, set the bastion to the local port of the previous tunnel
if idx > 0:
info['ssh_address_or_host'] = ('localhost', self.tunnels[-1].local_bind_port)
# if we are the last element, the target is the real target
if idx == len(self.tunnel_info) - 1:
target = (self.target_ip, self.target_port)
# else, the target is the next bastion
else:
if isinstance(self.tunnel_info[idx+1]['ssh_address_or_host'], tuple):
target = self.tunnel_info[idx+1]['ssh_address_or_host']
else:
target = (self.tunnel_info[idx+1]['ssh_address_or_host'], 22)
self.tunnels.append(SSHTunnelForwarder(remote_bind_address=target, **info))
try:
self.tunnels[idx].start()
except:
return False
return True
def stop_tunnels(self):
for tunnel in reversed(self.tunnels):
tunnel.stop()
def are_tunnels_active(self):
return self.tunnels and all([t.is_active for t in self.tunnels])
def get_local_connect_port(self):
if self.tunnels:
return self.tunnels[-1].local_bind_port
else:
return None
并将其与以下项结合使用:
tunnel_i = [
{"ssh_address_or_host": "192.168.11.29",
"ssh_username": "user1",
"ssh_password": "",
},
{"ssh_address_or_host": "172.1.1.1 ",
"ssh_username": "user2",
"ssh_password": "",
},
{"ssh_address_or_host": "10.100.1.1",
"ssh_username": "user3",
"ssh_password": "",
}
]
target_ip = "127.0.0.1"
target_port = 3306
tn = TunnelNetwork(tunnel_i, target_ip, target_port)
tn.start_tunnels()
确保在您使用的每个跃点上将 AllowTcpForwarding 设置为 On。
答案2
我不知道 python,但是现代 ssh 有该ProxyJump
指令。
这是我的一个文件中的一些匿名片段~/.ssh/config
。
host bastion
user sitaram
hostname 1.2.3.4
# external IP of bastion host
host inside
user not-sitaram
hostname 172.16.17.18
# IP of "inside" as seen from "bastion" server
proxyjump bastion
此后,我只需说ssh inside
(或scp inside:foo bar
、或 rsync、或 git 或其他),它“就可以正常工作”(TM)!Ssh 首先自动连接到堡垒,建立到内部服务器的隧道(不是 TCP 端口转发,而是基于 STDIN/STDOUT 的隧道),然后通过该隧道连接到内部服务器。
答案3
这应该可以做到:
ssh -J jumphost,remotehost vm -L 3306:localhost:3306
这将vm
使用jumphost
&作为代理通过 SSH 连接到 。然后它将上的remotehost
TCP 端口与本地 PC 上的端口连接起来。3306
vm
3306