一、需求背景
目前有两台服务器:
公网服务器
系统环境:
CentOS 7.9.2009 x86_64
宝塔面板
Nginx
公网 IP用途:
- 部署 FRP 服务端
frps - 作为公网入口
- 使用宝塔 Nginx 配置域名
- 使用 HTTPS 访问内网服务
内网设备
系统环境:
Armbian 23.02.2 Jammy
Ubuntu 22.04目前运行:
noVNC:
127.0.0.1:6081
1Panel:
127.0.0.1:2333目标:
https://vnc.agblr.xyz
↓
访问 Armbian noVNC
https://a.agblr.xyz
↓
访问 Armbian 1Panel并且内网的 6081、2333 不直接暴露到公网。
二、最终架构
整体架构如下:
Internet
│
▼
Cloudflare / DNS
│
▼
CentOS 公网服务器
宝塔 + Nginx
:80 / :443
│
┌─────────────┴─────────────┐
│ │
▼ ▼
127.0.0.1:16081 127.0.0.1:2333
noVNC代理 1Panel代理
│ │
└────────── FRPS ───────────┘
│
:7000
│
Internet
│
FRPC
│
┌────────┴────────┐
│ │
▼ ▼
127.0.0.1:6081 127.0.0.1:2333
noVNC 1Panel
│
└──── Armbian ────公网只需要开放:
80/TCP
443/TCP
7000/TCPFRP 映射出来的:
16081
2333只监听:
127.0.0.1因此无法直接从公网通过:
公网IP:16081
公网IP:2333访问。
三、为什么不用宝塔 FRP 插件
最开始尝试使用宝塔的 FRP 管理器,但报错:
AttributeError: module 'public' has no attribute 'get_server_ip'完整关键位置:
self.config['serveraddr'] = public.get_server_ip()原因是:
宝塔版本:7.9.2
CentOS:7.9.2009
Python:3.7.9FRP 插件调用了旧版宝塔 API:
public.get_server_ip()而当前宝塔代码中已经不存在该接口。
因此最终放弃宝塔 FRP 插件,改为:
FRP 手动安装
+
systemd 管理
+
宝塔只负责 Nginx / 域名 / HTTPS这种方式也更加稳定。
四、CentOS 安装 FRP 服务端
创建目录:
mkdir -p /www/server/frp
cd /www/server/frp下载 FRP:
wget gh-proxy.com/https://github.com/fatedier/frp/releases/download/v0.70.1/frp_0.70.1_linux_amd64.tar.gz解压:
tar -zxf frp_0.70.1_linux_amd64.tar.gz复制 frps:
cp frp_0.70.1_linux_amd64/frps /www/server/frp/frps
chmod 755 /www/server/frp/frps验证:
/www/server/frp/frps --version输出:
0.70.1说明安装成功。
五、一个实际踩过的坑:下载文件损坏
第一次下载时,由于目录里已经有同名文件,wget 实际把新的完整文件保存为了:
frp_0.70.1_linux_amd64.tar.gz.1但后续却执行:
tar -zxf frp_0.70.1_linux_amd64.tar.gz导致解压的是旧的残缺文件。
报错:
gzip: stdin: unexpected end of file
tar: Unexpected EOF in archive
tar: Error is not recoverable随后复制出来的 frps 也不完整:
/www/server/frp/frps --version直接出现:
Segmentation fault解决方法是删除旧文件重新下载:
rm -rf /www/server/frp/*重新下载、解压后:
/www/server/frp/frps --version正常返回:
0.70.1以后遇到类似问题可以先检查:
ls -lh以及:
gzip -t frp_0.70.1_linux_amd64.tar.gz如果 gzip -t 没有输出,一般说明 gzip 文件完整。
六、配置 FRP 服务端
生成一个随机 Token:
openssl rand -hex 32例如:
52ba40b2e66447009793152b1fe5aa03ba175bed401bfde15922e8969925d831实际使用时请使用自己生成的 Token。
创建:
vi /www/server/frp/frps.toml配置:
bindAddr = "0.0.0.0"
bindPort = 7000
proxyBindAddr = "127.0.0.1"
auth.method = "token"
auth.token = "替换成自己的TOKEN"
allowPorts = [
{ single = 16081 },
{ single = 2333 }
]这里有两个比较重要的地方。
proxyBindAddr
proxyBindAddr = "127.0.0.1"意味着 FRP 客户端映射出来的端口只监听:
127.0.0.1不会变成:
0.0.0.0这样可以避免 16081 和 2333 直接暴露公网。
allowPorts
allowPorts = [
{ single = 16081 },
{ single = 2333 }
]限制客户端只允许申请:
16081
2333避免 FRP 客户端随意创建其他公网映射。
七、验证 FRP 服务端配置
执行:
/www/server/frp/frps verify -c /www/server/frp/frps.toml如果没有配置错误即可继续。
八、配置 systemd
创建:
vi /etc/systemd/system/frps.service内容:
[Unit]
Description=FRP Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/www/server/frp/frps -c /www/server/frp/frps.toml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target加载:
systemctl daemon-reload设置开机启动:
systemctl enable --now frps检查:
systemctl status frps --no-pager -l检查监听:
ss -lntp | grep 7000正常应该看到:
0.0.0.0:7000九、开放 FRP 服务端端口
CentOS firewalld:
firewall-cmd --permanent --add-port=7000/tcp
firewall-cmd --reload宝塔安全页面也允许:
TCP 7000如果使用腾讯云、阿里云等,还需要在云服务器安全组开放:
TCP 7000注意:
16081
2333不需要开放。
十、Armbian 安装 FRP 客户端
先查看 CPU 架构:
uname -m如果:
aarch64则使用 ARM64 版本。
创建目录:
mkdir -p /opt/frp
cd /opt/frp下载:
wget gh-proxy.com/https://github.com/fatedier/frp/releases/download/v0.70.1/frp_0.70.1_linux_arm64.tar.gz解压:
tar -zxf frp_0.70.1_linux_arm64.tar.gz复制:
cp frp_0.70.1_linux_arm64/frpc /opt/frp/frpc
chmod 755 /opt/frp/frpc验证:
/opt/frp/frpc --version应该显示:
0.70.1十一、确认内网服务
noVNC:
ss -lntp | grep 6081确认:
127.0.0.1:6081测试:
curl -I http://127.0.0.1:60811Panel:
ss -lntp | grep 2333确认:
127.0.0.1:2333十二、配置 FRP 客户端
创建:
nano /opt/frp/frpc.toml内容:
serverAddr = "CentOS公网IP"
serverPort = 7000
auth.method = "token"
auth.token = "和CentOS完全相同的TOKEN"
[[proxies]]
name = "novnc"
type = "tcp"
localIP = "127.0.0.1"
localPort = 6081
remotePort = 16081
[[proxies]]
name = "1panel"
type = "tcp"
localIP = "127.0.0.1"
localPort = 2333
remotePort = 2333最终映射关系:
Armbian 127.0.0.1:6081
→
CentOS 127.0.0.1:16081以及:
Armbian 127.0.0.1:2333
→
CentOS 127.0.0.1:2333十三、启动 FRP 客户端测试
先检查:
/opt/frp/frpc verify -c /opt/frp/frpc.toml前台运行:
/opt/frp/frpc -c /opt/frp/frpc.toml成功后,在 CentOS 上执行:
ss -lntp | grep -E '16081|2333'应该看到:
127.0.0.1:16081
127.0.0.1:2333测试 noVNC:
curl -I http://127.0.0.1:16081测试 1Panel:
curl -I http://127.0.0.1:2333只要有正常 HTTP 响应,FRP 就已经打通。
十四、Armbian 设置 frpc 开机启动
创建:
vi /etc/systemd/system/frpc.service内容:
[Unit]
Description=FRP Client
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/opt/frp/frpc -c /opt/frp/frpc.toml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target执行:
systemctl daemon-reload
systemctl enable --now frpc检查:
systemctl status frpc --no-pager -l查看实时日志:
journalctl -u frpc -f服务端日志:
journalctl -u frps -f十五、宝塔配置 noVNC 域名
例如使用:
vnc.agblr.xyz
vnc.23332048.xyz将域名解析到:
CentOS公网IP然后创建宝塔站点。
noVNC 的 Nginx 配置可以使用:
server
{
listen 80;
server_name vnc.agblr.xyz vnc.23332048.xyz;
root /www/wwwroot/vnc.agblr.xyz;
location ^~ /.well-known/acme-challenge/
{
root /www/wwwroot/vnc.agblr.xyz;
allow all;
}
location /
{
proxy_pass http://127.0.0.1:16081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_connect_timeout 30s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_cache off;
}
access_log /www/wwwlogs/vnc.agblr.xyz.log;
error_log /www/wwwlogs/vnc.agblr.xyz.error.log;
}其中最重要的是:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";因为 noVNC 使用 WebSocket。
如果没有这几行,很可能出现:
网页可以打开
但是 VNC 无法连接
WebSocket connection failed十六、宝塔配置 1Panel 域名
例如:
a.agblr.xyz
a.23332048.xyz对应:
127.0.0.1:2333配置:
server
{
listen 80;
server_name a.agblr.xyz a.23332048.xyz;
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/a.agblr.xyz;
#SSL-START
#error_page 404/404.html;
#SSL-END
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
location ^~ /.well-known/acme-challenge/
{
root /www/wwwroot/a.agblr.xyz;
allow all;
}
location /
{
proxy_pass http://127.0.0.1:2333;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_set_header X-Forwarded-Host $host;
proxy_connect_timeout 30s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
client_max_body_size 1024m;
}
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
access_log /www/wwwlogs/a.agblr.xyz.log;
error_log /www/wwwlogs/a.agblr.xyz.error.log;
}之所以同样保留 WebSocket,是因为 1Panel 的:
SSH终端
容器终端
实时日志等功能都可能依赖 WebSocket。
十七、检查 Nginx 配置
每次修改完都先执行:
/www/server/nginx/sbin/nginx -t如果显示:
syntax is ok
test is successful再重新加载:
/www/server/nginx/sbin/nginx -s reload不要直接重启 Nginx。
这样即使配置写错,也不会直接把现有网站全部打断。
十八、曾遇到的 Nginx 报错
遇到过:
nginx: [emerg] "location" directive is not allowed here原因是把:
location /
{
}写到了:
server
{
}外面。
Nginx 正确结构必须是:
server
{
listen 80;
server_name example.com;
location /
{
...
}
}不能:
location /
{
}
server
{
}十九、Cloudflare 域名如何访问 noVNC
如果另有一个域名:
xxx.com托管在 Cloudflare,可以让它访问相同的 noVNC。
Cloudflare DNS:
类型:A
名称:@
内容:CentOS公网IP
代理状态:已代理(橙色云)访问:
https://xxx.com时,浏览器实际上还是访问:
Cloudflare :443
→
CentOS Nginx :443然后由 Nginx 再反代到:
127.0.0.1:16081这是正常行为。
DNS 本身只负责:
域名 → IPDNS 不负责:
域名 → IP:端口所以不能配置:
xxx.com → CentOSIP:16081二十、CNAME 能不能用
可以。
例如:
xxx.com
CNAME
vnc.agblr.xyz但是要注意:
CNAME 只是 DNS 别名。
访问:
https://xxx.com时 HTTP Host 仍然是:
xxx.com不会自动变成:
vnc.agblr.xyz因此 Nginx 仍然需要:
server_name vnc.agblr.xyz xxx.com;否则 Nginx 可能匹配到其他站点。
同时 CNAME 不能写:
vnc.agblr.xyz:16081也不能写:
https://vnc.agblr.xyz/vnc.html因为 DNS 不理解端口和 URL 路径。
二十一、URL 跳转与反向代理的区别
如果只要求:
访问 xxx.com然后浏览器地址变成:
vnc.agblr.xyz可以使用 Cloudflare Redirect Rules。
这种属于:
301/302 跳转最终地址栏会发生变化。
例如:
https://xxx.com
↓
302
↓
https://vnc.agblr.xyz但是如果要求:
访问 https://xxx.com
地址栏始终保持:
https://xxx.com
实际访问:
noVNC则应该使用:
Nginx reverse_proxy而不是 URL 跳转。
二十二、Cloudflare + noVNC 最终链路
最终推荐:
浏览器
│
│ HTTPS / WSS
▼
Cloudflare
│
│ HTTPS
▼
CentOS 宝塔 Nginx
│
│ HTTP + WebSocket
▼
127.0.0.1:16081
│
▼
FRPS
│
▼
FRPC
│
▼
Armbian 127.0.0.1:6081
│
▼
noVNC如果使用 Cloudflare 橙色云,建议:
SSL/TLS:
Full (strict)同时源站也申请正常 HTTPS 证书。
二十三、常用排错命令
查看 FRPS
systemctl status frps日志:
journalctl -u frps -f监听:
ss -lntp | grep 7000查看 FRPC
systemctl status frpc日志:
journalctl -u frpc -f查看映射端口
CentOS:
ss -lntp | grep -E '16081|2333'正常:
127.0.0.1:16081
127.0.0.1:2333noVNC 测试
CentOS:
curl -I http://127.0.0.1:160811Panel 测试
curl -I http://127.0.0.1:2333Nginx 检查
/www/server/nginx/sbin/nginx -t重新加载:
/www/server/nginx/sbin/nginx -s reload二十四、安全建议
当前架构中最重要的安全设计是:
proxyBindAddr = "127.0.0.1"因此:
16081
2333没有直接暴露公网。
公网真正开放的是:
80
443
7000推荐继续保持这种模式:
公网
↓
Nginx
↓
本机 FRP 映射端口而不要直接:
公网IP:16081
公网IP:2333尤其是 1Panel 这种管理后台,更不建议直接暴露原始端口。
另外建议:
- FRP 使用长随机 Token
- 1Panel 开启安全入口
- 1Panel 开启强密码或二次认证
- noVNC 本身设置 VNC 密码
- 对管理后台增加访问限制
- 宝塔开启 HTTPS
- Cloudflare 使用 Full (strict)
- 不对公网开放 16081 / 2333
二十五、最终效果
最终可以实现:
https://vnc.agblr.xyz
↓
Armbian noVNChttps://a.agblr.xyz
↓
Armbian 1Panel如果 Cloudflare 还有:
xxx.com也可以通过:
A / CNAME
+
Nginx server_name让:
https://xxx.com访问同一个 noVNC 服务。
整个方案不依赖第三方免费内网穿透节点,核心链路全部自己控制:
Armbian
↓
FRPC
↓
自己的公网 CentOS
↓
FRPS
↓
宝塔 Nginx
↓
自己的域名相比共享式内网穿透,优势主要是:
节点自己控制
带宽自己控制
延迟更加可控
不限隧道数量
不限自定义域名
不限第三方免费流量配额
支持 HTTPS
支持 WebSocket
容易排错对于 noVNC、1Panel、SSH Web Terminal 等需要长期稳定访问的服务,这种架构更加合适。
评论