一、环境准备 
说明一下frp 分为: 1、frps 服务端(外网使用);2、frpc 客户端(内网使用)

模拟环境:家用服务器没有公网 ip  
                  云服务器有公网 ip

先说frp工作原理

frp主要由两个组件组成:客户端(frpc) 和 服务端(frps)。通常情况下,服务端部署在具有公网 IP 地址的机器上,而客户端部署在需要穿透的内网服务所在的机器上。

由于内网服务缺乏公网 IP 地址,因此无法直接被非局域网内的用户访问。用户通过访问服务端的 frps,frp 负责根据请求的端口或其他信息将请求路由到相应的内网机器,从而实现通信。

代理

在 frp 中,一个代理对应一个需要公开访问的内网服务。一个客户端可以同时配置多个代理,以满足不同的需求。

代理类型

frp 支持多种代理类型,以适应不同的使用场景。以下是一些常见的代理类型:

  • TCP:提供纯粹的 TCP 端口映射,使服务端能够根据不同的端口将请求路由到不同的内网服务。

  • UDP:提供纯粹的 UDP 端口映射,与 TCP 代理类似,但用于 UDP 流量。

  • HTTP:专为 HTTP 应用设计,支持修改 Host Header 和增加鉴权等额外功能。

  • HTTPS:类似于 HTTP 代理,但专门用于处理 HTTPS 流量。

  • STCP:提供安全的 TCP 内网代理,要求在被访问者和访问者的机器上都部署 frpc,不需要在服务端暴露端口。

  • SUDP:提供安全的 UDP 内网代理,与 STCP 类似,需要在被访问者和访问者的机器上都部署 frpc,不需要在服务端暴露端口。

  • XTCP:点对点内网穿透代理,与 STCP 类似,但流量不需要经过服务器中转。

  • TCPMUX:支持服务端 TCP 端口的多路复用,允许通过同一端口访问不同的内网服务。

每种代理类型适用于不同的使用情境,您可以根据需求选择合适的代理类型来配置 frp。

安装包地址:https://github.com/fatedier/frp/releases/tag/v0.61.2

二、外网服务器部署 frps      
1、上传压缩包到任意目录然后解压,示例 /usr/local/ 目录

# 解压到当前目录
tar -zxvf frp_0.61.1_linux_amd64.tar.gz
 
# 移动并改名
mv frp_0.61.1_linux_amd64 /usr/local/frp
 
# 创建 frp 用户
useradd -r -s /usr/sbin/nologin -M frp
 
# 授权文件夹
chown -R frp:frp /usr/local/frp

2、备份并修改 frps 配置文件 frps.toml

# 备份配置文件
cp /usr/local/frp/frps.toml /usr/local/frp/frps.tomlbak
# 直接执行覆盖原来的配置

tee /usr/local/frp/frps.toml > /dev/null << 'EOF'
[common]
# 服务端绑定的端口,用于客户端与服务端通信的核心端口。需要确保该端口对外网开放。
bind_port = 7000
 
# HTTP 代理的虚拟主机端口。用于通过域名访问客户端的 HTTP 服务,需配置 DNS。
vhost_http_port = 80
 
# HTTPS 代理的虚拟主机端口。用于通过域名访问客户端的 HTTPS 服务,需配置 DNS。
vhost_https_port = 443
 
# 密钥,是自定义的,想怎么填写就怎么填写,但客户端和服务端必须保持一致,确保安全通信。
token = "Frp20241129"
 
# 仪表板端口,用于查看 frps 服务端的运行状态和管理客户端连接。访问地址:服务器IP:7500
dashboard_port = 7500
EOF

3、加入系统服务并开机自启动

# 加入系统服务
cat <<EOF | tee /etc/systemd/system/frps.service
[Unit]
Description=FRP Server
After=network-online.target
Wants=network-online.target
[Service]
User=frp
WorkingDirectory=/usr/local/frp
ExecStart=/usr/local/frp/frps -c /usr/local/frp/frps.toml
Environment=FRP_LOG_LEVEL=info
Restart=always
RestartSec=5s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF

# 重载配置使其生效
systemctl daemon-reload
 
# 启动 frps 服务
systemctl start frps.service
 
# 设置开机自启
systemctl enable frps.service
三、内网服务器部署 frpc    
 1、上传压缩包到任意目录然后解压,示例 /usr/local/ 目录

# 解压到当前目录
tar -zxvf frp_0.61.1_linux_amd64.tar.gz
 
# 移动并改名
mv frp_0.61.1_linux_amd64 /usr/local/frp
 
# 创建 frp 用户
useradd -r -s /usr/sbin/nologin -M frp
 
# 授权文件夹
chown -R frp:frp /usr/local/frp

2、备份并修改 frpc 配置文件 frpc.toml 

cp /usr/local/frp/frpc.toml /usr/local/frp/frpc.tomlbak

