Gateway 反向代理配置¶
在生产环境中,通常需要将 Gateway 部署在反向代理(如 nginx 或 Caddy)之后,以实现 HTTPS 终结、域名路由、负载均衡等功能。
为什么需要反向代理¶
- HTTPS/TLS 终结:在代理层处理 SSL 证书,Gateway 本身仅需处理 HTTP
- 域名路由:通过同一个 443 端口对外提供多个服务
- 负载均衡:多实例部署时分发请求
- 安全加固:隐藏内部端口,统一入口管控
- 静态资源:代理层直接服务仪表盘前端文件
Nginx 配置¶
基础 HTTP + WebSocket 代理¶
upstream echo_gateway {
server 127.0.0.1:8090;
}
server {
listen 443 ssl http2;
server_name gateway.example.com;
ssl_certificate /etc/ssl/certs/gateway.example.com.pem;
ssl_certificate_key /etc/ssl/private/gateway.example.com.key;
# HTTP API 代理
location / {
proxy_pass http://echo_gateway;
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;
}
# WebSocket 代理 — 会话
location /ws {
proxy_pass http://echo_gateway;
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;
# WebSocket 超时设置
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# WebSocket 代理 — 仪表盘
location /ws/dashboard {
proxy_pass http://echo_gateway;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# 健康检查(可选:限制仅内网访问)
location /api/v1/health {
proxy_pass http://echo_gateway;
# allow 10.0.0.0/8;
# deny all;
}
}
WebSocket 超时
默认 nginx 的 proxy_read_timeout 为 60 秒,WebSocket 长连接会因此被断开。建议设置为 3600 秒或更长。Gateway 自身会通过 ping/pong 帧维持连接活性。
HTTP 重定向到 HTTPS¶
Caddy 配置¶
Caddy 自动管理 HTTPS 证书,配置相对简洁:
gateway.example.com {
reverse_proxy 127.0.0.1:8090
# Caddy 自动处理 WebSocket upgrade,无需额外配置
# 但可以显式设置超时
reverse_proxy /ws* 127.0.0.1:8090 {
transport http {
keepalive 3600s
}
}
}
Caddy 的优势
Caddy 自动从 Let's Encrypt 获取并续期 TLS 证书,自动处理 WebSocket 协议升级,无需额外配置 Upgrade/Connection 头。对于简单部署场景,Caddy 是更省心的选择。
必须转发的请求头¶
无论使用哪种反向代理,以下请求头必须正确转发:
| 请求头 | 用途 | 示例值 |
|---|---|---|
Host |
原始主机名,用于 allowed_hosts 校验 | gateway.example.com |
X-Forwarded-For |
客户端真实 IP,用于速率限制和审计 | 203.0.113.50 |
X-Forwarded-Proto |
原始协议,用于生成正确的重定向 URL | https |
Upgrade |
WebSocket 协议升级 | websocket |
Connection |
配合 Upgrade 使用 | upgrade |
X-Forwarded-For 信任链
Gateway 需要正确识别客户端真实 IP 以执行速率限制和审计日志。确保代理链中每一层都正确追加 X-Forwarded-For,且 Gateway 配置信任的代理 IP 范围。
WebSocket 注意事项¶
超时与保活¶
- 代理层
read_timeout应大于 Gateway 的 ping 间隔(默认 30 秒) - 建议设置为 3600 秒以支持长时间空闲连接
- Gateway 会定期发送 WebSocket ping 帧维持连接
缓冲设置¶
连接数限制¶
# 限制单 IP WebSocket 并发连接数
limit_conn_zone $binary_remote_addr zone=ws_conn:10m;
location /ws/ {
limit_conn ws_conn 10;
# ... 其他配置
}
Gateway 端配置¶
allowed_hosts¶
当 Gateway 位于反向代理之后时,需要配置 allowed_hosts 以匹配代理转发过来的 Host 头:
环回地址检测失效
当 Gateway 位于反向代理之后时,所有请求的来源 IP 都会变为 127.0.0.1(代理的地址)。这意味着环回地址豁免对所有请求都会生效。务必:
- 配置
allowed_hosts严格限制允许的 Host 头 - 对敏感接口使用令牌认证(token auth)而非依赖环回豁免
- 确保代理层本身有适当的访问控制
allowed_origins¶
使用自定义域名时,更新 CORS 配置以匹配实际访问域名:
健康检查端点¶
/api/v1/health 端点可用于负载均衡器的健康探测;若修改了
gateway.api_prefix,这里也要使用相同前缀:
# nginx upstream 健康检查(需 nginx plus 或第三方模块)
upstream echo_gateway {
server 127.0.0.1:8090;
# health_check uri=/api/v1/health interval=10s;
}
对于 Kubernetes 或云负载均衡器:
# Kubernetes Ingress 健康检查注解示例
annotations:
nginx.ingress.kubernetes.io/health-check-path: /api/v1/health
nginx.ingress.kubernetes.io/health-check-interval: "10"
健康检查频率
建议健康检查间隔设置为 10-30 秒。过于频繁的健康检查会增加日志噪音,但不会对 Gateway 性能产生实质影响。
完整部署示例¶
以下是一个典型的生产部署配置组合:
Gateway 配置(config.yaml):
gateway:
enabled: true
host: "127.0.0.1" # 仅监听本地,由代理转发
port: 8090
auth:
mode: "allowlist"
allowed_users:
- "telegram:123456"
- "wechat:wx_admin"
api_tokens:
- "tk-external-service-001"
admin_tokens:
- "atk-ops-team-master"
allowed_hosts:
- "gateway.example.com"
allowed_origins:
- "https://dashboard.example.com"
Nginx 配置要点:
server {
listen 443 ssl http2;
server_name gateway.example.com;
# TLS 配置省略...
location / {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws {
proxy_pass http://127.0.0.1:8090;
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-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
}
关于 X-Forwarded-For¶
网关没有 trusted_proxies 配置项,也不从 X-Forwarded-For、X-Real-IP 等转发头推导客户端 IP。所有涉及信任的判定都取真实 TCP 对端地址(socket 的 peername),而非 request.remote 或任何请求头。
这是有意的:转发头由客户端可控,一旦用它判定「来自本机」,远程调用方只要伪造一个头就能拿到本机豁免。代价是反向代理后端看到的对端地址是代理自己的地址。
因此在反代场景下需要注意两点:
- 回环豁免会命中代理,而非最终用户。 代理与网关同机时,网关看到的对端是回环地址,于是所有经代理进来的请求都获得本机信任。这种部署必须在代理层完成认证,不能依赖网关的回环判定区分用户。
- 审计日志中的地址是代理地址。 需要记录最终用户 IP 时,在反向代理侧记录,网关日志无法提供。
同时要配置 gateway.auth.allowed_hosts,把代理对外的域名列进去——绑定到非回环地址时,该列表没有可用条目会让所有管理端点(会话、配置、记忆写入、任务、定时、知识库)拒绝浏览器请求。注意通配地址(0.0.0.0、::)不是有效条目,填进去等同于没配。