SSH访问内网/内网穿透的几种情景详解

@[TOC]

0x01 背景

疫情期间远程办公,自然少不了需要连接到公司内网的服务器跑程序。SSH是一种常用的连接到内网的方式,几乎能做到所有连接内网的操作。下面作者将对几种常用或者不常用的连接到内网的使用情况做一些示例。

0x02 网络拓扑

下面将对网络中的几台机器的网络拓扑做说明。

IP均作匿名化处理

  • 个人笔记本1
    • 主机名:PC1
    • 位置: 公网
    • IP:不重要
  • 跳板机
    • 主机名:proxy.test.com
    • ssh:
      • 端口: 5022
      • 用户名: user
    • IP/域名
      • 公网: proxy.test.com
      • 内网: 192.168.8.10
  • 服务器1
    • 主机名: srv1
    • ssh
      • 端口: 818
      • 用户名: user
    • IP
      • 内网:192.168.8.18

0x03 基础知识

0x04 工具列表

0x041 本地PC

  • 如果是windows
    • 安装git-bash并使用它提供的ssh功能
  • 如果是MacOS或者是Linux
    • 使用本地自带的ssh

0x042 企业服务器

需要配置好SSH服务器

0x05 ssh端口转发的基本操作以及通过企业网络访问网站

0x051 适用情况

本方法可以适用于访问

  • 企业内部搭建的网站
  • 必须企业IP授权才能访问的网站(如论文检索网站)

0x052 步骤

1. 执行ssh动态转发命令

# 在PC1上执行
ssh -D 10824 [email protected] -p 5022

上面的命令是在PC1本地开放端口10824,该端口作为一个Sock代理服务器,使用此代理可以通过企业内网访问网络

2. 浏览器配置代理

在谷歌Chrome浏览器中配置Proxy SwitchyOmega。

添加新情景模式,类型为代理服务器。

配置代理协议为sock4,服务器和端口分别为localhost,10824

现在就能在浏览器中访问只有企业内部才可访问的网站了。

0x053 优化

1. 速度优化

SSH添加**-C**参数可以实现数据包的压缩,对于大量数据的传输会提高速度。

ssh -C -D 10824 [email protected] -p 5022

2. bug调试

添加**-v**参数能够显示连接过程中的详细信息,出现错误后可以自行调试。

ssh -vC -D 10824 [email protected] -p 5022

3. 不打开终端

添加**-N**参数可以不打开终端界面。

ssh -vNC -D 10824 [email protected] -p 5022

4. 添加公钥访问,减少密码输入,并提高安全性

ssh-keygen # 生成密钥对
# 添加私钥到服务器
ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected] -p 5022
# 此时连接可以不用密码
ssh -vNC -D 10824 [email protected] -p 5022

5. 保存为config,减少记忆量

在用户目录的.ssh下创建config文件,填入以下内容

Host proxy # 别名
  HostName proxy.test.com # 远程跳板机的域名或者IP
  Port 5022
  User user
  IdentityFile "~/.ssh/id_rsa" # 私钥
  DynamicForward 10824 # 动态转发
  Compression yes # 压缩

现在使用下面的命令即可访问

ssh -vN proxy

6. 防止中断

服务器默认会把不活动的ssh断掉,我们可以在config文件中加入keepalive选项来防止中断

ServerAliveInterval 180
ServerAliveCountMax 2

这个配置的意思是每180秒发送一次keepalive的空包,如果两次失败就会断掉连接

# 完整.ssh/config
ServerAliveInterval 180
ServerAliveCountMax 2
Host proxy # 别名
  HostName proxy.test.com # 远程跳板机的域名或者IP
  Port 5022
  User user
  IdentityFile "~/.ssh/id_rsa" # 私钥
  DynamicForward 10824 # 动态转发
  Compression yes # 压缩

7. 自动重播

可以搭配上自动重新连接的操作,来达到PC休眠苏醒后也能及时连接的功能。

