梦一场
- Update at Sat May 11 16:49:47 CST 2019
利用正反 SSH 隧道穿透防火墙访问内网服务器
Announce:
- 工具:实现内网穿透,我目前比较推荐frp和goproxy
- Github: frp: https://github.com/fatedier/frp
- Github: goproxy: https://github.com/snail007/goproxy
- frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp 协议,为 http 和 https 应用协议提供了额外的能力,且尝试性支持了点对点穿透。
- goproxy是基于golang实现的高性能http,https,websocket,tcp,udp,socks5,ss代理服务器,支持正向代理、反向代理、透明代理、内网穿透、TCP/UDP端口映射、SSH中转、TLS加密传输、协议转换、防污染DNS代理。
- 对于内网穿透来说frp比goproxy更功能更多,goproxy更偏向于网络代理
- goproxy内置了长驻后台的功能,如果想后台运行proxy,命令行可以关闭,只需要在命令最后加上
--daemon
参数即可。如果想防止应用异常退出,可以使用--forever
参数,proxy会fork子进程,然后监控子进程,如果子进程异常退出,5秒后重启子进程,该参数配合后台运行参数--daemon
和日志参数--log
,可以保障proxy一直在后台执行不会因为意外退出,而且可以通过日志文件看到proxy的输出日志内容
Goproxy
下载 & 安装
自动安装
- 如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置
1
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
安装完成,配置目录是/etc/proxy,更详细的使用方法请参考手册目录,进一步了解你想要使用的功能.
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
手动安装
下载proxy
下载地址: https://github.com/snail007/goproxy/releases/latest
下面以v6.2为例,如果有最新版,请使用最新版链接.1
2cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v6.2/proxy-linux-amd64.tar.gz下载自动安装脚本
1
2
3
4cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
chmod +x install.sh
./install.sh
Docker安装
docker
项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy的master分支最新版本,
全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile
或者使用参数GOPROXY_VERSION指定构建的goproxy版本.1
ARG GOPROXY_VERSION=v5.3
步骤:
克隆仓库,然后cd进入仓库文件夹,执行:
1
sudo docker build .
镜像打标签:
1
sudo docker tag <上一步的结果ID> snail007/goproxy:latest
运行
参数OPTS的值就是传递给proxy的所有参数
比如下面的例子启动了一个http服务:1
sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 snail007/goproxy:latest
查看日志:
1
sudo docker logs -f goproxy
Windows 直接下载
*.exe
版就行了
使用
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
通过下面的命令生成自签名的证书和key文件。proxy keygen -C proxy
TCP普通用法
- 需求:可以通过服务器的28080端口访问到位于本地服务器上的web服务
- 条件:
- 本地机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
- 步骤:
- 在vps上执行
1
2proxy bridge -p ":33080" -C proxy.crt -K proxy.key
proxy server -r ":28080@:80" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key - 在本地机器A上面执行
1
proxy client -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
- 完成
- 在vps上执行
UDP普通用法
背景:
- 公司机器A提供了DNS解析服务,UDP:53端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在家里能够通过设置本地dns为22.22.22.22,使用公司机器A进行域名解析服务.步骤:
在vps上执行
1
2proxy bridge -p ":33080" -C proxy.crt -K proxy.key
proxy server --udp -r ":53@:53" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key在公司机器A上面执行
1
proxy client -P "22.22.22.22:33080" -C proxy.crt -K proxy.key
完成
frp
下载 & 安装
根据对应的操作系统及架构,从 Release 页面下载最新版本的程序。
将 frps 及 frps.ini 放到具有公网 IP 的机器上。
将 frpc 及 frpc.ini 放到处于内网环境的机器上。
使用
Contents
通过自定义域名访问部署于内网的web服务
有时想要让其他人通过域名访问或者测试我们在本地搭建的 web 服务,但是由于本地机器没有公网 IP,无法将域名解析到本地的机器,通过 frp 就可以实现这一功能,以下示例为 http 服务,https 服务配置方法相同, vhost_http_port 替换为 vhost_https_port, type 设置为 https 即可。
修改 frps.ini 文件,设置 http 访问端口为 8080:
1
2
3
4# frps.ini
[common]
bind_port = 7000
vhost_http_port = 8080启动 frps;
./frps -c ./frps.ini
修改 frpc.ini 文件,假设 frps 所在的服务器的 IP 为 x.x.x.x,local_port 为本地机器上 web 服务对应的端口, 绑定自定义域名
www.yourdomain.com
:1
2
3
4
5
6
7
8
9# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[web]
type = http
local_port = 80
custom_domains = www.yourdomain.com启动 frpc:
./frpc -c ./frpc.ini
将
www.yourdomain.com
的域名 A 记录解析到 IPx.x.x.x
,如果服务器已经有对应的域名,也可以将 CNAME 记录解析到服务器原先的域名。通过浏览器访问
http://www.yourdomain.com:8080
即可访问到处于内网机器上的 web 服务。
通过ssh访问内网机器
修改 frps.ini 文件,这里使用了最简化的配置:
1
2
3# frps.ini
[common]
bind_port = 7000启动 frps:
./frps -c ./frps.ini
修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x;
1
2
3
4
5
6
7
8
9
10# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000启动 frpc:
./frpc -c ./frpc.ini
通过 ssh 访问内网机器,假设用户名为 test:
ssh -oPort=6000 test@x.x.x.x
转发DNS查询请求
DNS 查询请求通常使用 UDP 协议,frp 支持对内网 UDP 服务的穿透,配置方式和 TCP 基本一致。
启动 frps:
./frps -c ./frps.ini
修改 frpc.ini 文件,设置 frps 所在服务器的 IP 为 x.x.x.x,转发到 Google 的 DNS 查询服务器
8.8.8.8
的 udp 53 端口:1
2
3
4
5
6
7
8
9
10# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[dns]
type = udp
local_ip = 8.8.8.8
local_port = 53
remote_port = 6000启动 frpc:
./frpc -c ./frpc.ini
通过 dig 测试 UDP 包转发是否成功,预期会返回
www.google.com
域名的解析结果:dig @x.x.x.x -p 6000 www.google.com
对外提供简单的文件访问服务
通过
static_file
插件可以对外提供一个简单的基于 HTTP 的文件访问服务。- 启动 frpc,启用
static_file
插件,配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[test_static_file]
type = tcp
remote_port = 6000
plugin = static_file
# 要对外暴露的文件目录
plugin_local_path = /tmp/file
# 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径
plugin_strip_prefix = static
plugin_http_user = abc
plugin_http_passwd = abc- 启动 frpc,启用
- 通过浏览器访问
http://x.x.x.x:6000/static/
来查看位于/tmp/file
目录下的文件,会要求输入已设置好的用户名和密码。
安全地暴露内网服务
对于某些服务来说如果直接暴露于公网上将会存在安全隐患。
使用 stcp(secret tcp) 类型的代理可以避免让任何人都能访问到要穿透的服务,但是访问者也需要运行另外一个 frpc。
以下示例将会创建一个只有自己能访问到的 ssh 服务代理。
启动 frpc,转发内网的 ssh 服务,配置如下,不需要指定远程端口:
1
2
3
4
5
6
7
8
9
10
11# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[secret_ssh]
type = stcp
# 只有 sk 一致的用户才能访问到此服务
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22在要访问这个服务的机器上启动另外一个 frpc,配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[secret_ssh_visitor]
type = stcp
# stcp 的访问者
role = visitor
# 要访问的 stcp 代理的名字
server_name = secret_ssh
sk = abcdefg
# 绑定本地端口用于访问 ssh 服务
bind_addr = 127.0.0.1
bind_port = 6000通过 ssh 访问内网机器,假设用户名为 test:
ssh -oPort=6000 test@127.0.0.1
点对点内网穿透
frp 提供了一种新的代理类型 xtcp 用于应对在希望传输大量数据且流量不经过服务器的场景。
使用方式同 stcp 类似,需要在两边都部署上 frpc 用于建立直接的连接。
目前处于开发的初级阶段,并不能穿透所有类型的 NAT 设备,所以穿透成功率较低。穿透失败时可以尝试 stcp 的方式。
frps 除正常配置外需要额外配置一个 udp 端口用于支持该类型的客户端:
1
2
3
4# frps.ini
[common]
bind_port = 7000
bind_udp_port = 7001启动 frpc,转发内网的 ssh 服务,配置如下,不需要指定远程端口:
1
2
3
4
5
6
7
8
9
10
11# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[p2p_ssh]
type = xtcp
# 只有 sk 一致的用户才能访问到此服务
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22在要访问这个服务的机器上启动另外一个 frpc,配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
[p2p_ssh_visitor]
type = xtcp
# xtcp 的访问者
role = visitor
# 要访问的 xtcp 代理的名字
server_name = p2p_ssh
sk = abcdefg
# 绑定本地端口用于访问 ssh 服务
bind_addr = 127.0.0.1
bind_port = 6000通过 ssh 访问内网机器,假设用户名为 test:
ssh -oPort=6000 test@127.0.0.1
总结
如果我们想要frp承载TCP类型的流量,可以参考 通过ssh访问内网机器,只需要将
frpc
配置文件里面的端口修改为需要进行代理的端口就行了。再将第6行修改一下就行了,中括号里面内容虽然可以随意填,但是为了鲜明,最好认真命名。UDP可以参考 转发DNS查询请求
frp 配置文件支持使用系统环境变量进行模版渲染,模版格式采用 Go 的标准格式。
由于 frp 目前支持的功能和配置项较多,未在文档中列出的功能可以从完整的示例配置文件中发现。
- frps 完整配置文件
- frpc 完整配置文件
参见
- frp README: https://github.com/fatedier/frp/blob/master/README.md
- goproxy README: https://github.com/snail007/goproxy/blob/master/README.md