eqms内外网中转

作者: 哎呦喂
日期: 2026-05-20
目标: 将内网EQMS系统(10.188.56.27)通过公网域名 eqms.1**.com.cn 安全访问


一、架构设计

最终方案:Cloudflare Tunnel + Nginx反向代理

用户浏览器
    ↓ (HTTPS)
Cloudflare CDN
    ↓ (Cloudflare Tunnel加密通道)
cloudflared (本地服务,端口8080)
    ↓ (HTTP)
Nginx 反向代理 (localhost:8080)
    ↓ (内网HTTP)
内网EQMS系统 (10.188.56.27:80)

为什么选Cloudflare Tunnel?

方案优点缺点结论
端口转发简单需公网IP,不安全❌ 淘汰
Tailscale安全需要客户端,手机端麻烦❌ 淘汰
Cloudflare WARP安全手机端登录失败❌ 淘汰
Cloudflare Tunnel无需公网IP,HTTPS自动证书,免费需要Cloudflare账号✅ 采用

二、环境准备

2.1 系统环境

  • 服务器: Windows Server (USER-20260422QU)
  • 内网EQMS: 10.188.56.27:80
  • 内网QTI: 10.88.248.67:9001
  • 域名: 1**.com.cn (已在Cloudflare管理)
  • Nginx: 1.26.2 (C:\nginx)
  • Cloudflared: 2026.5.0

2.2 安装Cloudflared

# 下载
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe
# 保存到
C:\Users\Administrator\.cloudflared\cloudflared.exe

三、Cloudflare Tunnel配置

3.1 登录Cloudflare

cd C:\Users\Administrator\.cloudflared
.\cloudflared.exe tunnel login

浏览器打开Cloudflare授权页面,选择 1**.com.cn 域名。

3.2 创建Tunnel

# 创建tunnel
.\cloudflared.exe tunnel create my-tunnel

# 输出示例:
# Tunnel credentials written to C:\Users\Administrator\.cloudflared\46ead48d-721f-4346-939b-bc4c7a3c9b58.json
# Cloudflare chose port 8000 to 8007 for load-balancing the tunnel pool.
# Your tunnel will not try these ports until you associate them with a configuration.
# Created tunnel my-tunnel with id 46ead48d-721f-4346-939b-bc4c7a3c9b58

记录Tunnel ID: 46ead48d-721f-4346-939b-bc4c7a3c9b58

3.3 配置DNS

# 为eqms子域名创建CNAME记录
.\cloudflared.exe tunnel route dns my-tunnel eqms.1**.com.cn

# 为qti子域名创建CNAME记录
.\cloudflared.exe tunnel route dns my-tunnel qti.1*.com.cn

3.4 编写配置文件

文件: C:\Users\Administrator\.cloudflared\config.yml

tunnel: 46ead48d-721f-4346-939b-bc4c7a3c9b58
credentials-file: C:\Users\Administrator\.cloudflared\46ead48d-721f-4346-939b-bc4c7a3c9b58.json

ingress:
  # EQMS系统
  - hostname: eqms.1**.com.cn
    service: http://localhost:8080
    originRequest:
      noTLSVerify: true

  # QTI系统
  - hostname: qti.1**.com.cn
    service: http://localhost:8080
    originRequest:
      noTLSVerify: true

  # 兜底规则(必须放在最后)
  - service: http_status:404

关键配置说明:

  • service: http://localhost:8080 - 流量转发到本地Nginx
  • noTLSVerify: true - 跳过TLS验证(因为Nginx用的是HTTP)
  • 多个hostname可以指向同一个本地服务,由Nginx根据Host头路由

四、Nginx反向代理配置

4.1 目录结构

C:\nginx\
├── conf\
│   └── nginx_eqms.conf   # EQMS专属配置
├── logs\                  # 日志目录
└── html\                  # 静态文件目录