使用while循环来重新连接

while :; do ssh -vN proxy ; done

8. 制作快捷方式(Windows)

  1. 在桌面上新建快捷方式

目标设定为

"<绝对路径>\git-bash.exe" -c "while :; do ssh -vN proxy; done"

注意设置为git-bash的绝对路径。 2. 可以拖动该快捷方式到任务栏 3. 或者将快捷方式放入开始菜单的启动文件夹,实现开机自启。

0x06 访问内网服务器ssh

大家可以想到的方式是先访问跳板机,然后再在跳板机上进行ssh连接到srv1。但是这样需要在跳板机proxy上放置自己的私钥,不安全。

作者使用了ssh提供的ssh代理的功能。

ssh -o "ProxyCommand ssh [email protected] -p 5022 -W %h:%p" [email protected] -p 818

-o:是SSH 选项。本步骤使用了ProxyCommand的选项,它可以做到通过proxy.test.com这台跳板机访问srv1(192.168.8.18)。

优化

在上一节0x05中,我们配置了ssh config文件,本步骤中可以继续用。

命令可以优化为:

ssh -o "ProxyCommand ssh proxy -W %h:%p" [email protected] -p 818

继续优化

在ssh config中加入srv1

# 加入0x05部分的完整ssh config
Host srv1
 HostName 192.168.8.18
 User user
 port 818
 IdentityFile "~/.ssh/id_rsa"
 ProxyCommand ssh proxy -W %h:%p # 这个是核心

现在可以使用下面的终极简化命令访问srv1

ssh srv1

0x07 本地访问企业内部数据库端口

假设srv1上面有个redis数据库服务,端口为6379,只能从srv1的本地访问。

现在本地的PC1机器在测试程序,也想直接访问这个端口。

这时候可以用到本地端口转发的功能。命令格式为:

# 格式
ssh -L <本地ip>:<本地端口>:<远程ip>:<远程端口> <服务器>

在本情况下的命令为。

ssh -L 6379:6379 srv1

这时候访问127.0.0.1的6379端口即可连到redis数据库。

作者比较喜欢打开调试、压缩和禁止连到终端。

ssh -vNCL 6379:6379 srv1

此转发同样可以写入配置文件

Host srv1
 HostName 192.168.8.18
 User user
 port 818
 IdentityFile "~/.ssh/id_rsa"
 ProxyCommand ssh proxy -W %h:%p # 这个是核心
 LocalForward 6379 6379
 Compression yes

0x071 保持服务器名称不变

开发的时候会把服务器的名称固定写到配置文件中,在本地调试的时候为了模拟服务器环境,一般不会更改配置文件。

如果还想以 srv1:6379 连接到redis数据库,可以按如下操作。

1. 修改hosts文件

添加下面的一行到hosts文件

127.0.8.18 srv1

2. 修改.ssh/config文件

把srv1下的LocalForward修改为

LocalForward 127.0.8.18:6379 6379

3. 建立连接

ssh -vN srv1

此时PC1本地可以通过srv1:6379连接到redis数据库。

0x072 扩展本地端口转发(连到远程桌面)

使用本地端口转发不仅可以转发到远程服务自己的端口,也可以转发到该服务器所能连接的其他服务器的端口。

比如远程公司内还有一台开发用的windows工作站 WS1(192.168.7.214)。作者想通过windows的远程桌面服务RDP连接到WS1。

这时候可以通过srv1把WS1的3389端口转发到本地的13389端口。

ssh -vN -L 127.0.7.214:13389:192.168.7.214:3389 srv1

此时再打开windows的远程桌面连接(rdp),连接到127.0.7.214:13389,即可连到内网机器的远程桌面。

0x08 让服务器能访问本地测试环境的数据库

有时候本地也会搭建测试服务让服务器访问。 比如本地PC1搭建了一个mysql服务器,端口为3306。 服务器访问PC1的mysql服务需要使用SSH的远程端口转发功能。

