mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 15:08:02 +08:00
fix: 过滤入口拒绝日志并强化鉴权边界
This commit is contained in:
+19
-11
@@ -1,12 +1,25 @@
|
||||
{
|
||||
servers {
|
||||
max_header_size 64KB
|
||||
timeouts {
|
||||
read_header 10s
|
||||
idle 2m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 修改为你的域名
|
||||
api.sub2api.com {
|
||||
# This baseline assumes clients connect directly to Caddy. When Caddy is
|
||||
# behind a CDN, configure explicit trusted proxy CIDRs and {client_ip} as
|
||||
# documented in EDGE_SECURITY.md; {remote_host} would otherwise be the CDN.
|
||||
# =========================================================================
|
||||
# TLS 安全配置
|
||||
# =========================================================================
|
||||
tls {
|
||||
# 仅使用 TLS 1.2 和 1.3
|
||||
protocols tls1.2 tls1.3
|
||||
|
||||
|
||||
# 优先使用的加密套件
|
||||
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
}
|
||||
@@ -20,22 +33,17 @@ api.sub2api.com {
|
||||
health_interval 30s
|
||||
health_timeout 10s
|
||||
health_status 200
|
||||
|
||||
|
||||
# 负载均衡策略(单节点可忽略,多节点时有用)
|
||||
lb_policy round_robin
|
||||
lb_try_duration 5s
|
||||
lb_try_interval 250ms
|
||||
|
||||
# 传递真实客户端信息
|
||||
# 兼容 Cloudflare 和直连:后端应优先读取 CF-Connecting-IP,其次 X-Real-IP
|
||||
|
||||
# 仅从实际 TCP 对端生成转发头,避免透传客户端伪造值。
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host {host}
|
||||
# 保留 Cloudflare 原始头(如果存在)
|
||||
# 后端获取 IP 的优先级建议: CF-Connecting-IP → X-Real-IP → X-Forwarded-For
|
||||
header_up CF-Connecting-IP {http.request.header.CF-Connecting-IP}
|
||||
|
||||
# 连接池优化
|
||||
transport http {
|
||||
keepalive 120s
|
||||
@@ -44,7 +52,7 @@ api.sub2api.com {
|
||||
write_buffer 16KB
|
||||
compression off
|
||||
}
|
||||
|
||||
|
||||
# 故障转移
|
||||
fail_duration 30s
|
||||
max_fails 3
|
||||
@@ -72,7 +80,7 @@ api.sub2api.com {
|
||||
# 请求大小限制 (防止大文件攻击)
|
||||
# =========================================================================
|
||||
request_body {
|
||||
max_size 100MB
|
||||
max_size 256MB
|
||||
}
|
||||
|
||||
# =========================================================================
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
# Edge and HTTP Ingress Security
|
||||
|
||||
Sub2API supports long-lived SSE and WebSocket requests. Protect the request
|
||||
ingress without imposing a response `WriteTimeout`: a write deadline would
|
||||
terminate healthy long generations and streams.
|
||||
|
||||
## Application defaults
|
||||
|
||||
- `server.max_header_bytes: 65536` limits HTTP/1 request headers to 64 KiB;
|
||||
Go maps it to the corresponding HTTP/2 header-list limit.
|
||||
- `server.read_header_timeout: 10` bounds slow-header attacks. It does not
|
||||
limit request processing or response streaming.
|
||||
- `server.max_request_body_size: 268435456` is the absolute 256 MiB safety net.
|
||||
- `gateway.max_body_size: 268435456` remains available to multimodal, Gemini,
|
||||
image, video, and batch-image endpoints.
|
||||
- `gateway.text_max_body_size: 33554432` limits the known pure-text
|
||||
`/embeddings` and `/alpha/search` endpoints to 32 MiB.
|
||||
- H2C defaults to 50 concurrent streams per connection, a 2 MiB connection
|
||||
upload window, and a 512 KiB stream upload window.
|
||||
- Invalid credential abuse is limited in process by trusted client IP (IPv6
|
||||
`/64`): 120 failures per 60 seconds followed by a 60-second block. This is a
|
||||
per-instance safety net; multi-instance enforcement still belongs at the
|
||||
load balancer, CDN, or WAF.
|
||||
|
||||
Do not add a single application-wide request semaphore: an SSE request may
|
||||
legitimately occupy it for many minutes. Apply connection and unauthenticated
|
||||
request controls at the edge; authenticated user/API-key concurrency remains
|
||||
the application's responsibility.
|
||||
|
||||
## Trusted client IPs
|
||||
|
||||
`server.trusted_proxies` must contain only the CIDR/IP addresses that connect
|
||||
directly to Sub2API, normally the local Nginx/Caddy address or the private load
|
||||
balancer subnet. An empty list disables forwarded-IP trust.
|
||||
|
||||
Never trust `CF-Connecting-IP`, `X-Real-IP`, or `X-Forwarded-For` merely because
|
||||
the header exists. A CDN deployment must firewall the origin so only the CDN or
|
||||
load balancer can reach it, and the proxy must overwrite forwarded headers.
|
||||
|
||||
Example for a proxy on the same host:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
trusted_proxies:
|
||||
- 127.0.0.1/32
|
||||
- ::1/128
|
||||
```
|
||||
|
||||
## Nginx baseline
|
||||
|
||||
Define shared zones in the `http` block. Tune rates to measured legitimate
|
||||
traffic; the values below are conservative starting points, not universal
|
||||
capacity targets.
|
||||
|
||||
```nginx
|
||||
limit_conn_zone $binary_remote_addr zone=sub2api_conn:20m;
|
||||
limit_req_zone $binary_remote_addr zone=sub2api_auth:20m rate=5r/s;
|
||||
limit_req_zone $binary_remote_addr zone=sub2api_api:40m rate=30r/s;
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name api.example.com;
|
||||
|
||||
client_header_timeout 10s;
|
||||
client_max_body_size 256m;
|
||||
large_client_header_buffers 4 16k;
|
||||
limit_conn sub2api_conn 40;
|
||||
|
||||
location ~ ^/(auth|api/auth)/ {
|
||||
limit_req zone=sub2api_auth burst=10 nodelay;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
location ~ ^/(v1/)?(embeddings|alpha/search)$ {
|
||||
client_max_body_size 32m;
|
||||
limit_req zone=sub2api_api burst=60 nodelay;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
location / {
|
||||
limit_req zone=sub2api_api burst=60 nodelay;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_read_timeout 1800s;
|
||||
proxy_send_timeout 1800s;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Do not use an incoming `$http_x_forwarded_for` value unless Nginx real-IP
|
||||
processing is restricted to explicit trusted proxy CIDRs.
|
||||
|
||||
## Caddy and CDN
|
||||
|
||||
The bundled `deploy/Caddyfile` sets a 64 KiB header limit, a 10-second header
|
||||
timeout, a 256 MiB absolute body limit, and overwrites forwarded addresses from
|
||||
the TCP peer. It is therefore a direct-to-Caddy baseline. Do not use its
|
||||
`{remote_host}` forwarding lines unchanged behind a CDN: all clients would be
|
||||
attributed to a CDN egress address, collapsing rejection aggregation and the
|
||||
invalid-auth limiter onto unrelated users.
|
||||
|
||||
For a CDN deployment, first firewall the origin so only current CDN egress
|
||||
CIDRs can connect. Then configure those exact ranges as Caddy trusted proxies
|
||||
and derive upstream headers from Caddy's parsed `{client_ip}`. For example:
|
||||
|
||||
```caddyfile
|
||||
{
|
||||
servers {
|
||||
trusted_proxies static 192.0.2.0/24 2001:db8:1234::/48
|
||||
trusted_proxies_strict
|
||||
client_ip_headers CF-Connecting-IP X-Forwarded-For
|
||||
}
|
||||
}
|
||||
|
||||
api.example.com {
|
||||
reverse_proxy 127.0.0.1:8080 {
|
||||
header_up X-Real-IP {client_ip}
|
||||
header_up X-Forwarded-For {client_ip}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace the documentation ranges with the CDN's published, automatically
|
||||
maintained egress ranges. `CF-Connecting-IP` is safe here only because direct
|
||||
origin access is blocked and Caddy trusts only those TCP peers. Configure
|
||||
Sub2API `server.trusted_proxies` with the Caddy address/private subnet so the
|
||||
application accepts only Caddy's rewritten headers.
|
||||
|
||||
Caddy core does not provide a general request-rate limiter; use a trusted
|
||||
CDN/WAF, a supported rate-limit module, or host firewall controls.
|
||||
|
||||
At a CDN/WAF, configure connection limits, header/body limits, bot challenges,
|
||||
and per-IP/ASN rates before traffic reaches the origin. Allow origin ingress
|
||||
only from CDN egress CIDRs or a private load balancer. Keep the application port
|
||||
off the public Internet.
|
||||
|
||||
## DDoS boundary
|
||||
|
||||
Application checks reduce amplification after a connection reaches Go. They
|
||||
cannot absorb volumetric attacks, TLS floods, bandwidth saturation, or a large
|
||||
distributed source set. Those require upstream network capacity, CDN/WAF
|
||||
filtering, provider firewall rules, and origin isolation. Avoid high-cardinality
|
||||
metrics or per-request database security logs during rejection storms.
|
||||
@@ -27,6 +27,7 @@ This directory contains files for deploying Sub2API on Linux servers and Apple-s
|
||||
| `sub2api-datamanagementd.service` | datamanagementd systemd service unit file |
|
||||
| `DATAMANAGEMENTD_CN.md` | datamanagementd 部署与联动说明(中文) |
|
||||
| `config.example.yaml` | Example configuration file |
|
||||
| `EDGE_SECURITY.md` | Reverse proxy, CDN/WAF, trusted proxy, and ingress hardening guide |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -27,6 +27,15 @@ server:
|
||||
# 用于生成邮件中的外部链接(例如:重置密码链接)的前端基础地址
|
||||
# Example: "https://example.com"
|
||||
frontend_url: ""
|
||||
# Maximum time to receive complete request headers. Does not limit response streams.
|
||||
# 完整读取请求头的最大时间;不限制响应流持续时间。
|
||||
read_header_timeout: 10
|
||||
# Request header limit in bytes (64 KiB); also bounds the HTTP/2 header list.
|
||||
# 请求头上限(字节,默认 64 KiB);同时约束 HTTP/2 header list。
|
||||
max_header_bytes: 65536
|
||||
# Keep-alive idle timeout in seconds.
|
||||
# Keep-Alive 空闲连接超时(秒)。
|
||||
idle_timeout: 120
|
||||
# Trusted proxies for X-Forwarded-For parsing (CIDR/IP). Empty disables trusted proxies.
|
||||
# 信任的代理地址(CIDR/IP 格式),用于解析 X-Forwarded-For 头。留空则禁用代理信任。
|
||||
trusted_proxies: []
|
||||
@@ -167,6 +176,9 @@ gateway:
|
||||
# Max request body size in bytes (default: 256MB)
|
||||
# 请求体最大字节数(默认 256MB)
|
||||
max_body_size: 268435456
|
||||
# Pure-text endpoint body limit (embeddings and alpha/search), default 32 MiB.
|
||||
# 纯文本端点请求体上限(embeddings、alpha/search),默认 32 MiB。
|
||||
text_max_body_size: 33554432
|
||||
# Max bytes to read for non-stream upstream responses (default: 8MB)
|
||||
# 非流式上游响应体读取上限(默认 8MB)
|
||||
upstream_response_read_max_bytes: 8388608
|
||||
@@ -708,6 +720,23 @@ api_key_auth_cache:
|
||||
# Enable singleflight for cache misses
|
||||
# 缓存未命中时启用 singleflight 合并回源
|
||||
singleflight: true
|
||||
# Maximum concurrent database lookups for authentication cache misses
|
||||
# 认证缓存未命中时允许并发回源数据库的最大数量
|
||||
lookup_concurrency: 64
|
||||
# Process-local invalid-auth abuse protection. Counts only missing, malformed,
|
||||
# deprecated-query, and confirmed invalid credentials; valid requests and
|
||||
# Redis/DB failures do not consume the budget.
|
||||
# 本机无效鉴权防护:仅统计缺失、格式错误、废弃 query 及确认无效的凭据。
|
||||
invalid_abuse:
|
||||
enabled: true
|
||||
# Invalid attempts per trusted client IP (IPv6 grouped by /64) per window.
|
||||
# 每个可信客户端 IP(IPv6 按 /64 聚合)在窗口内允许的无效次数。
|
||||
threshold: 120
|
||||
window_seconds: 60
|
||||
block_seconds: 60
|
||||
# Maximum tracked client identities per process; memory remains bounded.
|
||||
# 每进程最多跟踪的客户端身份数量,确保内存有界。
|
||||
capacity: 16384
|
||||
|
||||
# =============================================================================
|
||||
# Dashboard Cache Configuration
|
||||
|
||||
Reference in New Issue
Block a user