FRP

frp#

  • 是什么?
    • 基于端口的 4 层反向代理
    • 基于 vhost 的 7 层反向代理
    • 服务端支持 http, tcp, udp, kcp
    • 服务端支持 ws 暴露 - TCP over Websocket
    • 代理支持 tcp,udp,http,https,stcp,xtcp
      • stcp - 两个客户端通过共享密钥互通
      • xtcp - NAT 穿透 P2P - 成功率低, 基本无意义
    • 服务端和客户端支持简单 dashboard
  • 文档
  • 默认端口 7000
  • 组件
    • frps - 服务端 - 一般有外网 IP
    • frpc - 客户端 - 一般位于内网, 期望被外网访问, A -> frps -> frpc
  • 缺陷
    • xtcp 点到点穿透率低, 基本失败, 如果需要 p2p 可选择 tinc.

配置#

  • 配置会通过 tpl 渲染
  • 可通过 Envs 访问环境变量 - 例如 server_addr = {{.Envs.SERVER_ADDR}}
  • https 通过 SNI 路由
caution
  • custom_domains 不能是 subdomain_host 子域名
    • 但 a.b.c.com 也会认为是 c.com 子域名
  • subdomain 不能包含 .*
  • frps 没有 reload 因此修改配置需要重启
  • visitor 端需要配置 server_name
  • https2https
  • https2http
  • http2https
    • 不需要证书 - 将 https 暴露为 http - type=http
    • plugin_local_addr
    • plugin_host_header_rewrite
    • pluginheader

frps.ini

[common]
bind_port = 7000
kcp_bind_port = 7000
# 设置了 udp 端口 udp 相关协议才能工作
bind_udp_port = 7001
# 建议修改 Token - 否则任何人都可以在该节点暴露端口
# token=$(uuidgen)
# 支持 http 和 https vhost 的必须配置
subdomain_host = example.local
vhost_http_port = 8080
vhost_https_port = 8443

frpc.ini

[common]
# frps 地址
# server_addr = x.x.x.x
server_port = 7000
# 需要暴露的端口
[ssh]
# tcp, udp, http, https, stcp, sudp, xtcp, tcpmux
type = tcp
local_ip = 127.0.0.1
local_port = 22
# frps 暴露端口
remote_port = 6000

frps.ini#

[common]
# IPv6 格式 "[::1]:80", "[ipv6-host]:http", "[ipv6-host%zone]:80"
bind_addr = 0.0.0.0
bind_port = 7000
# 用于辅助打通 NAT
bind_udp_port = 7001
# kcp udp 端口 - 未设置则不开启 kcp
kcp_bind_port = 7000
# 代理监听端口 - 默认为 bind_addr
# proxy_bind_addr = 127.0.0.1
# http 虚拟主机端口 - 可以与 bind_port 相同
vhost_http_port = 80
vhost_https_port = 443
# 虚拟主机超时时间 - 默认 60 秒
# vhost_http_timeout = 60
# HTTP CONNECT 端口 - 默认 0
# 设置为 0 则不会做多路
# tcpmux_httpconnect_port = 1337
# 管理面板
dashboard_addr = 0.0.0.0
dashboard_port = 7500
# 默认 admin admin
dashboard_user = admin
dashboard_pwd = admin
# 是否开启 {dashboard_addr}:{dashboard_port}/metrics
enable_prometheus = true
# debug 用 - 前端 assets 目录
# assets_dir = ./static
# console 或日志文件
log_file = ./frps.log
# trace, debug, info, warn, error
log_level = info
log_max_days = 3
disable_log_color = false
# 错误日志记录到 frpc
detailed_errors_to_client = true
# frpc 和 frps 授权方式 - token, oidc
authentication_method = token
# 是否对心跳鉴权
authenticate_heartbeats = false
# frps 新链接是否鉴权
authenticate_new_work_conns = false
# auth token
token = 12345678
# jwt iss
oidc_issuer =
# jwt aud
oidc_audience =
# 不校验过期
oidc_skip_expiry_check = false
# 不校验 iss
oidc_skip_issuer_check = false
# 心跳超时 - 默认 90 不建议修改
# heartbeat_timeout = 90
# 用户链接超时 - 默认 90 不建议修改
# user_conn_timeout = 10
# frpc 允许的端口范围
# allow_ports = 2000-3000,3001,3003,4000-50000
# 最大链接池
max_pool_count = 5
# 每个客户端允许的端口数 - 0 不限制
max_ports_per_client = 0
# 强制 TLS 链接
tls_only = false
# tls_cert_file = server.crt
# tls_key_file = server.key
# tls_trusted_ca_file = ca.crt
# 如果设置了子域名,类型为 http 或 https 的配置可以通过 name.frps.com
# subdomain_host = frps.com
# tcp 流多路
tcp_mux = true
# 404 页
# custom_404_page = /path/to/404.html
# udp 包大小,客户端和服务端应该保持一致 - 影响 sudp 代理
udp_packet_size = 1500
[plugin.user-manager]
addr = 127.0.0.1:9000
path = /handler
ops = Login
[plugin.port-manager]
addr = 127.0.0.1:9001
path = /handler
ops = NewProxy

