基本概念
SSH tunneling又称为SSH port forwarding。
如果想快速了解其应用场景,这篇文章A short guide to SSH port forwarding 很不错。其详细解释了Client to Server的Local Forwarding。虽然没有涉及Server to Client的Remote Forwarding,但他也指出:
Even though our examples above only discuss client-to-server port forwarding rules, the concept of server-to-client port forwarding is entirely symmetric. Only the roles are reversed: with S2C forwarding, the listening address is relative to the SSH server, and the destination host address is relative to the SSH client.
如果想了解概念和示例,官方的指南就很不错。概念看SSH Tunneling,示例看SSH Tunneling: Examples, Command, Server Config。
Local Forwarding示例
实验的拓扑如下图:
- SSH client就是我的笔记本电脑, Windows操作系统。
- SSH Server和Target Server都在公有云上
- SSH Server的IP地址或主机名为
$SSH_SERVER
- Target Server的IP地址或主机名为
$TARGET_SERVER
- SSH Server的IP地址或主机名为
$L
表示本地端口,本例中使用15211$R
表示需要连接的目标端口(也就是对外提供服务的端口),本例中使用1521,即Oracle数据库的监听。- SSH私钥存放在我笔记本上,文件名为pkey.pem
几个重要的概念:
- SSH Tunnel只建立在SSH Client和SSH Server间,他们之间配置SSH即可(公钥,私钥,SSH端口)。
- SSH Server和Target Server间无需配置SSH,只需网络连通并开放必要的端口即可
- SSH Client和Target Server之间的网络可以不通
- Target Server只需开放必要的端口(本例为1521)给SSH Server,和SSH Client可以没有任何关系
- Target Server的IP地址或主机名是给SSH Server用的,和SSH Client没有任何关系。例如当Target Server和SSH Server是同一台主机时,这个地址可以是私网地址。
在我笔记本上执行以下命令,创建SSH Tunnel:
ssh -i pkey.pem ${SSH_USER}@${SSH_SERVER} -L 15211:${TARGET_SERVER}:1521
然后,在我的笔记本(就是Local端),就可以看到在15211端口启动了监听:
$ netstat -an|grep 15211
TCP 127.0.0.1:15211 0.0.0.0:0 LISTENING
TCP [::1]:15211 [::]:0 LISTENING
此时,可以通过笔记本上的客户端工具(如SQL Developer)测试到目标数据库服务器的连通性,指定:
- 主机:127.0.0.1
- 端口:15211
SQL Developer连通数据库后,可以在我的笔记本(Local端)看到建立的连接:
$ netstat -an|grep 15211
TCP 127.0.0.1:15211 0.0.0.0:0 LISTENING
TCP 127.0.0.1:15211 127.0.0.1:60878 ESTABLISHED
TCP 127.0.0.1:60878 127.0.0.1:15211 ESTABLISHED
TCP [::1]:15211 [::]:0 LISTENING
同时,在目标服务器上,也可以看到来自SSH服务器的连接:
$ netstat -an|grep 1521
tcp 0 0 10.0.0.233:1521 0.0.0.0:* LISTEN
...
tcp 0 0 10.0.0.233:1521 13x.1xx.5x.8x:28658 ESTABLISHED
...
其中10.0.0.233为目标服务器的私网地址,13x.1xx.5x.8x为SSH服务器的公网地址。
Remote Forwarding 示例
实验的拓扑如下图:
在这个图中,SSH Client就是我的笔记本电脑,同时也是提供服务的Target Server。
SSH Server仍然存在,但和上例不同,Target Server没有了,因为和SSH Client重合了。在我的笔记本上,我装了一个Oracle数据库,端口1521。
在SSH Client上创建反向的SSH Tunnel,其中15211为SSH Server上的监听端口:
$ ssh -i opcbook.pem ${SSH_USER}@${SSH_SERVER} -R 15211:localhost:1521
在SSH Server上,可以看到已经启动了监听。
$ netstat -an|grep 15211
tcp 0 0 127.0.0.1:15211 0.0.0.0:* LISTEN
tcp6 0 0 ::1:15211 :::* LISTEN
这里有一个问题,就是绑定的地址是127.0.0.1。这意味着只能使用SSH Server充当App Client。在其上的tnsnames.ora文件中配置网络服务名vagrant:
vagrant =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 15211))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orclpdb1)
)
)
然后就可以使用sqlplus连回我本地的数据库了:
$ sqlplus ssb@vagrant
SQL*Plus: Release 19.0.0.0.0 - Production on Tue Feb 20 05:46:01 2024
Version 19.16.0.0.0
Copyright (c) 1982, 2022, Oracle. All rights reserved.
Last Successful login time: Fri Feb 09 2024 08:46:35 +00:00
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL>
查看ssh的帮助,关于-R选项,有句话很关键:
By default, TCP listening sockets on the server will be bound to the loopback interface only. This may be overridden by specifying a bind_address. An empty bind_address, or the address ‘*’, indicates that the remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the server’s GatewayPorts option is enabled (see sshd_config(5)).
来试一下。
修改"/etc/ssh/sshd_config",修改GatewayPorts为yes。重启sshd。确认已修改:
# sshd -T|grep -i gateway
gatewayports yes
然后使用之前相同的命令建立反向SSH Tunnel,这一回,监听主机不同了:
$ netstat -an|grep 15211
tcp 0 0 0.0.0.0:15211 0.0.0.0:* LISTEN
tcp6 0 0 :::15211 :::* LISTEN
此时,任何客户端都可以来连这个15211端口了。我用笔记本上的SQL Developer试了下,成功了。
注意,由于我的SSH Server是在云上,因此云上的防火墙和其操作系统的防火墙都需开放15211端口。