# 直接执行覆盖原来的配置
tee /usr/local/frp/frpc.toml > /dev/null << 'EOF'
[common]
server_addr = "此处引号内填写外网服务器的公网IP"
# 服务端口
server_port = 7000
# 密钥,需要和 frps 配置一致
token = "Frp20241129"
 
# 配置每个内网服务
 
# 服务名称,随便写,自己能看懂就行
[ssh_test]
type = "tcp"
local_ip = "127.0.0.1"
# 家用服务器上暴露的端口
local_port = 22
# 外网服务器上暴露的端口,ssh时候,使用外网 IP 和这个端口可以远程访问家里的服务器
remote_port = 2299
 
# 服务名称,随便写,自己能看懂就行
[web_server]
type = "http"
local_ip = "127.0.0.1"
# 家用服务器上暴露的端口
local_port = 80
# 外网服务器上暴露的端口,使用外网 IP 和这个端口可以远程访问家里web服务,这个配置和域名
#remote_port = 6001
# 用于 HTTP 和 HTTPS 域名的代理。填写自己的域名,没有就注释这个配置,启用 remote_port 配置
#remote_port 和 custom_domains 配置冲突,只能使用一个
custom_domains = "www.adbc.cn"
 
 
EOF

3、加入系统服务并开机自启动

# 加入系统服务
cat <<EOF | tee /etc/systemd/system/frpc.service
[Unit]
Description=FRP Client
After=network-online.target
Wants=network-online.target
[Service]
User=frp
WorkingDirectory=/usr/local/frp
ExecStart=/usr/local/frp/frpc -c /usr/local/frp/frpc.toml
Environment=FRP_LOG_LEVEL=info
Restart=always
RestartSec=5s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF

# 重载配置使其生效
systemctl daemon-reload
 
# 启动 frps 服务
systemctl start frpc.service
 
# 设置开机自启
systemctl enable frpc.service
四,查看 frpc 是否在 frps 内注册,并测试网页访问    

五、其他
拆分 frpc 配置与 https 证书配置

[common]
server_addr = "此处引号内填写外网服务器的公网IP,可以是域名"
server_port = 7000
token = "Frp20241129"
tls_enable = true
# 拆分配置,需要打开即可
#includes = "./confd/*.toml"
 
# 网页https配置,无需nginx转发
[web_server]
type = https
plugin = https2http
plugin_local_addr = 127.0.0.1:80
custom_domains = www.mnshy.cn   # 修改未你的域名,记得配置
plugin_crt_path = /usr/local/ssl/go-ssl-master/cert_storage/mnshy.cn.crt  # 证书路径
plugin_key_path = /usr/local/ssl/go-ssl-master/cert_storage/mnshy.cn.key  # 证书密钥路径
plugin_header_X-From-Where = frp
use_encryption = true
force_https = true

问题说明:如果遇到 frps 启动失败:出现了 permission denied 错误
解决方法:将 frps 的启动用户修改为root,因为普通用户没有权限绑定到 1024 以下的端口(例如 80 端口)。

推荐配置

# 1) 生成 CA 私钥和自签根证书
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=MyCA/CN=MyCA" \
  -out ca.crt

# 2) 生成服务端私钥和 CSR,然后用 CA 签发
openssl genrsa -out server.key 4096
openssl req -new -key server.key \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=FRP/CN=frps.my.org" \
  -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 3650 -sha256

# 3) 生成客户端私钥和 CSR,然后用 CA 签发
openssl genrsa -out client.key 4096
openssl req -new -key client.key \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=FRP-Client/CN=frpc001" \
  -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out client.crt -days 3650 -sha256

# 最后将以下文件分发到对应节点:
# - frps 上: ca.crt, server.crt, server.key
# - frpc 上: ca.crt, client.crt, client.key

 

frps.toml

[common]
# 服务端绑定的端口,用于客户端与服务端通信的核心端口。需要确保该端口对外网开放。
bind_port = XXX
 
# HTTP 代理的虚拟主机端口。用于通过域名访问客户端的 HTTP 服务,需配置 DNS。
#vhost_http_port = 80
 
# HTTPS 代理的虚拟主机端口。用于通过域名访问客户端的 HTTPS 服务,需配置 DNS。
#vhost_https_port = 443
 
# 密钥,是自定义的,想怎么填写就怎么填写,但客户端和服务端必须保持一致,确保安全通信。
token = "XXX"
 
# 仪表板端口,用于查看 frps 服务端的运行状态和管理客户端连接。访问地址:服务器IP:端口
dashboard_port = XXX


##日志
log_file = ./frps.log  
log_level = debug 
log_max_days = 7  


# 开启 TLS 并使用自己的服务器证书
tls_enable              = true
tls_cert_file           = ./server.crt
tls_key_file            = ./server.key
#
# # 要验证客户端证书,指定根 CA,并打开客户端证书校验
tls_client_cert_auth    = true
tls_trusted_ca_file     = ./ca.crt


frpc.toml

[common]
server_addr = "公网IP"
# 服务端口
server_port = XXX 
# 密钥,需要和 frps 配置一致
token = "XXXX"