frpc.ini#

common#

[common]
server_addr = 0.0.0.0
server_port = 7000
# 通过 http,socks,ntlm 代理连接 frps
# 支持环境变量, 只支持 tcp 类型协议
# http_proxy = http://user:[email protected]:8080
# http_proxy = socks5://user:[email protected]:1080
# http_proxy = ntlm://user:[email protected]:2080
log_file = ./frpc.log
log_level = info
log_max_days = 3
disable_log_color = false
authenticate_heartbeats = false
authenticate_new_work_conns = false
token = 12345678
# OIDC 客户端配置
oidc_client_id =
oidc_client_secret =
# OIDC Token Endpoint
oidc_token_endpoint_url =
# jwt aud
oidc_audience =
# frpc 控制端访问鉴权 - 支持 reload 等操作
admin_addr = 127.0.0.1
admin_port = 7400
admin_user = admin
admin_pwd = admin
# assets_dir = ./static
# 链接池 - 预先建立的链接数
pool_count = 0
tcp_mux = true
# 代理名字 - {user}.{proxy}
# user = your_name
# 登陆失败退出
login_fail_exit = true
# frps 通讯协议 - tcp, kcp, websocket
protocol = tcp
# 客户端 tls 配置
tls_enable = true
# tls_cert_file = client.crt
# tls_key_file = client.key
# tls_trusted_ca_file = ca.crt
# 修改 dns
# dns_server = 8.8.8.8
# 生效配置 - 默认全部
# start = ssh,dns
# heartbeat_interval = 30
# heartbeat_timeout = 90
# 客户端额外元信息
meta_var1 = 123
meta_var2 = 234
udp_packet_size = 1500

proxy#

  • 每个 section 为一个代理配置
  • 如果配置了 user 则名字为 {user}.{proxy}
