uncategorized

使用FRP做内网穿透

远程办公,调试接口,利用家里内网闲置服务器,just frp it.

⚠️ 安全考虑部分放在文末,但要先提出来

如何理解内网穿透

抛开技术细节不谈,如何理解穿透呢,大概可以这么认为:正常情况下,我们无法访问到内网的环境,但我们可以在一台公网机器上部署frps,需要被穿透的内网机器作为frpc向公网机器注册自己的网络连接信息,换言之就是,frpc启动并且向frps注册之后,frps建立并负责维持和frpc的连接,这时之前无法与内网机器建立连接的机器,就可以通过公网服务器frps来与各个内网环境建立连接,frps就像一个代理

安装

Frp不需要安装,直接下载release包,存放到合适的位置即可(对于客户端和服务端都是一样的)

解压后的目录大概应该长这样

ccd2b629-94fc-303f-a209-98cf3623f4b2.png

frp 的部署架构大概是,需要一台公网可以自由访问的服务器作为 frp 的server (frps), 其他待穿透的环境,如内网服务器,作为客户端(frpc), frps 只需要关注客户端程序frpc对应的配置文件 frpc.ini,同样的服务端只需要关注 frps 和 frps.ini,这个对应关系十分简洁明了

从一些典型用例开始

我想开放内网的http服务到外网环境…

类似场景枚举

  1. 我有一组内网部署的静态页面(如官网)想开放到公网给客户做演示
  2. 我内网有一些公共基建服务,如 gitlab / confluence / jenkins 希望开放到公网以供同事在公司以外的环境使用(当然这需要公司相关的安全准许)
  3. 我内网部署的 api 服务,希望开放到公网以供远程调试

以上的场景本质上都是http层的服务对外暴露,要达到以上目的,我们需要准备以下内容:

  1. 内网服务,此处我们以对外暴露内网部署的 rest-api 为例,我们假设 api 已经部署在内网服务器的8080端口
  2. 一台公网服务器,云服务器或实体机器,只要公网能访问即可
  3. 一个能解析到指定公网服务器的域名(或子域名 / 三级域名)
  4. frp package, 可以从frp官网下载,分别发送到内网服务器和公网服务器各一份

第一步,先在公网服务器配置frp服务端 (记作frps)。

先来搞定配置文件,拷贝 frps.ini 或者直接在上面改都行。默认的配置文件应该看起来是这样的

1
2
[common]
bind_port = 7000

默认的配置文件只配置了frp server的运行端口,也就是启动后 frps 在远程服务器上占用7000端口运行,frpc 的注册和交互都与这个端口进行

我们还需要配置一个 vhost_http_port 用来指明,当外网通过云服务器与内网连接的时候,是通过哪个端口进行的,这里我们暂取一个5000

1
2
3
[common]
bind_port = 7000
vhost_http_port = 5000

此时启动 frps ,并指定配置文件即可

./frps -c frps.ini

当然,这样启动的话,程序是前台启动的,会始终占据当前会话的输入输出,并且如果会话结束,这个程序也就停止了,所以一般类似的情况,我们都会用 nohup 将程序以后台的方式启动,更进一步,为了简化之后的启动过程,并且避免忘记指令的烦恼,我们可以写成一个 start.sh 脚本供一键使用

1
nohup ./frps -c dzm_frps.ini &

给这个赋下权并执行即可,至此 frps 服务端侧的配置就完成了,只要公网服务器一直保持运行,就没它什么事情了。

第二步,配置域名指向公网服务器

在域名提供商的后台配置一下,将域名(或子域名)指向公网服务器的ip地址,记录类型选A记录

如何验证配置成功,在Linux / Mac / win10 cmd 上有 nslookup 命令,可以查到域名的解析结果,如果 address 这栏显示的是公网服务器的地址,就表示配置成功了,这步根据域名提供商的差异,可能会有一定延迟

a279122b-c2fd-5f5b-b698-cfedad8cb901.png

第三步,在部署了待穿透的api的内网服务器上配置 frp 客户端 (记作 frpc)

同样先看配置文件 frpc.ini

1
2
3
4
5
6
7
8
9
10
[common]
server_addr = 127.0.0.1
server_port = 7000

; 下面可以注释掉
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

ssh 及以下的那部分暂时先不用关注,可以 comment out

上面 [common] 这栏表示,我们的客户端 frpc 要向这个地址和这个端口发起注册请求,这两栏分别填公网服务器公网ip地址,及第一步中配置的bind_port,bind_port在我们这例中是 7000

然后为我们的内网 api 增加一段配置:

1
2
3
4
5
6
7
8
9
[common]
server_addr = 127.0.0.1
server_port = 7000

;; 下面是增加的配置
[api]
type = http
local_port = 8080
custom_domains = api.yourdomain.com

[api] 只是一个名字,随意起,不要与之前用过的重复即可

type 表明我们是一个 http 连接的穿透,照填即可

local_port 表明的是 api 服务在内网机器本地的端口

custom_domains 是第二步中配置的域名,根据自己的情况替换

最后使用启动frps类似的方法启动frpc即可,如果这时你去查看frps的日志(启动目录下的 nohup.out文件),你就会看到有客户端发起连接的提示

94840dcc-8c97-8b56-92ff-db6a62a931c1.png

这时,访问 api.yourdomain.com:5000 就等于访问了内网的8080

至此,需求已经满足。

这中间发生了什么呢,大概就是,我访问api.yourdomain.com:8080, DNS 把域名和端口解析成 ip+port 即 x.x.x.x:8080, 这时frps 接到请求,然后顺便检查域名,将域名匹配到注册了这个域名的 frpc,并与之建立连接,这时请求就顺着注册记录来到了内网,内网将这个请求发送到8080进行处理

整体路径:

(公网环境) api.yourdomain.com:5000 👉 x.x.x.x:5000 (DNS) 👉 frps(通过某种连接技术维持和内网的连接并负责路由) 👉 frpc 👉 127.0.0.1:8080 (内网环境)

如果我还有另一个服务在内网 9090 (如confluence)也想开放到公网 …

在内网机器的frpc配置上增加一段,并为其准备一个新的域名(子域名)即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[common]
server_addr = 127.0.0.1
server_port = 7000

[api]
type = http
local_port = 8080
custom_domains = api.yourdomain.com

;; 下面是增加的配置
[api]
type = http
local_port = 9090
;; 假设这是为该服务在公网准备的新的子域名
custom_domains = cf.yourdomain.com

保存,重启 frpc 后,自动向frps重新注册,frps全程不需要动

我想从外网ssh连接到内网服务器…

TBC. 之后再介绍

关于安全问题

frp 在我看来是一种临时或对一些不是十分重要的网络服务的穿透方案,如给客户做演示,临时开放内网接口等场景,出于安全考虑,最好能用完即关闭,以护内网环境周全;企业级网络环境划分,重要网络位置的对外暴露,请参考其他更加安全和周密的VPN产品或网络解决方案

对于 frp 穿透的服务

  1. 不重要的服务,或作为大众演示目的的服务,可以直接开放
  2. 具备一定信息敏感性的服务,请使用frp提供的鉴权加护后开放
  3. 高权限高影响的服务,请考虑不要使用frp穿透开放
  4. 请保证公网机器的环境安全,瓮城不保,内城难安
  5. 请根据情况在企业安全人员的许可下操作

◉ End.


参考资料:

  1. Frp Official Docs

如博文有叙述不妥以及不准确的地方, 望各位看客不吝赐教, 感谢.

Share