Skip to content

Docker 安装 Frps 和 Frpc

Frp 官方文档:https://gofrp.org/zh-cn/docs/overview/

Frps 服务端 Docker 镜像:https://hub.docker.com/r/snowdreamtech/frps

Frpc 客户端 Docker 镜像:https://hub.docker.com/r/snowdreamtech/frpc

安装和部署

说明: --network host:容器使用主机的网络,无需端口映射,网络性能更好。 服务端配置:/opt/frp/frps.toml 客户端配置:/opt/frp/frpc.toml 注意:配置参考后面章节。

服务端部署

shell
# 服务端
docker pull snowdreamtech/frps:0.68.1

# 启动服务端
docker run --restart=always --network host -d -v /opt/frp/frps.toml:/etc/frp/frps.toml --name frps snowdreamtech/frps:0.68.1

客户端部署

shell
# 客户端
docker pull snowdreamtech/frpc:0.68.1

# 启动客户端
docker run --restart=always --network host -d -v /opt/frp/frpc.toml:/etc/frp/frpc.toml --name frpc snowdreamtech/frpc:0.68.1

通过 SSH 访问内网机器

https://gofrp.org/zh-cn/docs/examples/ssh/

SSH 访问 - 服务端配置

配置文件:/opt/frp/frps.toml

toml
bindPort = 7000
auth.token = "1qaz2wsx"

bindPort:服务端与客户端的通信端口。 auth.token:服务端与客户端的验证令牌。

SSH 访问 - 客户端配置

配置文件:/opt/frp/frpc.toml

toml
serverAddr = "aday.fun"
serverPort = 7000
auth.token = "1qaz2wsx"

[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 7001

serverAddr:服务端的 IP 地址或域名。 serverPort:服务端与客户端的通信端口。需要和服务端保持一致。 auth.token:服务端与客户端的验证令牌。需要和服务端保持一致。 localIP:客户端本地的 IP 地址。docker 容器启动时网络用 host 模式,这里就用 127.0.0.1 就可以访问的到。 localPort:客户端本地的 SSH 端口。 remotePort:服务端监听的端口。访问【服务端 ip 和 remotePort】就可以就可以转发到内网机器的 22 SSH 端口了。

通过 http 访问内网 web 服务

官方文档 - 为 HTTP 网页服务进行内网穿透:https://gofrp.org/zh-cn/docs/examples/vhost-http/

Frps - http - 服务端配置

配置文件:/opt/frp/frps.toml

toml
bindPort = 7000
auth.token = "1qaz2wsx"

# 增加 vhostHTTPPort
vhostHTTPPort = 8000

Frpc - http - 客户端配置

配置文件:/opt/frp/frpc.toml

toml
serverAddr = "aday.fun"
serverPort = 7000
auth.token = "1qaz2wsx"

# SSH 穿透配置
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 7001

# 增加第一个 HTTP 穿透配置
[[proxies]]
name = "web1"
type = "http"
localPort = 8080
customDomains = ["web1.aday.fun"]

# 增加第二个 HTTP 穿透配置(如果有多个 web 服务的话)
[[proxies]]
name = "web2"
type = "http"
localPort = 8081
customDomains = ["web2.aday.fun"]

将 web1.aday.fun 域名解析到服务器的 ip 地址; 浏览器访问 http://web1.aday.fun:8000 端口即可访问到内网的 8080 端口的服务。 web2.aday.fun 同理。域名解析后,会转发到内网的 8081 端口的服务。

通过 https 访问内网 web 服务

官方文档 - 为本地 HTTP 服务启用 HTTPS:https://gofrp.org/zh-cn/docs/examples/https2http/

Frps - https - 服务端配置

配置文件:/opt/frp/frps.toml

toml
bindPort = 7000
auth.token = "1qaz2wsx"
# 注意:不是 vhostHTTPPort,多了个 S
vhostHTTPSPort = 8000

Frpc - https - 客户端配置

配置文件:/opt/frp/frpc.toml

toml
serverAddr = "aday.fun"
serverPort = 7000
auth.token = "1qaz2wsx"

# SSH 穿透配置
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 7001

# 增加第一个 HTTP 穿透配置
[[proxies]]
name = "web1"
# 注意:多了 s
type = "https"
customDomains = ["web1.aday.fun"]

[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:8080"

# HTTPS 证书相关的配置(可以使用自签名证书)
crtPath = "./server.crt"
keyPath = "./server.key"
hostHeaderRewrite = "web1.aday.fun"
requestHeaders.set.x-from-where = "frp"
shell
# 可通过 openssl 生成证书。
cd /opt/frp

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout server.key \
  -out server.crt \
  -days 36500 \
  -addext "subjectAltName = IP:127.0.0.1,DNS:rustfs.aday.fun" \
  -addext "keyUsage = digitalSignature,keyEncipherment" \
  -addext "extendedKeyUsage = serverAuth"

用 nginx 管理证书转发到 frp 的可行性分析

虽然 Frp 本身也支持 HTTPS 穿透,但一般我们的服务端都装了 nginx, 配置了 https 证书。

如果在 frp 中再维护一份证书,就比较麻烦。

因此,你可能会想到,如果用户请求服务器端 nginx, 由 nginx 转发到 frp, 再由 frp 转发到内网机器。

  • 这样 ssl 证书可以放在 nginx 端
  • 可使用 nginx 隐藏访问端口,来更方便的访问多个服务
  • 让 frp 符合单一职责,就只让 frp 做内网穿透,其他交给 nginx 来做,逻辑更清楚。

如果你是以上这么想的,那么它存在一个漏洞。因为服务端要必须暴露 vhostHTTPPort 端口,那么这个端口就可以被其他用户访问。

用户不通过 nginx, 而直接通过 http://域名:vhostHTTPPort 访问,那么这个用户就可以直接通过 http 访问到你的服务。

这样用 nginx 来管理证书的目的就失效了。

总结:即使用 nginx 来转发,依然要在 frp 端配置证书,才能达到 https 的效果,这个不能省略。

防止换证书麻烦的最佳实践: nginx 配置域名证书 https 转发到 frp; frp 配置自签名证书,并开启 https;

即用两个证书。用自签名证书来避免域名证书三个月后过期更换麻烦的问题。

这样如果直接访问 frp 的 vhostHTTPPort 端口,就会得到 frp 的 https 证书,而不是 nginx 的证书。规避了 http 访问。

正常通过 nginx 访问,转发到 frp, 用的 nginx 证书。

frp 其它文档