ssh -R <远程IP>:<远程端口>:<本地IP>:<本地端口> <服务器>

本示例中执行的命令为

ssh -R 127.0.0.1:3306:3306 srv1

写入ssh/config配置文件中

Host srv1
 HostName 192.168.8.18
 User user
 port 818
 IdentityFile "~/.ssh/id_rsa"
 ProxyCommand ssh proxy -W %h:%p
 RemoteForward 127.0.0.1:3306 3306
 Compression yes

现在执行ssh -vN srv1即可将发送到远程3306端口的请求转发到本地的3306端口。

注意: 服务器默认不允许转发开放公开访问的端口。需要在服务器配置文件中打开。

#edit file /etc/ssh/sshd_config, add the following line
GatewayPorts yes

0x09 使用VSCode远程编程调试

VSCode提供了Remote Development插件,可以通过ssh连接到远程进行开发,达到和本地开发同样的效率。 此插件使用用户文件夹下的.ssh/config文件作为ssh配置。

具体请参看官方介绍,此处不再赘述。

0x10 文件传输

传输文件可以使用scp命令。 常用命令如下:

# 拷贝到本地
scp file user@host:/path/to/file

# 拷贝到远程
scp user@host:/path/to/file /local/path/to/file

# 拷贝多个文件到远程
scp file1 file2 user@host:/path/to/directory

# 拷贝文件夹到远程
scp -r /path/to/directory user@host:/path/to/directory

其他参数:

-r      # 传输文件夹
-v      # 显示详情
-C      # 压缩传输
-l 800  # limit bandwith with 800
-p      # preserving the original attributes of the copied files
-q      # 隐藏输出

更多命令参考: scp cheat sheetscp man page

0x11 映射服务器文件夹

上面提到的对于浏览大量文件的时候还是不太方便,因此作者又提到了ssh文件映射的功能,本功能需要第三方软件支持。

0x111 MacOSX/Linux

对于本地为macosx或linux的用户: 映射服务器文件夹可以使用sshfs,此软件比较成熟,操作习惯也与ssh基本相同,不再赘述。

0x112 Windows

对于windows用户,可以使用SFTP Drive. 设置好服务器地址,用户名,私钥和打开的文件夹即可。

但是对于跳板机访问方式,SFTP Drive不支持使用密钥验证的ssh隧道做代理。而且它的代理是对所有连接都启用。

因此我们可以使用一个折中的方式,先用本地端口转发把本地的某端口映射为服务器的ssh端口,然后在SFTP Driver中访问此本地端口。

Host proxy
  HostName proxy.test.com
  Port 5022
  User user
  IdentityFile "~/.ssh/id_rsa" # 私钥
  Compression yes # 压缩
  LocalForward 127.0.8.18:818 192.168.8.18:818 # 转发远程srv1的ssh端口

然后再SFTP Drive中填写服务器地址为127.0.8.18,端口为818。

0x12 访问同在远程的同事的本地服务

另有一同事电脑PC2在他自己家工作。PC2上开放了一个数据库服务,端口号为6379,PC1需要连接到PC2的数据库。

可以使用跳板机作为中转。

# PC2 上操作
# 将本地6379端口映射到跳板机的16379
ssh -R 16379:6379 proxy
# PC1 上操作
# 将跳板机的16379端口映射到本地的6379
ssh -L 6379:16379 proxy

更多使用场景欢迎在下方评论或者发送邮件给作者。

参考

  1. Remote Development (VSCode)
  2. sshfs (Arch Wiki)
  3. SFTP Drive (Nsoftware)
  4. scp cheetsheet
  5. scp man page
  6. 实战 SSH 端口转发 (IBM)
  7. SSH Config File
  8. 使用ssh-keygen和ssh-copy-id三步实现SSH无密码登录 和ssh常用命令
  9. ssh
  10. git scm