跳转至

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

server {
    listen 80;
    server_name gateway.example.com;
    return 301 https://$host$request_uri;
}

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 帧维持连接

缓冲设置

# 禁用代理缓冲以降低流式输出延迟
proxy_buffering off;

连接数限制

# 限制单 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:
  auth:
    allowed_hosts:
      - "gateway.example.com"
      - "gateway.internal.example.com"  # 内网域名(如有)

环回地址检测失效

当 Gateway 位于反向代理之后时,所有请求的来源 IP 都会变为 127.0.0.1(代理的地址)。这意味着环回地址豁免对所有请求都会生效。务必:

  1. 配置 allowed_hosts 严格限制允许的 Host 头
  2. 对敏感接口使用令牌认证(token auth)而非依赖环回豁免
  3. 确保代理层本身有适当的访问控制

allowed_origins

使用自定义域名时,更新 CORS 配置以匹配实际访问域名:

gateway:
  auth:
    allowed_origins:
      - "https://gateway.example.com"
      - "https://dashboard.example.com"

健康检查端点

/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-ForX-Real-IP 等转发头推导客户端 IP。所有涉及信任的判定都取真实 TCP 对端地址(socket 的 peername),而非 request.remote 或任何请求头。

这是有意的:转发头由客户端可控,一旦用它判定「来自本机」,远程调用方只要伪造一个头就能拿到本机豁免。代价是反向代理后端看到的对端地址是代理自己的地址。

因此在反代场景下需要注意两点:

  • 回环豁免会命中代理,而非最终用户。 代理与网关同机时,网关看到的对端是回环地址,于是所有经代理进来的请求都获得本机信任。这种部署必须在代理层完成认证,不能依赖网关的回环判定区分用户。
  • 审计日志中的地址是代理地址。 需要记录最终用户 IP 时,在反向代理侧记录,网关日志无法提供。

同时要配置 gateway.auth.allowed_hosts,把代理对外的域名列进去——绑定到非回环地址时,该列表没有可用条目会让所有管理端点(会话、配置、记忆写入、任务、定时、知识库)拒绝浏览器请求。注意通配地址(0.0.0.0::)不是有效条目,填进去等同于没配。

相关文档