开启tls连接
tls_enable      = true
# 必须和 server.crt 中 CN 或 SAN 匹配
tls_server_name = IP
tls_ca_cert_file    = ./ca.crt
tls_cert_file       = ./client.crt
tls_key_file        = ./client.key


log_file = ./frpc.log
# # 日志级别
log_level = debug
# # 日志最多记录天数
log_max_days = 3
 
# 服务名称,随便写,自己能看懂就行
[ssh]
type = "tcp"
local_ip = "ssh服务的ip"
# 家用服务器上暴露的端口
local_port = 22
# 外网服务器上暴露的端口,ssh时候,使用外网 IP 和这个端口可以远程访问家里的服务器
remote_port = XXX
###和上面的tls互斥
#use_encryption = true
use_compression = true


怎么测试开启了tls加密:

#用 openssl 测试握手,可以直接跟它做一次 TLS 握手,看能不能成功:

openssl s_client \
  -connect frpsip:port \
  -CAfile /path/to/ca.crt \
  -cert  /path/to/client.crt \
  -key   /path/to/client.key

如果输出里有:

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
…
SSL handshake has read XXXX bytes and written YYYY bytes

那肯定走的是 TLS 隧道。

第二种方式抓包:

在frps服务器上:

tcpdump -i eth0 port XXX -s 0 -X


可以直接在十六进制里看到标准的 TLS 记录头了:
…  
0x0030:  005d d2d1 **17 03 03 00 1d** a9 f8 49 1e 95 88 23 …  
…

    0x17:TLS Application Data

    0x03 0x03:TLS 1.2/1.3 协议版本

    0x00 0x1d:后面跟 29 字节的加密载荷

这个 17 03 03 就是典型的“TLS 应用数据”记录标识,说明这一段已经落入了 TLS 加密隧道。

同理,你之前如果能在任意包的开头看到 16 03 03,就表明是 TLS 握手(ClientHello/ServerHello);而 17 03 03 则是握手后已加密的应用数据。


 

利用FRP内网穿透从公网访问家里NAS场景实践

公网服务器上面安装frps服务端

[common]
bind_address = 0.0.0.0
bind_port = FRPS的监听端口

authentication_method = token
authenticate_heartbeats = false
authenticate_new_work_conns = false
token = FRPS服务TOKEN

log_file =  /usr/local/frp/logs/frps.log
log_level = info
log_max_days = 30

家中内网服务器安装frpc

[common]
server_addr = 云服务器公网IP
server_port = FRPS的监听端口
authentication_method = token
authenticate_heartbeats = false
token = FRPS服务TOKEN
log_file =  /usr/local/frp/logs/frpc.log
log_level = info
log_max_days = 30


[918_synologyds220_http]
type = tcp
local_ip = 192.168.31.200
local_port = 5000
remote_port = 5000

这样可以从公网IP访问家里的NAS 

图片

3、问题

但是带来这样一个问题,由于通过frp内网穿透NAS完全暴露公网 ,NAS上面看到公网访问过来的IP都是本机IP 192.168.31.200

如果有暴力破解攻击,然后NAS又开启了自动封锁,这时就会出现问题,封锁了192.168.31.200这个IP导致正常从公网访问的其他用户无法正常登录了

图片

4、解决思路及探索过程

通过内网穿透外部访问NAS的看到的IP要为真实的源公网IP frpc 中type为tcp类型这种方式就不可行

找到frp的官方文档中这篇实践示例找到的解决方案

https://gofrp.org/zh-cn/docs/examples/vhost-http/

HTTP 类型的代理非常适合将内网的 Web 服务通过自定义域名提供给外部用户。 相比于 TCP 类型代理,HTTP 代理不仅可以复用端口,还提供了基于 HTTP 协议的许多功能 简单配置 HTTP 类型的代理,您可以让用户通过自定义域名访问内网的 Web 服务

5、改造的具体步骤

1、修改frps.ini 添加
指定 HTTP 请求的监听端口为 5000
cat /usr/local/frp/frps.ini 
[common]
bind_address = 0.0.0.0
bind_port = 8111
vhost_http_port = 5000

authentication_method = token
authenticate_heartbeats = false
authenticate_new_work_conns = false
token = XXXXXXXXXX

log_file =  /usr/local/frp/logs/frps.log
log_level = info
log_max_days = 30
重新启动frps服务

图片

2、修改frpc上的配置
[common]
`server_addr = 47.1XX.9X.2X7
server_port = 8111
authentication_method = token
authenticate_heartbeats = false
authenticate_new_work_conns = false
token = XXXXXXXXXX
log_file =  /usr/local/frp/logs/frpc.log
log_level = info
log_max_days = 30

[Synology_DSM5000_domain]
type = http
local_ip = 192.168.31.200
local_port = 5000
custom_domains =  47.1XX.9X.2X7`

图片

然后重新启动frpc服务

3、验证问题解决

这时再从公网访问NAS这时看到连接过来的用户IP就是公网真实源IP 

图片

更多推荐