fatedier/frp
GitHub: fatedier/frp
frp是一款用Go语言编写的轻量级内网穿透工具,可将位于NAT或防火墙后的本地服务安全地暴露到公网。
Stars: 104788 | Forks: 14914
# frp
[](https://circleci.com/gh/fatedier/frp)
[](https://github.com/fatedier/frp/releases)
[](https://goreportcard.com/report/github.com/fatedier/frp)
[](https://somsubhra.github.io/github-release-stats/?username=fatedier&repository=frp)
[README](README.md) | [中文文档](README_zh.md)
## 什么是 frp?
frp 是一个快速的反向代理工具,可以帮助你将位于 NAT 或防火墙后面的本地服务器暴露到互联网上。它目前支持 **TCP** 和 **UDP**,以及 **HTTP** 和 **HTTPS** 协议,允许通过域名将请求转发到内部服务。
frp 还提供 P2P 连接模式。
## 目录
* [开发状态](#development-status)
* [关于 V2](#about-v2)
* [架构](#architecture)
* [使用示例](#example-usage)
* [通过 SSH 访问局域网内的计算机](#access-your-computer-in-a-lan-network-via-ssh)
* [多个 SSH 服务共享同一端口](#multiple-ssh-services-sharing-the-same-port)
* [在局域网内使用自定义域名访问内部 Web 服务](#accessing-internal-web-services-with-custom-domains-in-lan)
* [转发 DNS 查询请求](#forward-dns-query-requests)
* [转发 Unix 域套接字](#forward-unix-domain-socket)
* [暴露一个简单的 HTTP 文件服务器](#expose-a-simple-http-file-server)
* [为本地 HTTP(S) 服务启用 HTTPS](#enable-https-for-a-local-https-service)
* [私密地暴露你的服务](#expose-your-service-privately)
* [P2P 模式](#p2p-mode)
* [功能](#features)
* [配置文件](#configuration-files)
* [使用环境变量](#using-environment-variables)
* [将配置拆分到不同文件](#split-configures-into-different-files)
* [服务器仪表盘](#server-dashboard)
* [客户端管理界面](#client-admin-ui)
* [监控](#monitor)
* [Prometheus](#prometheus)
* [客户端鉴权](#authenticating-the-client)
* [Token 验证](#token-authentication)
* [OIDC 验证](#oidc-authentication)
* [加密与压缩](#encryption-and-compression)
* [TLS](#tls)
* [热加载 frpc 配置](#hot-reloading-frpc-configuration)
* [从客户端获取代理状态](#get-proxy-status-from-client)
* [服务器端口白名单](#only-allowing-certain-ports-on-the-server)
* [端口复用](#port-reuse)
* [带宽限制](#bandwidth-limit)
* [为每个代理设置](#for-each-proxy)
* [TCP 流多路复用](#tcp-stream-multiplexing)
* [支持 KCP 协议](#support-kcp-protocol)
* [支持 QUIC 协议](#support-quic-protocol)
* [连接池](#connection-pooling)
* [负载均衡](#load-balancing)
* [服务健康检查](#service-health-check)
* [重写 HTTP Host Header](#rewriting-the-http-host-header)
* [设置其他 HTTP Headers](#setting-other-http-headers)
* [获取真实 IP](#get-real-ip)
* [HTTP X-Forwarded-For](#http-x-forwarded-for)
* [Proxy Protocol](#proxy-protocol)
* [为 Web 服务要求 HTTP Basic Auth (密码)](#require-http-basic-auth-password-for-web-services)
* [自定义子域名](#custom-subdomain-names)
* [URL 路由](#url-routing)
* [TCP 端口复用](#tcp-port-multiplexing)
* [通过 PROXY 连接 frps](#connecting-to-frps-via-proxy)
* [端口范围映射](#port-range-mapping)
* [客户端插件](#client-plugins)
* [服务器管理插件](#server-manage-plugins)
* [SSH 隧道网关](#ssh-tunnel-gateway)
* [虚拟网络 (VirtualNet)](#virtual-network-virtualnet)
* [特性门控](#feature-gates)
* [可用的特性门控](#available-feature-gates)
* [启用特性门控](#enabling-feature-gates)
* [特性生命周期](#feature-lifecycle)
* [相关项目](#related-projects)
* [贡献](#contributing)
* [捐赠](#donation)
* [GitHub Sponsors](#github-sponsors)
* [PayPal](#paypal)
## 开发状态
frp 目前正在开发中。你可以尝试 `master` 分支的最新发布版本,或者使用 `dev` 分支来访问当前正在开发的版本。
我们目前正在开发版本 2,并尝试进行一些代码重构和改进。但是,请注意它将不兼容版本 1。
我们将在适当的时候从版本 0 过渡到版本 1,并且只接受错误修复和改进,而不是大的功能请求。
### 关于 V2
v2 版本的复杂度和难度远超预期。我只能利用碎片时间进行开发,频繁的打断严重影响了生产力。鉴于这种情况,我们将继续优化和迭代当前版本,直到有更多的空闲时间来进行大版本的 overhaul。
v2 的概念基于我在云原生领域(特别是 K8s 和 ServiceMesh)多年的经验和反思。其核心是一个现代化的四层和七层代理,类似于 envoy。这个代理本身是高度可扩展的,不仅能实现内网穿透的功能,还适用于其他各种领域。在这个高度可扩展的核心之上,我们旨在实现 frp v1 的所有能力,同时也解决以前无法实现或难以优雅实现的功能。此外,我们将保持高效的开发和迭代能力。
此外,我设想 frp 本身成为一个高度可扩展的系统和平台,类似于我们可以基于 K8s 提供一系列扩展能力。在 K8s 中,我们可以根据企业需求进行定制开发,利用 CRD、controller 模式、webhook、CSI 和 CNI 等功能。在 frp v1 中,我们引入了服务器插件的概念,实现了一些基本的扩展性。但是,它依赖于简单的 HTTP 协议,并且需要用户启动独立进程并自行管理。这种方式既不灵活也不方便,而且现实世界的千变万化。期望一个由少数人维护的非营利性开源项目满足所有人的需求是不现实的。
最后,我们承认配置管理、权限验证、证书管理和 API 管理等模块的当前设计不够现代化。虽然我们可能在 v1 版本中进行一些优化,但确保兼容性仍然是一个具有挑战性的问题,需要付出相当大的努力来解决。
我们衷心感谢您对 frp 的支持。
## 架构

## 使用示例
首先,从 [Release](https://github.com/fatedier/frp/releases) 页面下载适用于您的操作系统和架构的最新程序。
接下来,将 `frps` 二进制文件和服务器配置文件放在具有公网 IP 的服务器 A 上。
最后,将 `frpc` 二进制文件和客户端配置文件放在位于局域网内的服务器 B 上,该局域网无法直接从公网访问。
某些杀毒软件会错误地将 frpc 标记为恶意软件并将其删除。这是因为 frp 是一种能够创建反向代理的网络工具。由于反向代理能够绕过防火墙端口限制,杀毒软件有时会将其标记。如果您正在使用杀毒软件,则可能需要在杀毒软件设置中将 frpc 加入白名单/排除项,以避免被误删。有关更多详细信息,请参阅 [issue 3637](https://github.com/fatedier/frp/issues/3637)。
### 通过 SSH 访问局域网内的计算机
1. 在服务器 A 上修改 `frps.toml`,设置 frp 客户端连接的 `bindPort`:
```
# frps.toml
bindPort = 7000
```
2. 在服务器 A 上启动 `frps`:
`./frps -c ./frps.toml`
3. 在服务器 B 上修改 `frpc.toml`,并将 `serverAddr` 字段设置为 frps 服务器的公网 IP:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000
```
请注意,`localPort`(监听在客户端)和 `remotePort`(暴露在服务器上)用于进出 frp 系统的流量,而 `serverPort` 用于 frps 和 frpc 之间的通信。
4. 在服务器 B 上启动 `frpc`:
`./frpc -c ./frpc.toml`
5. 要通过服务器 A 从另一台机器通过 SSH 访问服务器 B(假设用户名为 `test`),请使用以下命令:
`ssh -oPort=6000 test@x.x.x.x`
### 多个 SSH 服务共享同一端口
此示例通过 tcpmux 类型的代理实现在同一端口上暴露多个 SSH 服务。类似地,只要客户端支持 HTTP Connect 代理连接方式,就可以通过这种方式实现端口复用。
1. 在具有公网 IP 的机器上部署 frps 并修改 frps.toml 文件。这是一个简化的配置:
```
bindPort = 7000
tcpmuxHTTPConnectPort = 5002
```
2. 在内部机器 A 上部署 frpc,配置如下:
```
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "ssh1"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["machine-a.example.com"]
localIP = "127.0.0.1"
localPort = 22
```
3. 在内部机器 B 上部署另一个 frpc,配置如下:
```
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "ssh2"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["machine-b.example.com"]
localIP = "127.0.0.1"
localPort = 22
```
4. 要使用 SSH ProxyCommand 访问内部机器 A,假设用户名为 "test":
`ssh -o 'proxycommand socat - PROXY:x.x.x.x:%h:%p,proxyport=5002' test@machine-a.example.com`
5. 要访问内部机器 B,唯一的区别是域名,假设用户名为 "test":
`ssh -o 'proxycommand socat - PROXY:x.x.x.x:%h:%p,proxyport=5002' test@machine-b.example.com`
### 在局域网内使用自定义域名访问内部 Web 服务
有时我们需要将位于 NAT 网络后面的本地 Web 服务暴露给其他人,以便使用我们自己的域名进行测试。
遗憾的是,我们无法将域名解析为本地 IP。但是,我们可以使用 frp 来暴露 HTTP(S) 服务。
1. 修改 `frps.toml` 并将 vhost 的 HTTP 端口设置为 8080:
```
# frps.toml
bindPort = 7000
vhostHTTPPort = 8080
```
如果你想配置 https 代理,你需要设置 `vhostHTTPSPort`。
2. 启动 `frps`:
`./frps -c ./frps.toml`
3. 修改 `frpc.toml` 并将 `serverAddr` 设置为远程 frps 服务器的 IP 地址。指定你的 Web 服务的 `localPort`:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "web"
type = "http"
localPort = 80
customDomains = ["www.example.com"]
```
4. 启动 `frpc`:
`./frpc -c ./frpc.toml`
5. 将 `www.example.com` 的 A 记录映射到远程 frps 服务器的公网 IP,或者将 CNAME 记录指向你的原始域名。
6. 通过 URL `http://www.example.com:8080` 访问你的本地 Web 服务。
### 转发 DNS 查询请求
1. 修改 `frps.toml`:
```
# frps.toml
bindPort = 7000
```
2. 启动 `frps`:
`./frps -c ./frps.toml`
3. 修改 `frpc.toml` 并将 `serverAddr` 设置为远程 frps 服务器的 IP 地址。将 DNS 查询请求转发到 Google 公共 DNS 服务器 `8.8.8.8:53`:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "dns"
type = "udp"
localIP = "8.8.8.8"
localPort = 53
remotePort = 6000
```
4. 启动 frpc:
`./frpc -c ./frpc.toml`
5. 使用 `dig` 命令测试 DNS 解析:
`dig @x.x.x.x -p 6000 www.google.com`
### 转发 Unix 域套接字
将 Unix 域套接字(例如 Docker 守护进程套接字)暴露为 TCP。
如上所述配置 `frps`。
1. 使用以下配置启动 `frpc`:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "unix_domain_socket"
type = "tcp"
remotePort = 6000
[proxies.plugin]
type = "unix_domain_socket"
unixPath = "/var/run/docker.sock"
```
2. 使用 `curl` 获取 docker 版本来测试配置:
`curl http://x.x.x.x:6000/version`
### 暴露一个简单的 HTTP 文件服务器
暴露一个简单的 HTTP 文件服务器,以便从公共互联网访问存储在局域网中的文件。
如上所述配置 `frps`,然后:
1. 使用以下配置启动 `frpc`:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "test_static_file"
type = "tcp"
remotePort = 6000
[proxies.plugin]
type = "static_file"
localPath = "/tmp/files"
stripPrefix = "static"
httpUser = "abc"
httpPassword = "abc"
```
2. 在浏览器中访问 `http://x.x.x.x:6000/static/` 并指定正确的用户名和密码,以查看 `frpc` 机器上 `/tmp/files` 中的文件。
### 为本地 HTTP(S) 服务启用 HTTPS
你可以使用 `https2https` 插件,并将 `localAddr` 指向 HTTPS 端点。
1. 使用以下配置启动 `frpc`:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "test_https2http"
type = "https"
customDomains = ["test.example.com"]
[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:80"
crtPath = "./server.crt"
keyPath = "./server.key"
hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp"
```
2. 访问 `https://test.example.com`。
### 私密地暴露你的服务
为了降低将某些服务直接暴露给公共网络的风险,STCP (Secret TCP) 模式要求使用预共享密钥才能从其他客户端访问该服务。
配置 `frps` 与上述相同。
1. 在机器 B 上使用以下配置启动 `frpc`。此示例用于暴露 SSH 服务(端口 22),请注意用于预共享密钥的 `secretKey` 字段,并且这里的 `remotePort` 字段已移除:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "secret_ssh"
type = "stcp"
secretKey = "abcdefg"
localIP = "127.0.0.1"
localPort = 22
```
2. 在另一台机器 C 上启动另一个 `frpc`(通常在另一台机器上),使用以下访问带有安全密钥(`secretKey` 字段)的 SSH 服务:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[visitors]]
name = "secret_ssh_visitor"
type = "stcp"
serverName = "secret_ssh"
secretKey = "abcdefg"
bindAddr = "127.0.0.1"
bindPort = 6000
```
3. 在机器 C 上,使用以下命令连接到机器 B 上的 SSH:
`ssh -oPort=6000 127.0.0.1`
### P2P 模式
**xtcp** 旨在直接在客户端之间传输大量数据。仍然需要 frps 服务器,因为这里的 P2P 仅指实际的数据传输。
请注意,它可能不适用于所有类型的 NAT 设备。如果 xtcp 不起作用,您可能需要回退到 stcp。
1. 在机器 B 上启动 `frpc` 并暴露 SSH 端口。注意 `remotePort` 字段已被移除:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
# 如果默认服务器不可用,设置一个新的 STUN 服务器。
# natHoleStunServer = "xxx"
[[proxies]]
name = "p2p_ssh"
type = "xtcp"
secretKey = "abcdefg"
localIP = "127.0.0.1"
localPort = 22
```
2. 启动另一个 `frpc`(通常在另一台机器 C 上),配置为使用 P2P 模式连接到 SSH:
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
# 如果默认服务器不可用,设置一个新的 STUN 服务器。
# natHoleStunServer = "xxx"
[[visitors]]
name = "p2p_ssh_visitor"
type = "xtcp"
serverName = "p2p_ssh"
secretKey = "abcdefg"
bindAddr = "127.0.0.1"
bindPort = 6000
# 当需要自动保持隧道连接时,将其设置为 true
keepTunnelOpen = false
```
3. 在机器 C 上,使用以下命令连接到机器 B 上的 SSH:
`ssh -oPort=6000 127.0.0.1`
## 功能
### 配置文件
自 v0.52.0 起,我们支持 TOML、YAML 和 JSON 配置。请注意,INI 已被弃用,并将在未来的版本中删除。新功能将仅在 TOML、YAML 或 JSON 中可用。想要使用这些新功能的用户应相应地切换其配置格式。
阅读完整的示例配置文件以发现此处未描述的更多功能。
示例使用 TOML 格式,但您仍然可以使用 YAML 或 JSON。
这些配置文件仅供参考。请勿直接使用此配置运行程序,因为它可能存在各种问题。
[frps (服务器) 的完整配置文件](./conf/frps_full_example.toml)
[frpc (客户端) 的完整配置文件](./conf/frpc_full_example.toml)
### 使用环境变量
可以在配置文件中引用环境变量,使用 Go 的标准格式:
```
# frpc.toml
serverAddr = "{{ .Envs.FRP_SERVER_ADDR }}"
serverPort = 7000
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = {{ .Envs.FRP_SSH_REMOTE_PORT }}
```
使用上述配置,变量可以像这样传递给 `frpc` 程序:
```
export FRP_SERVER_ADDR=x.x.x.x
export FRP_SSH_REMOTE_PORT=6000
./frpc -c ./frpc.toml
```
`frpc` 将使用 OS 环境变量渲染配置文件模板。请记住在引用前加上 `.Envs`。
### 将配置拆分到不同文件
您可以将多个代理配置拆分到不同的文件中,并将它们包含在主文件中。
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
includes = ["./confd/*.toml"]
```
```
# ./confd/test.toml
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000
```
### 服务器仪表盘
通过仪表盘检查 frp 的状态和代理统计信息。
为仪表盘配置一个端口以启用此功能:
```
# 默认值为 127.0.0.1。当你想从公网访问时,将其改为 0.0.0.0。
webServer.addr = "0.0.0.0"
webServer.port = 7500
# dashboard 的用户名和密码都是可选的
webServer.user = "admin"
webServer.password = "admin"
```
然后访问 `http://[serverAddr]:7500` 查看仪表盘,用户名和密码均为 `admin`。
此外,您可以使用您的域名泛解析或普通 SSL 证书来启用 HTTPS 端口:
```
webServer.port = 7500
# dashboard 的用户名和密码都是可选的
webServer.user = "admin"
webServer.password = "admin"
webServer.tls.certFile = "server.crt"
webServer.tls.keyFile = "server.key"
```
然后访问 `https://[serverAddr]:7500` 以在安全的 HTTPS 连接中查看仪表盘,用户名和密码均为 `admin`。

### 客户端管理界面
客户端管理界面可帮助您检查和管理 frpc 的配置。
为管理界面配置一个地址以启用此功能:
```
webServer.addr = "127.0.0.1"
webServer.port = 7400
webServer.user = "admin"
webServer.password = "admin"
```
然后访问 `http://127.0.0.1:7400` 查看管理界面,用户名和密码均为 `admin`。
### 监控
当启用 Web 服务器时,frps 会将监控数据在缓存中保存 7 天。进程重启后数据将被清除。
也支持 Prometheus。
#### Prometheus
首先启用仪表盘,然后在 `frps.toml` 中配置 `enablePrometheus = true`。
`http://{dashboard_addr}/metrics` 将提供 Prometheus 监控数据。
### 客户端鉴权
有 2 种验证方法用于验证 frpc 与 frps。
您可以通过在 `frpc.toml` 和 `frps.toml` 中配置 `auth.method` 来决定使用哪一种,默认值为 token。
配置 `auth.additionalScopes = ["HeartBeats"]` 将使用配置的验证方法在 frpc 和 frps 之间的每次心跳中添加和验证身份验证。
配置 `auth.additionalScopes = ["NewWorkConns"]` 将对 frpc 和 frps 之间的每个新工作连接执行相同操作。
#### Token 验证
当在 `frpc.toml` 和 `frps.toml` 中指定 `auth.method = "token"` 时 - 将使用基于 token 的验证。
确保在 `frps.toml` 和 `frpc.toml` 中指定相同的 `auth.token`,以便 frpc 通过 frps 的验证
##### Token 来源
frp 支持使用 `tokenSource` 配置从外部来源读取验证 token。目前支持基于文件的 token 来源。
**基于文件的 token 来源:**
```
# frpc.toml
auth.method = "token"
auth.tokenSource.type = "file"
auth.tokenSource.file.path = "/path/to/token/file"
```
Token 将在启动时从指定文件中读取。这对于 token 由外部系统管理或出于安全原因需要与配置文件分开的情况非常有用。
#### OIDC 验证
当在 `frpc.toml` 和 `frps.toml` 中指定 `auth.method = "oidc"` 时 - 将使用基于 OIDC 的验证。
OIDC 代表 OpenID Connect,使用的流程称为 [Client Credentials Grant](https://tools.ietf.org/html/rfc6749#section-4.4)。
要使用此验证类型 - 请按如下方式配置 `frpc.toml` 和 `frps.toml`:
```
# frps.toml
auth.method = "oidc"
auth.oidc.issuer = "https://example-oidc-issuer.com/"
auth.oidc.audience = "https://oidc-audience.com/.default"
```
```
# frpc.toml
auth.method = "oidc"
auth.oidc.clientID = "98692467-37de-409a-9fac-bb2585826f18" # Replace with OIDC client ID
auth.oidc.clientSecret = "oidc_secret"
auth.oidc.audience = "https://oidc-audience.com/.default"
auth.oidc.tokenEndpointURL = "https://example-oidc-endpoint.com/oauth2/v2.0/token"
```
### 加密与压缩
这些功能默认情况下是关闭的。您可以开启加密和/或压缩:
```
# frpc.toml
[[proxies]]
name = "ssh"
type = "tcp"
localPort = 22
remotePort = 6000
transport.useEncryption = true
transport.useCompression = true
```
#### TLS
自 v0.50.0 起,`transport.tls.enable` 和 `transport.tls.disableCustomTLSFirstByte` 的默认值已更改为 true,并且默认启用 TLS。
对于端口复用,frp 发送一个首字节 `0x17` 来拨号 TLS 连接。这仅在您将 `transport.tls.disableCustomTLSFirstByte` 设置为 false 时生效。
要**强制** `frps` 仅接受 TLS 连接 - 在 `frps.toml` 中配置 `transport.tls.force = true`。**这是可选的。**
**`frpc` TLS 设置:**
```
transport.tls.enable = true
transport.tls.certFile = "certificate.crt"
transport.tls.keyFile = "certificate.key"
transport.tls.trustedCaFile = "ca.crt"
```
**`frps` TLS 设置:**
```
transport.tls.force = true
transport.tls.certFile = "certificate.crt"
transport.tls.keyFile = "certificate.key"
transport.tls.trustedCaFile = "ca.crt"
```
您将需要 **一个根 CA 证书** 和 **至少一个 SSL/TLS 证书**。它**可以**是自签名的或常规的(例如 Let's Encrypt 或其他 SSL/TLS 证书提供商)。
如果您通过 IP 地址而不是主机名使用 `frp`,请确保在生成 SSL/TLS 证书时在主题备用名称 (SAN) 区域中设置适当的 IP 地址。
举一个例子:
* 准备 openssl 配置文件。它存在于 Linux 系统的 `/etc/pki/tls/openssl.cnf` 和 MacOS 的 `/System/Library/OpenSSL/openssl.cnf`,您可以将其复制到当前路径,如 `cp /etc/pki/tls/openssl.cnf ./my-openssl.cnf`。如果没有,您可以自己构建,如:
```
cat > my-openssl.cnf << EOF
[ ca ]
default_ca = CA_default
[ CA_default ]
x509_extensions = usr_cert
[ req ]
default_bits = 2048
default_md = sha256
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
[ req_attributes ]
[ usr_cert ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
EOF
```
* 构建 CA 证书:
```
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt
```
* 构建 frps 证书:
```
openssl genrsa -out server.key 2048
openssl req -new -sha256 -key server.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=server.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com")) \
-out server.csr
openssl x509 -req -days 365 -sha256 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com") \
-out server.crt
```
* 构建 frpc 证书:
```
openssl genrsa -out client.key 2048
openssl req -new -sha256 -key client.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=client.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \
-out client.csr
openssl x509 -req -days 365 -sha256 \
-in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \
-out client.crt
```
### 热加载 frpc 配置
启用 HTTP API 需要 `webServer` 字段:
```
# frpc.toml
webServer.addr = "127.0.0.1"
webServer.port = 7400
```
然后运行命令 `frpc reload -c ./frpc.toml` 并等待大约 10 秒,让 `frpc` 创建、更新或删除代理。
**请注意,除了 'start' 之外,全局客户端参数不会被修改。**
`start` 是一个在合并所有来源(配置文件/包含/存储)之后评估的全局允许列表。
如果 `start` 非空,则任何未列在其中的代理或访问者都不会启动,包括通过 Store API 创建的条目。
保留 `start` 主要是为了兼容性,通常不建议在新配置中使用。
首选每个代理/每个访问者的 `enabled`,除非您明确想要这种全局允许列表行为,否则请保持 `start` 为空。
您可以在重新加载之前运行命令 `frpc verify -c ./frpc.toml` 来检查是否存在配置错误。
### 从客户端获取代理状态
使用 `frpc status -c ./frpc.toml` 获取所有代理的状态。启用 HTTP API 需要 `webServer` 字段。
### 服务器端口白名单
`frps.toml` 中的 `allowPorts` 用于防止端口滥用:
```
# frps.toml
allowPorts = [
{ start = 2000, end = 3000 },
{ single = 3001 },
{ single = 3003 },
{ start = 4000, end = 50000 }
]
```
### 端口复用
frps 中的 `vhostHTTPPort` 和 `vhostHTTPSPort` 可以与 `bindPort` 使用相同的端口。frps 将检测连接的协议并进行相应的处理。
您需要注意的是,如果您想将 `vhostHTTPSPort` 和 `bindPort` 配置为同一端口,则需要首先将 `transport.tls.disableCustomTLSFirstByte` 设置为 false。
我们希望将来尝试允许多个代理使用不同的协议绑定同一个远程端口。
### 带宽限制
#### 为每个代理设置
```
# frpc.toml
[[proxies]]
name = "ssh"
type = "tcp"
localPort = 22
remotePort = 6000
transport.bandwidthLimit = "1MB"
```
在每个代理的配置中设置 `transport.bandwidthLimit` 以启用此功能。支持的单位是 `MB` 和 `KB`。
将 `transport.bandwidthLimitMode` 设置为 `client` 或 `server` 以在客户端或服务器端限制带宽。默认值为 `client`。
### TCP 流多路复用
frp 自 v0.10.0 起支持 TCP 流多路复用,类似于 HTTP2 多路复用,在这种情况下,连接到同一 frpc 的所有逻辑连接都多路复用到同一 TCP 连接中。
您可以通过修改 `frps.toml` 和 `frpc.toml` 来禁用此功能:
```
# frps.toml 和 frpc.toml,必须相同
transport.tcpMux = false
```
### 支持 KCP 协议
KCP 是一种快速可靠的协议,可以降低平均延迟 30% 到 40%,并将最大延迟减少三倍,代价是比 TCP 浪费 10% 到 20% 的带宽。
KCP 模式使用 UDP 作为底层传输。在 frp 中使用 KCP:
1. 在 frps 中启用 KCP:
```
# frps.toml
bindPort = 7000
# 为 KCP 指定一个 UDP 端口。
kcpBindPort = 7000
```
`kcpBindPort` 编号可以与 `bindPort` 编号相同,因为 `bindPort` 字段指定的是 TCP 端口。
2. 配置 `frpc.toml` 以使用 KCP 连接到 frps:
```
# frpc.toml
serverAddr = "x.x.x.x"
# 与 frps.toml 中的 'kcpBindPort' 相同
serverPort = 7000
transport.protocol = "kcp"
```
### 支持 QUIC 协议
QUIC 是一种建立在 UDP 之上的新型多路复用传输协议。
在 frp 中使用 QUIC:
1. 在 frps 中启用 QUIC:
```
# frps.toml
bindPort = 7000
# 为 QUIC 指定一个 UDP 端口。
quicBindPort = 7000
```
`quicBindPort` 编号可以与 `bindPort` 编号相同,因为 `bindPort` 字段指定的是 TCP 端口。
2. 配置 `frpc.toml` 以使用 QUIC 连接到 frps:
```
# frpc.toml
serverAddr = "x.x.x.x"
# 与 frps.toml 中的 'quicBindPort' 相同
serverPort = 7000
transport.protocol = "quic"
```
### 连接池
默认情况下,frps 在用户请求时创建一个新的 frpc 连接到后端服务。通过连接池,frps 保持一定数量的预建立连接,从而减少建立连接所需的时间。
此功能适用于大量的短连接。
1. 在 `frps.toml` 中配置每个代理可以使用的池计数限制:
```
# frps.toml
transport.maxPoolCount = 5
```
2. 启用并指定连接池的数量:
```
# frpc.toml
transport.poolCount = 1
```
### 负载均衡
负载均衡由 `group` 支持。
此功能目前仅适用于 `tcp`、`http`、`tcpmux` 类型。
```
# frpc.toml
[[proxies]]
name = "test1"
type = "tcp"
localPort = 8080
remotePort = 80
loadBalancer.group = "web"
loadBalancer.groupKey = "123"
[[proxies]]
name = "test2"
type = "tcp"
localPort = 8081
remotePort = 80
loadBalancer.group = "web"
loadBalancer.groupKey = "123"
```
`loadBalancer.groupKey` 用于身份验证。
到端口 80 的连接将被随机分派到同一组中的代理。
对于 `tcp` 类型,同一组中的 `remotePort` 应该相同。
对于 `http` 类型,`customDomains`、`subdomain`、`locations` 应该相同。
### 服务健康检查
健康检查功能可以帮助您通过负载均衡实现高可用性。
添加 `healthCheck.type = "tcp"` 或 `healthCheck.type = "http"` 以启用健康检查。
对于健康检查类型 **tcp**,将 ping 服务端口 (TCPing):
```
# frpc.toml
[[proxies]]
name = "test1"
type = "tcp"
localPort = 22
remotePort = 6000
# 启用 TCP 健康检查
healthCheck.type = "tcp"
# TCPing 超时秒数
healthCheck.timeoutSeconds = 3
# 如果健康检查连续失败 3 次,代理将从 frps 中移除
healthCheck.maxFailed = 3
# 每 10 秒进行一次健康检查
healthCheck.intervalSeconds = 10
```
对于健康检查类型 **http**,将向服务发送 HTTP 请求,并期望得到 HTTP 2xx OK 响应:
```
# frpc.toml
[[proxies]]
name = "web"
type = "http"
localIP = "127.0.0.1"
localPort = 80
customDomains = ["test.example.com"]
# 启用 HTTP 健康检查
healthCheck.type = "http"
# frpc 将发送一个 GET 请求到 '/status'
# 并期望得到 HTTP 2xx OK 响应
healthCheck.path = "/status"
healthCheck.timeoutSeconds = 3
healthCheck.maxFailed = 3
healthCheck.intervalSeconds = 10
```
### 重写 HTTP Host Header
默认情况下,frp 根本不会修改隧道传输的 HTTP 请求,因为它是逐字节复制。
然而,说到 Web 服务器和 HTTP 请求,您的 Web 服务器可能依赖 `Host` HTTP 标头来确定要访问的网站。frp 可以在转发 HTTP 请求时使用 `hostHeaderRewrite` 字段重写 `Host` 标头:
```
# frpc.toml
[[proxies]]
name = "web"
type = "http"
localPort = 80
customDomains = ["test.example.com"]
hostHeaderRewrite = "dev.example.com"
```
当 HTTP 请求到达实际的 Web 服务器时,其 `Host` 标头将被重写为 `Host: dev.example.com`,尽管来自浏览器的请求可能具有 `Host: test.example.com`。
### 设置其他 HTTP Headers
与 `Host` 类似,您可以使用代理类型 `http` 覆盖其他 HTTP 请求和响应标头。
```
# frpc.toml
[[proxies]]
name = "web"
type = "http"
localPort = 80
customDomains = ["test.example.com"]
hostHeaderRewrite = "dev.example.com"
requestHeaders.set.x-from-where = "frp"
responseHeaders.set.foo = "bar"
```
在此示例中,它将在 HTTP 请求中设置标头 `x-from-where: frp`,并在 HTTP 响应中设置 `foo: bar`。
### 获取真实 IP
#### HTTP X-Forwarded-For
此功能适用于 `http` 代理或启用了 `https2http` 和 `https2https` 插件的代理。
您可以从 HTTP 请求标头 `X-Forwarded-For` 获取用户的真实 IP。
#### Proxy Protocol
frp 支持 Proxy Protocol 将用户的真实 IP 发送到本地服务。
这是一个 https 服务的示例:
```
# frpc.toml
[[proxies]]
name = "web"
type = "https"
localPort = 443
customDomains = ["test.example.com"]
# 目前支持 v1 和 v2
transport.proxyProtocolVersion = "v2"
```
您可以在 nginx 中启用 Proxy Protocol 支持,以便在 HTTP 标头 `X-Real-` 中暴露用户的真实 IP,然后在您的 Web 服务中读取 `X-Real-IP` 标头以获取真实 IP。
### 为 Web 服务要求 HTTP Basic Auth (密码)
任何能够猜到您的隧道 URL 的人都可以访问您的本地 Web 服务器,除非您使用密码保护它。
这将对所有请求强制执行 HTTP Basic Auth,使用 frpc 配置文件中指定的用户名和密码。
它只能在代理类型为 http 时启用。
```
# frpc.toml
[[proxies]]
name = "web"
type = "http"
localPort = 80
customDomains = ["test.example.com"]
httpUser = "abc"
httpPassword = "abc"
```
在浏览器中访问 `http://test.example.com`,现在将提示您输入用户名和密码。
### 自定义子域名
当多人共享一个 frps 服务器时,对于 http 和 https 类型,使用 `subdomain` 配置非常方便。
```
# frps.toml
subDomainHost = "frps.com"
```
将 `*.frps.com` 解析为 frps 服务器的 IP。这通常称为泛域名 DNS 记录。
```
# frpc.toml
[[proxies]]
name = "web"
type = "http"
localPort = 80
subdomain = "test"
```
现在您可以在 `test.frps.com` 上访问您的 Web 服务。
请注意,如果 `subdomainHost` 不为空,则 `customDomains` 不应是 `subdomainHost` 的子域名。
### URL 路由
frp 支持通过 URL 路由将 HTTP 请求转发到不同的后端 Web 服务。
`locations` 指定用于路由的 URL 前缀。无论列出的顺序如何,frps 首先搜索由文字字符串给出的最具体的前缀位置。
```
# frpc.toml
[[proxies]]
name = "web01"
type = "http"
localPort = 80
customDomains = ["web.example.com"]
locations = ["/"]
[[proxies]]
name = "web02"
type = "http"
localPort = 81
customDomains = ["web.example.com"]
locations = ["/news", "/about"]
```
URL 前缀为 `/news` 或 `/about` 的 HTTP 请求将被转发到 **web02**,其他请求将被转发到 **web01**。
### TCP 端口复用
frp 支持在 frps 上的单个端口上接收指向不同代理的 TCP 套接字,类似于 `vhostHTTPPort` 和 `vhostHTTPSPort`。
目前唯一支持的 TCP 端口复用方法是 `httpconnect` - HTTP CONNECT 隧道。
当在 frps 中将 `tcpmuxHTTPConnectPort` 设置为非 0 值时,frps 将在此端口上监听 HTTP CONNECT 请求。
HTTP CONNECT 请求的主机将用于匹配 frps 中的代理。当 `multiplexer = "httpconnect"` 时,可以通过在 `tcpmux` 代理下配置 `customDomains` 和/或 `subdomain` 在 frpc 中配置代理主机。
例如:
```
# frps.toml
bindPort = 7000
tcpmuxHTTPConnectPort = 1337
```
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
[[proxies]]
name = "proxy1"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["test1"]
localPort = 80
[[proxies]]
name = "proxy2"
type = "tcpmux"
multiplexer = "httpconnect"
customDomains = ["test2"]
localPort = 8080
```
在上述配置中 - 可以在端口 1337 上联系 frps,带有如下 HTTP CONNECT 标头:
```
CONNECT test1 HTTP/1.1\r\n\r\n
```
连接将被路由到 `proxy1`。
### 通过 PROXY 连接 frps
如果您设置了 OS 环境变量 `HTTP_PROXY`,或者在 frpc.toml 文件中设置了 `transport.proxyURL`,frpc 可以通过代理连接到 frps。
它仅在协议为 tcp 时有效。
```
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
transport.proxyURL = "http://user:pwd@192.168.1.128:8080"
```
### 端口范围映射
*在 v0.56.0 中添加*
我们可以使用 Go 模板的范围语法结合内置的 `parseNumberRangePair` 函数来实现端口范围映射。
运行以下示例时,将创建 8 个名为 `test-6000, test-6001 ... test-6007` 的代理,每个都将远程端口映射到本地端口。
```
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
[[proxies]]
name = "tcp-{{ $v.First }}"
type = "tcp"
localPort = {{ $v.First }}
remotePort = {{ $v.Second }}
{{- end }}
```
### 客户端插件
默认情况下,frpc 仅将请求转发到本地 TCP 或 UDP 端口。
插件用于提供丰富的功能。有内置插件,例如 `unix_domain_socket`、`http_proxy`、`socks5`、`static_file`、`http2https`、`https2http`、`https2https`,您可以查看[示例用法](#example-usage)。
使用插件 **http_proxy**:
```
# frpc.toml
[[proxies]]
name = "http_proxy"
type = "tcp"
remotePort = 6000
[proxies.plugin]
type = "http_proxy"
httpUser = "abc"
httpPassword = "abc"
```
`httpUser` 和 `httpPassword` 是 `http_proxy` 插件中使用的配置参数。
### 服务器管理插件
阅读[文档](/doc/server_plugin.md)。
在 [gofrp/plugin](https://github.com/gofrp/plugin) 中查找更多插件。
### SSH 隧道网关
*在 v0.53.0 中添加*
frp 支持在 frps 端监听 SSH 端口,并通过 SSH -R 协议实现 TCP 协议代理,而不依赖 frpc。
```
# frps.toml
sshTunnelGateway.bindPort = 2200
```
当运行 `./frps -c frps.toml` 时,将在当前工作目录中自动创建一个名为 `.autogen_ssh_key` 的私钥文件。此生成的私钥文件将被 frps 中的 SSH 服务器使用。
执行命令
```
ssh -R :80:127.0.0.1:8080 v0@{frp address} -p 2200 tcp --proxy_name "test-tcp" --remote_port 9090
```
在 frps 上设置一个代理,将本地 8080 服务转发到端口 9090。
```
frp (via SSH) (Ctrl+C to quit)
User:
ProxyName: test-tcp
Type: tcp
RemoteAddress: :9090
```
这等效于:
```
frpc tcp --proxy_name "test-tcp" --local_ip 127.0.0.1 --local_port 8080 --remote_port 9090
```
请参阅此[文档](/doc/ssh_tunnel_gateway.md)以获取更多信息。
### 虚拟网络 (VirtualNet)
*在 v0.62.0 中添加的 Alpha 功能*
VirtualNet 功能使 frp 能够通过 TUN 接口在客户端和访问者之间创建和管理虚拟网络连接。这允许机器之间进行 IP 级路由,将 frp 从简单的端口转发扩展到支持完整的网络连接。
有关配置和使用的详细信息,请参阅 [VirtualNet 文档](/doc/virtual_net.md)。
## 特性门控
frp 支持特性门控以启用或禁用实验性功能。这允许用户在功能被认为是稳定的之前试用它们。
### 可用的特性门控
| 名称 | 阶段 | 默认值 | 描述 |
|------|-------|---------|-------------|
| VirtualNet | ALPHA | false | frp 的虚拟网络功能 |
### 启用特性门控
要启用实验性功能,请将特性门控添加到您的配置中:
```
featureGates = { VirtualNet = true }
```
### 特性生命周期
功能通常经历三个阶段:
1. **ALPHA**:默认禁用,可能不稳定
2. **BETA**:可能默认启用,更稳定但仍在发展中
3. **GA (通用可用)**:默认启用,可用于生产环境
## 相关项目
* [gofrp/plugin](https://github.com/gofrp/plugin) - frp 插件仓库,包含基于 frp 扩展机制实现的各种插件,满足不同场景的定制需求。
* [gofrp/tiny-frpc](https://github.com/gofrp/tiny-frpc) - 使用 ssh 协议实现的 frp 客户端轻量版本(最小约 3.5MB),支持一些最常用的功能,适用于资源有限的设备。
## 贡献
有兴趣参与吗?我们很乐意为您提供帮助!
* 看看我们的 [issues 列表](https://github.com/fatedier/frp/issues),并考虑向 **dev 分支** 发送 Pull Request。
* 如果您想添加新功能,请先创建一个 issue 来描述新功能以及实现方法。一旦提案被接受,请创建新功能的实现并将其作为 pull request 提交。
* 抱歉我的英语不好。欢迎对此文档进行改进,甚至是一些拼写错误修复。
* 如果您有很棒的想法,请发送电子邮件至 fatedier@gmail.com。
**注意:我们更希望您在 [issues](https://github.com/fatedier/frp/issues) 中提出建议,这样有相同问题的人可以快速搜索到,我们也不需要重复回答。**
## 捐赠
如果 frp 对您帮助很大,您可以通过以下方式支持我们:
### GitHub Sponsors
通过 [Github Sponsors](https://github.com/sponsors/fatedier) 支持我们。
您可以将贵公司的徽标放置在此项目的 README 文件中。
### PayPal
通过 [PayPal](https://www.paypal.me/fatedier) 向我的账户 **fatedier@gmail.com** 捐款。
标签:DNS 代理, EVTX分析, EVTX分析, frp, frpc, frp-client, frps, frp-server, Go 语言, Homebrew安装, HTTPS, NAT 穿透, P2P 连接, Python安全, SSH 隧道, TCP/UDP 代理, 二进制发布, 内网穿透, 反向代理, 局域网暴露, 底层编程, 开源工具, 日志审计, 端口转发, 网络安全, 网络通信, 负载均衡, 边缘计算, 远程访问, 防火墙绕过, 隐私保护, 隧道工具