Cause
在内网有一台服务器,能连接外网但由于没有公网 IP 而无法在外网直接访问,之前想要在上面跑程序的时候,就得通过远程桌面进行操作。虽然 Teamviewer
速度还可以,但总感觉有点不太爽……
很久以前(大概一两个月前)就在逼乎上看到过一篇文章说可以
使用vscode进行远程炼丹 (原文见 Reference #1
噫!我来兴趣了,而且其实早就想试试了,然而之前在忙别的没空折腾 (其实现在也没空),就是前几天在配服务器环境的时候 (麻烦到爆炸,最讨厌配环境了),一想干脆就再折腾一下,弄好远程炼丹(编程)吧。
于是乎就这样连Linux命令都不知道几个的憨憨瞎折腾了一两天的ssh,终于实现了远程炼丹,贼有成就感嘻嘻嘻。
其中遇到了一些奇怪的问题,简单记录一下,也方便我忘了再回来看看吧。
时间有限,VSCode的部分就大概提一下思路,重点是ssh
的部分吧……
Guidance for ssh
不妨假设如下,记清楚了噢,后面频繁用到的。
computer | IP/address | info |
---|---|---|
内网服务器A | 172.x.x.x | 可以访问外网,但外网不能直接访问A |
公网服务器B | test.com | 可以访问外网,也可被外网访问 |
能够联网的主机C | - | 想要在C上对A进行访问 |
内网服务器A上的设置
A设置反向代理到B的 port1 端口
工作原理:
1、本地主机和远程主机建立连接;
2、远程主机上分配了一个 socket 侦听 port 端口;
3、远程端口上有了连接, 该连接就经过安全通道转向本机的端口。备注:root 登录远程主机才能转发特权端口。
ssh -fCNR <port1>:localhost:22 [email protected] -p 22
-R port1:host:port2 将远程机器的端口映射到本地。port1是公网服务器B的端口,host为本地A的IP,port2是A要映射到公网的端口。
[email protected]
用户名@服务器B地址,表示连接到B,以用户usr_b
登录。当然test.com
也可以是IP地址啦。
-f SSH客户端在后台运行
-C 允许压缩
-g 允许远程主机可远程访问,这里有坑。 (但autossh
没有这个选项所以应该影响不大)
-N Do not execute a shell or command. 不执行脚本或命令,通常与-f连用。
-p 22 表示指定连接到B的22端口,默认就是22啦,如果不是的话设置成B的ssh端口。
注意一下,(网上说) 这里有个坑,-g还需要在公网服务器B做下面的设置,设置完后再重启服务。
vim /etc/ssh/sshd_config
# 新增
GatewayPorts yes
进阶1 autossh
由于ssh会自动断开连接,于是利用自动重连工具autossh
,保证连接稳定。
首先安装autossh,这里我用的是CentOS系统。其他类似的。
sudo yum -y install autossh
其实我发现yum(的源里)找不到autossh,后来是用wget下载然后手动安装的…
screen -S reverse_proxy
autossh -M <port2> -CNR <port1>:localhost:22 [email protected]
多了个-M参数,表示从B的port2检测是否断线。同时autossh会在后台运行,-f也不需要了。
这里的screen用于命令行终端切换,给这个界面起了个名叫reverse_proxy
,当然没有装screen的话也需要装一下,不过不执行screen这条语句理论上也能用。
注意,port1和port2均需要在B的安全组内设好规则,允许外网访问。
而后看一下有没有运行↓
可见A已和B建立连接。
此处port1为9999,port2为9998,仅用于演示,你完全可以看心情选择你喜欢的端口,当然有的端口有特定功能另外说。
进阶2 开机自启
当然,为了连接再可靠一点,确保A重启后还可以连上,可以在A上再设置一下开机自动启动autossh。
有一种方法是在/etc/rc.local
里面添加如下内容
su - user_a -c "autossh -i /home/user_a/.ssh/id_rsa -o BatchMode=yes -M <port2> -CNR <port1>:localhost:22 [email protected]"
其中,-i
后接的是SSH_KEY_FILE_PATH,即私钥地址,root用户默认为/root/.ssh/id_rsa
。user_a
是A上的用户,且要求在公网服务器B上,已经有了A主机上用户user_a的公钥 (这个在下面B的设置中介绍)。
然后记得给这个文件赋予可执行权限。
chmod +x /etc/rc.d/rc.local
不过看这个文件注释里的说法,好像这个方法不推荐了。
当然还可以使用其他的配置开机自启的方法,我也刚折腾对Linux也不熟,后面再看看吧。
现在你可以reboot
试一下了,前提是你能确保用这台服务器的其他人没意见,重要数据记得保存好噢。
这样A上的设置就告一段落了,下面看公网服务器B。
公网服务器B上的设置
如果只需要在B上对A进行远程炼丹,那就不需要在B上设置啥了,现在直接ssh就完事了。
ssh -p <port1> localhost
因为已经把A的ssh对应的22端口映射到B的port1端口了呀!
试一下果然如此。
进阶 免密登录
这样每次登录都需要输入密码,是有点麻烦了。
这有个办法,就是把本机的ssh公钥复制到要登录的服务器A的~/.ssh/authorized_key
文件中,实现免密登录。
什么,你还觉得太麻烦了???
这里还有一个快办法,就是在B上登录A后执行这个——
ssh-copy-id -i .ssh/id_rsa.pub [email protected]
-i 指向本地的公钥文件,一般在用户的目录下。
usr_a
为A上要登录的用户名
然后就可以从B免密登录内网服务器A啦。你现在可以在B上ssh试一下,理论上直接就已经登录好了呢!
在任意一台联网主机C对A进行访问的实现
到这里我有点成就感了,远程炼丹实际上已经实现了。
然而我的初衷是要在一台能联网的主机C上去连接内网A的呀,不行还得再折腾一下啊呜。
首先试了一下直接连接公网服务器B的port1,然而不可行。
不过网上有些资料说是可行的,这个我也不知道呢,可能要问神奇的海螺吧。
根据网上的说法,已经在B上对
GatewayPorts
进行了设置,也杀死进程重启了服务甚至直接reboot
了,然而不知道为啥不可行……
不过也不是没有办法呢,ssh里还有个正向代理闲着没事干呢,说上就上吧。
在B上继续进行如下配置
ssh -fCNL *:<port3>:localhost:<port1> localhost
表示将B上的port1映射到port3,注意port3也要设置安全组规则啊。
用ss -ant
看了一下,port3的确打开了。
理论上这样的话在C上ssh连接B的port3端口,就可以直接访问到内网A了的。
如果你成功了,那就可以直接跳到配置VSCode的部分了,尽情享受远程炼丹的快感吧!
喜欢的话可以滑到页面下方,赞赏一下给我买点好吃的(我饿了.jpg
然而我并没有成功……
看了一下安全组设置,没问题,唉,大不了全开了嘛。还是不行。
这里我难受死了,杀死进程又重来试了好几次,一样没有效果。
再去网上查了查,直到看到一篇文章里提到了这个问题,但他直接用的ssh而没有用到autossh,用他的方案还是没解决问题。不过里面提到了一个命令叫lsof
,即“列出打开文件(lists openfiles)”,而在Unix中一切(包括网络套接口)都是文件。这可是个神器啊!
这里用到的是
使用
-i :port
来显示与指定端口相关的网络信息。
lsof -i :<port3> # 查看连接port3端口的网络信息
结果类似于这样(忘记截图了),此处port3为仅供演示所用的9988。
# lsof -i :9988
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh 658 root 4u IPv6 146251 0t0 TCP *:9988 (LISTEN)
这里我才发现,噫,怎么只监听了IPv6,而没有IPv4啊!
实际上通过ss -ant
也可以发现对应的端口只有tcp6而没有tcp…
然而阿里云实例(安全组内)只有IPv4出口来着,而且也是通过IPv4连接到B的,怪不得连不上呢。(佛了
不过也奇怪了,理论上ssh应该v4和v6都监听的吧。
去查了一下ssh命令,这次强制使用 IPv4试试。
(在B上杀死之前的ssh,再执行下面的命令。记得改成你的端口哈。
ssh -fCNL *:<port3>:localhost:<port1> localhost -4
-4
表示强制使用IPv4
这回再看lsof -i :<port3>
:
# lsof -i :9988
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh 954 root 4u IPv4 255331 0t0 TCP *:9988 (LISTEN)
然后再在C上连接B的port3端口。
哇哇哇成功了!
看来果然是这个问题啊我哭了又笑了。
下面就可以设置VSCode来实现远程炼丹啦!
Guidance for VSCode
具体可以参考Reference #1,这里简单操作一下。
在VSCode安装**Remote Development
开发包(扩展)**,然后在远程资源管理器里新增一个SSH Target。
在用户的.ssh/config
文件内新增你的内网炼丹炉信息。
Host 给你的内网主机起个名(看你心情随意取)
HostName 公网服务器B的IP或域名
Port 填port3,如果port1能连上就填port1
User 填内网服务器A的登录用户名
保存,然后便可以连接试一试了。
连接后输入A对应用户的密码,然后就连上了。打开文件夹还需要再输一次密码。
现在再用前面在B的配置里说的办法,把C主机的公钥放到A里就可以实现免密登录啦!
呐,这就是在C上连接到内网炼丹炉的效果了↓。
网络延时几乎没有(不过也取决于网络环境),和在本地编程几乎没有差别,爽到爆炸!
哇哇哇成就感爆棚啊哇哇哇!!!
Summary
时间因素,不可避免可能存在一些失误之处,其中也有不少可以拓展之处,欢迎交流提出哈。
本文仅用于学习研究,请在合理合法范围内使用,
未经允许不得商用,转载请署名MiaoTony并保留本文链接,谢谢。
说点题外话吧。
上周末到这周部署
NUAA_iCal_Web
在线版本弄了一周末(好菜啊第一次弄部署来着…),配服务器环境折腾了老半天(甚至只装好了GPU驱动和anaconda还没配CUDA),配远程炼丹又瞎折腾了一两天,写这篇文章又断断续续地花了好几个小时。
呜下周开始频繁考试了,不敢瞎折腾了嘤嘤嘤。(溜了溜了
Reference
- 使用vscode进行远程炼丹
- SSH反向连接使用Autossh自动ssh
- 利用阿里云ECS跳板机内网穿透- ssh
- SSH如何反向代理稳定穿透内网
- 内网穿透:在公网访问你家的 NAS (还包括
frp
相关) - 从外网 SSH 进局域网,反向代理+正向代理
- autossh 穿透,反向代理到内网
- ssh-copy-id三步实现SSH无密码登录和ssh常用命令
- Linux 命令神器:lsof
- 使用 autossh 自动重启 SSH 会话和通道
- CentOS7添加开机启动服务或脚本
etc.
非常感谢上面这些文章,给了我很多启发呢。