一、 背景与架构说明
1.1 为什么需要 SNI 分流
FRP 默认使用独立的端口进行通讯,但公有云环境往往只开放 80 和 443 端口。为了让 FRP 与 HTTPS 站点、AnyTLS 等协议共享 443 端口,我们利用 Nginx 的 ngx_stream_ssl_preread 模块在四层读取 TLS 客户端握手时的 SNI (Server Name Indication) 字段,根据域名将流量转发到不同的后端服务。
1.2 架构一览
- Nginx:监听 443 端口,完成 SNI 分流。
frp.example.com→ FRP 服务(127.0.0.1:7000)emby.example.com→ Emby HTTPS 站点(127.0.0.1:8001)anytls.example.com→ AnyTLS 服务(127.0.0.1:65443)- 未知域名 → 拒绝 SSL 握手(
127.0.0.1:8002)
- FRP 服务端 (frps):监听本地 7000 端口,接受 frpc 连接。
- FRP 客户端 (frpc):在内网以 Docker 方式运行,将内网服务映射到公网。
二、 安装 FRP 服务端 (frps)
2.1 下载并解压
在公网服务器上操作,首先下载 FRP 最新版(以 v0.69.1 为例,可去 Releases 页 查看更新):
cd /tmp
wget https://github.com/fatedier/frp/releases/download/v0.69.1/frp_0.69.1_linux_amd64.tar.gz
tar -zxvf frp_0.69.1_linux_amd64.tar.gz
2.2 安装到系统目录
创建配置目录并移动二进制文件:
sudo mkdir -p /etc/frp
sudo cp frp_0.69.1_linux_amd64/frps /etc/frp/
sudo cp frp_0.69.1_linux_amd64/frps.toml /etc/frp/
2.3 编写 frps 配置文件
编辑 /etc/frp/frps.toml,填入以下内容:
sudo nano /etc/frp/frps.toml
bindAddr = "::"
bindPort = 7000
auth.method = "token"
auth.token = "你的安全密码"
bindAddr = "::"表示同时监听 IPv4 与 IPv6 的 7000 端口,其实这里可以填内网地址127.0.0.1。- 此处使用 token 认证,防止未授权客户端连接。
2.4 注册为 systemd 服务
创建服务单元文件:
sudo nano /etc/systemd/system/frps.service
[Unit]
Description=Frp Server Service
After=network.target
[Service]
Type=simple
User=root
Restart=on-failure
RestartSec=5s
ExecStart=/etc/frp/frps -c /etc/frp/frps.toml
[Install]
WantedBy=multi-user.target
保存后启动并设置开机自启:
sudo systemctl daemon-reload
sudo systemctl enable frps
sudo systemctl start frps
sudo systemctl status frps
查看日志确认运行正常:
journalctl -u frps -f
此时 FRP 服务已在 7000 端口运行,若修改配置文件后,使用systemctl restart frps重启服务。
三、 Nginx SNI 分流配置
3.1 确保 Nginx 支持 stream 模块
首先检查 Nginx 版本及已编译模块:
nginx -V 2>&1 | grep -o "with-stream"
若无输出,则需要安装 nginx-full 或重新编译 Nginx,确保包含 ngx_stream_module.so。可使用以下三种方式(任选一种)安装模块:
sudo apt install libnginx-mod-stream -y
sudo apt install nginx-module-stream -y
sudo apt install nginx-full -y
在 Nginx 主配置顶部加载:
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
路径可能因发行版不同而异,常见为
/usr/lib/nginx/modules/或/usr/share/nginx/modules/。
3.2 编写 Nginx 主配置
编辑 /etc/nginx/nginx.conf,以下为支持 SNI 分流与 HTTPS 站点的混合配置(请将域名替换为你自己的):
user nginx;
worker_processes auto;
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
# 四层 TCP/UDP 代理
stream {
# SNI 分流映射表
map $ssl_preread_server_name $backend {
emby.example.com 127.0.0.1:8001;
frp.example.com 127.0.0.1:7000;
anytls.example.com 127.0.0.1:65443;
default 127.0.0.1:8002;
}
server {
listen 443;
listen [::]:443;
ssl_preread on;
proxy_pass $backend;
}
}
# 七层 HTTP 配置
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;
# 全局 SSL 证书设置
ssl_certificate /etc/mihomo/fullchain.pem;
ssl_certificate_key /etc/mihomo/privkey.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13_AES_128_GCM_SHA256:TLS13_AES_256_GCM_SHA384:TLS13_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
resolver 1.1.1.1 valid=60s;
resolver_timeout 2s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# HTTP → HTTPS 重定向
server {
listen 80;
listen [::]:80;
server_name _;
return 301 https://$host$request_uri;
}
# Emby 的 HTTPS 站点
server {
listen 127.0.0.1:8001 ssl;
http2 on;
server_name emby.example.com;
location / {
proxy_pass http://127.0.0.1:5001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# 默认 SSL 拒绝握手(处理未匹配的 SNI)
server {
listen 127.0.0.1:8002 ssl;
http2 on;
server_name _;
ssl_reject_handshake on;
}
}
stream块中,map指令根据 TLS SNI 分配后端地址。http块中,仅处理匹配的 HTTPS 流量(如 Emby),其余域名直接拒绝握手,降低特征暴露风险。- 请确保 SSL 证书路径
/etc/mihomo/fullchain.pem和/etc/mihomo/privkey.key已正确申请或存在,若使用其他证书请修改路径。
3.3 启用配置并重载 Nginx
sudo nginx -t # 检查配置语法
sudo systemctl reload nginx
此时,443 端口由 Nginx 统一接管,FRP 的流量被内部转发至 127.0.0.1:7000。请勿忘记在云防火墙放行 80 和 443 端口:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
四、 FRP 客户端配置 (frpc)
4.1 部署方式
本文推荐使用 Docker 运行 frpc,便于版本管理和资源隔离。以群晖或任意支持 Docker 的 Linux 内网主机为例。
4.2 编写 frpc.toml 配置文件
在主机上创建 /volume1/docker/docker-compose/frpc/frpc.toml(路径可根据实际修改),内容如下:
serverAddr = 'frp.example.com'
serverPort = 443
auth.method = "token"
auth.token = "你的安全密码"
transport.proxyURL = "http://username:[email protected]:7890"
[[proxies]]
name = "emby-tcp"
type = "tcp"
localIP = "192.168.2.150"
localPort = 8096
remotePort = 5001
serverAddr:填写你的 FRP 域名(已通过 SNI 分流至 frps)。serverPort = 443:因为 Nginx 在 443 上监听并转发到 FRP。auth.token:与服务端保持一致。transport.proxyURL:可选,通过本地代理连接 frp 服务器。[[proxies]]:定义单个代理规则,此处将内网192.168.2.150:8096(Emby) 映射到公网服务器的127.0.0.1:5001(结合 Nginx 上文的proxy_pass http://127.0.0.1:5001;使用)。
4.3 Docker Compose 运行
创建 docker-compose.yml(位于相同目录):
services:
frpc:
image: fatedier/frpc:v0.69.1
container_name: frpc
restart: always
network_mode: host
command: -c /etc/frp/frpc.toml
volumes:
- /volume1/docker/docker-compose/frpc/frpc.toml:/etc/frp/frpc.toml
启动客户端:
docker-compose up -d
docker logs frpc
若日志显示 start proxy success,表示隧道建立成功。现在访问 https://emby.example.com 即可安全访问内网 Emby 服务。
五、 管理与优化
5.1 服务端状态监控
- FRP 服务日志:
journalctl -u frps -f - FRP 控制面板(可选):可在
frps.toml中增加webServer配置,开启 Dashboard 查看客户端连接状态。
5.2 添加更多穿透服务
在 frpc.toml 中增加 [[proxies]] 块,同时在 Nginx 的 stream.map 与 http 块中配置对应域名和后端即可实现多服务共享 443 端口。
5.3 安全注意事项
- 务必使用强密码保护
auth.token。 - 建议为每个穿透服务申请并配置好 SSL 证书(可使用泛域名证书),关闭不安全的访问入口。
- 定期更新 FRP 版本,并关注安全公告。
至此,一套基于 FRP + Nginx SNI 分流的反向代理与内网穿透方案就搭建完成了。欢迎小伙伴们在评论区留言交流。
喜欢的话,留下你的评论吧~