4.2 配置文件:nginx_eqms.conf

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;

    # 根据Host头路由到不同后端
    map $host $backend_url {
        hostnames;
        eqms.1**.com.cn    "http://10.188.56.27:80";
        qti.1**.com.cn     "http://10.88.248.67:9001";
        default            "http://10.188.56.27:80";
    }

    server {
        listen       8080;
        server_name  _;

        # 临时目录配置(解决Cloudflare Tunnel的temp目录问题)
        client_body_temp_path C:\Users\Administrator\.qclaw\workspace\temp\client_body_temp;
        proxy_temp_path C:\Users\Administrator\.qclaw\workspace\temp\proxy_temp;
        fastcgi_temp_path C:\Users\Administrator\.qclaw\workspace\temp\fastcgi_temp;

        # EQMS根路径
        location / {
            proxy_pass $backend_url;
            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;
            
            # Cookie域名重写(解决跨域登录问题)
            proxy_cookie_domain $backend_url $host;
            
            # 重定向重写
            proxy_redirect http://$backend_url https://$host;
        }

        # QTI后端API专用路径
        location /qti-app/ {
            proxy_pass http://10.88.248.67:9001/qti-app/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # 替换响应内容中的内网IP(解决前端JS硬编码问题)
            sub_filter '//10.88.248.67:9001/' '//$host/';
            sub_filter '//10.188.18.177:9001/' '//$host/';
            sub_filter_once off;
        }

        # 健康检查
        location /nginx-health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

4.3 启动Nginx

# 测试配置
cd C:\nginx
.\nginx.exe -t -c "C:\nginx\conf\nginx_eqms.conf" -p C:\nginx

# 启动
start .\nginx.exe -c "C:\nginx\conf\nginx_eqms.conf" -p C:\nginx

# 重载配置(修改配置后执行)
.\nginx.exe -s reload -c "C:\nginx\conf\nginx_eqms.conf" -p C:\nginx

注意: Windows下必须用 -c 和 -p 参数指定配置文件和前缀路径,否则Nginx会找不到配置。


五、启动Cloudflare Tunnel

5.1 创建temp目录

New-Item -ItemType Directory -Force -Path C:\Users\Administrator\.qclaw\workspace\temp\client_body_temp
New-Item -ItemType Directory -Force -Path C:\Users\Administrator\.qclaw\workspace\temp\proxy_temp
New-Item -ItemType Directory -Force -Path C:\Users\Administrator\.qclaw\workspace\temp\fastcgi_temp

5.2 启动Tunnel

cd C:\Users\Administrator\.cloudflared
$env:TEMP = "C:\Users\Administrator\.qclaw\workspace\temp"
$env:TMP = "C:\Users\Administrator\.qclaw\workspace\temp"
.\cloudflared.exe tunnel --config C:\Users\Administrator\.cloudflared\config.yml run my-tunnel

验证启动成功:

Retrieved configuration from local configuration file
Starting tunnel tunnelID=46ead48d-721f-4346-939b-bc4c7a3c9b58
Connected to Cloudflare

5.3 验证公网访问

浏览器访问: https://eqms.1**.com.cn

预期结果: 看到EQMS登录页面


六、问题排查与解决

6.1 EQMS登录后Cookie域名不匹配

问题: 登录成功后,服务端Set-Cookie的domain是 sg*****l.com,但浏览器访问的是 eqms.1**.com.cn,导致Cookie不生效,一直跳回登录页。

解决: 在Nginx配置中添加Cookie域名重写

proxy_cookie_domain $backend_url $host;

这会将后端返回的 Set-Cookie: JSESSIONID=xxx; Domain=sg*****l.com 重写为 Domain=eqms.1**.com.cn

6.2 QTI系统404错误

问题: 访问 qti.1**.com.cn 返回404

原因: cloudflared config.yml 中QTI的ingress规则配置错误,service指向了错误的端口

解决: 修正 config.yml 中的service地址为 http://localhost:8080(统一由Nginx根据Host头路由)

6.3 Cloudflared temp目录不存在

问题:

could not create temp file: os: file does not exist

解决: 创建temp目录并设置环境变量

$env:TEMP = "C:\Users\Administrator\.qclaw\workspace\temp"
$env:TMP = "C:\Users\Administrator\.qclaw\workspace\temp"

同时在Nginx配置中添加:

client_body_temp_path C:\Users\Administrator\.qclaw\workspace\temp\client_body_temp;
proxy_temp_path C:\Users\Administrator\.qclaw\workspace\temp\proxy_temp;

6.4 QTI前端JS硬编码内网IP

问题: QTI前端JS中硬编码了 //10.88.248.67:9001/ 和 //10.188.18.177:9001/,导致公网访问时API请求发到内网地址失败。

解决: 在Nginx的 /qti-app/ location块中添加sub_filter

sub_filter '//10.88.248.67:9001/' '//$host/';
sub_filter '//10.188.18.177:9001/' '//$host/';
sub_filter_once off;

注意: 此方案仅适用于静态JS文件。如果IP是动态生成的(例如在JS执行时拼接),需要用其他方法(如inject JS覆盖变量)。


七、最终验证

7.1 验证EQMS访问

# 测试公网域名
curl -I https://eqms.1**.com.cn

# 预期响应
HTTP/2 200
cf-ray: xxxxxxxxxxx-xxx
cf-cache-status: DYNAMIC

7.2 验证QTI访问

curl -I https://qti.1**.com.cn

# 预期响应
HTTP/2 200

7.3 验证登录功能

  1. 打开 https://eqms.1**.com.cn
  2. 输入账号密码
  3. 登录成功,跳转到主页面
  4. 功能正常(点检标准查询、检修工单等)

八、开机自启动配置

8.1 创建启动脚本

文件: C:\Users\Administrator\start-eqms-tunnel.ps1

# 启动Nginx
$nginxPath = "C:\nginx\nginx.exe"
$nginxConf = "C:\nginx\conf\nginx_eqms.conf"
Start-Process -FilePath $nginxPath -ArgumentList "-c `"$nginxConf`" -p C:\nginx" -WindowStyle Hidden

# 等待Nginx启动
Start-Sleep -Seconds 3

# 设置temp目录
$env:TEMP = "C:\Users\Administrator\.qclaw\workspace\temp"
$env:TMP = "C:\Users\Administrator\.qclaw\workspace\temp"

# 启动Cloudflare Tunnel
$cloudflared = "C:\Users\Administrator\.cloudflared\cloudflared.exe"
$tunnelConfig = "C:\Users\Administrator\.cloudflared\config.yml"
Start-Process -FilePath $cloudflared -ArgumentList "tunnel --config `"$tunnelConfig`" run my-tunnel" -WindowStyle Hidden

8.2 添加到Windows任务计划程序

$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File C:\Users\Administrator\start-eqms-tunnel.ps1"
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId "Administrator" -LogonType ServiceAccount
Register-ScheduledTask -TaskName "EQMS Tunnel" -Action $action -Trigger $trigger -Principal $principal

九、配置文件清单

9.1 Cloudflare Tunnel配置

路径: C:\Users\Administrator\.cloudflared\config.yml

tunnel: 46ead48d-721f-4346-939b-bc4c7a3c9b58
credentials-file: C:\Users\Administrator\.cloudflared\46ead48d-721f-4346-939b-bc4c7a3c9b58.json

ingress:
  - hostname: eqms.1**.com.cn
    service: http://localhost:8080
    originRequest:
      noTLSVerify: true
  - hostname: qti.1**.com.cn
    service: http://localhost:8080
    originRequest:
      noTLSVerify: true
  - service: http_status:404

9.2 Nginx配置

路径: C:\nginx\conf\nginx_eqms.conf

(完整配置见第四节)

9.3 相关文件

文件路径说明
Cloudflare Tunnel凭证C:\Users\Administrator\.cloudflared\46ead48d-721f-4346-939b-bc4c7a3c9b58.jsonTunnel身份认证
Nginx主配置C:\nginx\conf\nginx_eqms.confEQMS反向代理配置
Nginx日志C:\nginx\logs\访问日志和错误日志
启动脚本C:\Users\Administrator\start-eqms-tunnel.ps1开机自启动脚本

十、常见问题FAQ

Q1: 访问 eqms.1**.com.cn 显示502 Bad Gateway?

排查步骤:

  1. 检查cloudflared是否运行: Get-Process cloudflared
  2. 检查Nginx是否运行: Get-Process nginx
  3. 检查内网EQMS是否可访问: curl http://10.188.56.27
  4. 查看Nginx错误日志: C:\nginx\logs\error.log

Q2: 登录后一直跳回登录页?

原因: Cookie域名不匹配

解决: 确保Nginx配置中有 proxy_cookie_domain $backend_url $host;

Q3: QTI系统前端API调用失败?

原因: 前端JS硬编码了内网IP

解决: 在Nginx配置中添加sub_filter,替换响应内容中的内网IP为当前域名

Q4: 如何更新Tunnel配置?

# 1. 修改 config.yml
notepad C:\Users\Administrator\.cloudflared\config.yml

# 2. 重启Tunnel
Get-Process cloudflared | Stop-Process
cd C:\Users\Administrator\.cloudflared
.\cloudflared.exe tunnel --config config.yml run my-tunnel

十一、安全建议

  1. Cloudflare Access: 在Cloudflare Dashboard中配置Access Policy,限制只有公司邮箱能访问
  2. IP白名单: 在Cloudflare Firewall Rules中设置允许访问的IP范围
  3. 审计日志: 开启Cloudflare Audit Logs,记录所有访问
  4. 定期更新: 定期更新cloudflared版本 .\cloudflared.exe update
  5. 备份配置: 定期备份 C:\Users\Administrator\.cloudflared\ 目录

十二、参考资料


文档版本: v1.0
最后更新: 2026-05-20