vardefaultvaluesdesc
typetcptcp,udp,http,https,stcp,xtcp代理协议类型
[ssh]
type = tcp
# 被代理目标
local_ip = 127.0.0.1
local_port = 22
# 带宽限制 - 单位支持 KB, MB
bandwidth_limit = 1MB
# frps 通讯加密
use_encryption = false
# 协议压缩
use_compression = false
# frps 暴露端口
# 0 为随机端口
remote_port = 6001
# 相同组会进行负载均衡
group = test_group
# 相同组 key 相同
group_key = 123456
# 监控检查 - 支持 tcp, http
health_check_type = tcp
health_check_timeout_s = 3
# 检查失败 3 次 frps 移除代理
health_check_max_failed = 3
health_check_interval_s = 10
# 额外元信息配置
meta_var1 = 123
meta_var2 = 234
# range: 生成批量端口
# 生成 tcp_port_6010,tcp_port_6011
[range:tcp_port]
type = tcp
local_ip = 127.0.0.1
local_port = 6010-6020,6022,6024-6028
remote_port = 6010-6020,6022,6024-6028
use_encryption = false
use_compression = false
# http 支持通过子域名访问
# http://web01.subdomain.com
[web01]
type = http
local_ip = 127.0.0.1
local_port = 80
use_encryption = false
use_compression = true
# Basic auth
http_user = admin
http_pwd = admin
# 修改域名名字
subdomain = web01
# 自定义域名
custom_domains = web02.yourdomain.com
# http 可以指定允许的 location
locations = /,/pic
# 修改 Host
host_header_rewrite = example.com
# 自定义 Header - 以下配置发送 X-From-Where: frp
header_X-From-Where = frp
health_check_type = http
# 自定义 http 健康检查参数
health_check_url = /status
health_check_interval_s = 10
health_check_max_failed = 3
health_check_timeout_s = 3
# https 代理协议版本 - v1, v2 或不指定
# Proxy 协议能传输客户端信息但是需要目标端支持
# proxy_protocol_version = v2
# unix socket
[plugin_unix_domain_socket]
type = tcp
remote_port = 6003
# 定义插件后 local_ip 和 local_port 无意义
# 由插件处理创建的链接
plugin = unix_domain_socket
# 插件参数
plugin_unix_path = /var/run/docker.sock
[plugin_http_proxy]
type = tcp
remote_port = 6004
plugin = http_proxy
plugin_http_user = abc
plugin_http_passwd = abc
[plugin_socks5]
type = tcp
remote_port = 6005
plugin = socks5
plugin_user = abc
plugin_passwd = abc
[plugin_static_file]
type = tcp
remote_port = 6006
plugin = static_file
plugin_local_path = /var/www/blog
plugin_strip_prefix = static
plugin_http_user = abc
plugin_http_passwd = abc
[plugin_https2http]
type = https
custom_domains = test.yourdomain.com
plugin = https2http
plugin_local_addr = 127.0.0.1:80
plugin_crt_path = ./server.crt
plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[plugin_https2https]
type = https
custom_domains = test.yourdomain.com
plugin = https2https
plugin_local_addr = 127.0.0.1:443
plugin_crt_path = ./server.crt
plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[plugin_http2https]
type = http
custom_domains = test.yourdomain.com
plugin = http2https
plugin_local_addr = 127.0.0.1:443
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[secret_tcp]
# If the type is secret tcp, remote_port is useless
# Who want to connect local port should deploy another frpc with stcp proxy and role is visitor
type = stcp
# sk used for authentication for visitors
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
use_encryption = false
use_compression = false
# user of frpc should be same in both stcp server and stcp visitor
[secret_tcp_visitor]
# frpc role visitor -> frps -> frpc role server
role = visitor
type = stcp
# the server name you want to visitor
server_name = secret_tcp
sk = abcdefg
# connect this address to visitor stcp server
bind_addr = 127.0.0.1
bind_port = 9000
use_encryption = false
use_compression = false
[p2p_tcp]
type = xtcp
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
use_encryption = false
use_compression = false
[p2p_tcp_visitor]
role = visitor
type = xtcp
server_name = p2p_tcp
sk = abcdefg
bind_addr = 127.0.0.1
bind_port = 9001
use_encryption = false
use_compression = false
[tcpmuxhttpconnect]
type = tcpmux
multiplexer = httpconnect
local_ip = 127.0.0.1
local_port = 10701
custom_domains = tunnel1

cookbook#

Kubernetes#

[kubernetes]
local_ip = {{.Envs.KUBERNETES_SERVICE_HOST}}
local_port = {{.Envs.KUBERNETES_SERVICE_PORT}}

https#

[https]
type = https
local_ip = example.com
local_port = 443
custom_domains = example.com
curl -vik --resolve example.com:8443:127.0.0.1 https://example.com:8443/

auto reload#

# 自动重载 - 需要开启 admin 端口
# -e close_write 只监听单个事件 - k8s mount 不触发 close_write - 因为不是写文件
# 会触发 delete
while inotifywait -e attrib /etc/frp/frpc.ini; do frpc -c /etc/frp/frpc.ini reload ; done
while inotifywait -e attrib /etc/frp/frpc.ini; do echo frpc.ini changed ; done
# 监听目录
inotifywait -r -m --format "%e %f" /etc/frp
inotifywait -e attrib -m --format "%e %f" /etc/frp/frpc.ini

FAQ#

get sid from visitor error#

nat 穿透率低, 目前没有解决方案. 如果需要 p2p 建议选择其他方案, 例如 tinc.

start new visitor connection error: custom listener for [] doesn't exist#

visitor 端出现, 应该是没有配置 server_name