fix: clean shit

在原始配置基础上重新应用限流功能,恢复丢失的路由和服务依赖。
This commit is contained in:
zetaloop
2026-04-06 11:15:43 +08:00
parent 35ce485e07
commit 063430b644
3 changed files with 112 additions and 647 deletions
+35 -17
View File
@@ -38,8 +38,6 @@ services:
rl-redis:
image: redis:${REDIS_VERSION:-8}
container_name: ${REDIS_CONTAINER_NAME:-rl-redis-dev-server}
profiles:
- infra
ports:
- "6380:6379"
restart: unless-stopped
@@ -86,42 +84,62 @@ services:
condition: service_started
envoy-gateway:
build:
context: ../deploy/dev/envoy
image: envoy-gateway:latest
container_name: ${ENVOY_GATEWAY_CONTAINER_NAME:-envoy-gateway-dev-server}
image: envoyproxy/envoy:v1.31-latest
container_name: juwan-envoy-gateway
restart: unless-stopped
command:
- /usr/local/bin/envoy
- -c
- /etc/envoy/envoy.yaml
- --log-level
- info
ports:
- "8080:8080"
- "9901:9901"
- "18080:8080"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
depends_on:
authz-adapter:
condition: service_started
required: false
user-api:
users-api:
condition: service_started
player-api:
condition: service_started
game-api:
condition: service_started
shop-api:
condition: service_started
order-api:
condition: service_started
wallet-api:
condition: service_started
community-api:
condition: service_started
objectstory-api:
condition: service_started
required: false
email-api:
condition: service_started
required: false
restart: unless-stopped
ratelimit:
image: ratelimit:latest
image: envoyproxy/ratelimit:05c08d03
container_name: rl-service
restart: unless-stopped
command: /bin/ratelimit
environment:
- REDIS_SOCKET_TYPE=tcp
- REDIS_URL=rl-redis:6379
- USE_STATSD=false
- RUNTIME_ROOT=/data
- RUNTIME_SUBDIRECTORY=ratelimit
- RUNTIME_WATCH_ROOT=true # 热重载
- RUNTIME_WATCH_ROOT=true
- LOG_LEVEL=debug
volumes:
- ./rls/ratelimit.yaml:/data/ratelimit/config/ratelimit.yaml:ro
ports:
- "8081:8081"
- "6070:6070"
- "18081:8081"
- "16070:6070"
depends_on:
rl-redis:
condition: service_started
# ==================== RPC 层 ====================
user-rpc:
+77 -4
View File
@@ -14,6 +14,7 @@ static_resources:
codec_type: AUTO
generate_request_id: true
use_remote_address: true
xff_num_trusted_hops: 1
route_config:
name: local_route
virtual_hosts:
@@ -36,6 +37,11 @@ static_resources:
route:
cluster: user_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: login
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
@@ -46,6 +52,11 @@ static_resources:
route:
cluster: user_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: register
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
@@ -76,6 +87,11 @@ static_resources:
route:
cluster: email_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: forgot_password_send
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
@@ -86,6 +102,11 @@ static_resources:
route:
cluster: email_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: verify_code_send
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
@@ -307,14 +328,37 @@ static_resources:
body:
inline_string: gateway route not found
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
start_time: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
protocol: "%PROTOCOL%"
authority: "%REQ(:AUTHORITY)%"
user_agent: "%REQ(USER-AGENT)%"
request_id: "%REQ(X-REQUEST-ID)%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
bytes_received: "%BYTES_RECEIVED%"
bytes_sent: "%BYTES_SENT%"
duration_ms: "%DURATION%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_host: "%UPSTREAM_HOST%"
upstream_service_time_ms: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
route_name: "%ROUTE_NAME%"
http_filters:
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
local TOKEN_HEADER = "xsrf-token"
local TOKEN_COOKIE = "XSRF-TOKEN"
local GUARD_COOKIE = "XSRF-GUARD"
local TOKEN_COOKIE = "__Host-XSRF-TOKEN"
local GUARD_COOKIE = "__Host-XSRF-GUARD"
local seeded = false
@@ -420,14 +464,14 @@ static_resources:
if metadata["need_set_token_cookie"] == true and token_value ~= nil and token_value ~= "" then
response_handle:headers():add(
"set-cookie",
TOKEN_COOKIE .. "=" .. token_value .. "; Path=/; Max-Age=7200; SameSite=Strict"
TOKEN_COOKIE .. "=" .. token_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure"
)
end
if metadata["need_set_guard_cookie"] == true and guard_value ~= nil and guard_value ~= "" then
response_handle:headers():add(
"set-cookie",
GUARD_COOKIE .. "=" .. guard_value .. "; Path=/; Max-Age=7200; SameSite=Strict; HttpOnly"
GUARD_COOKIE .. "=" .. guard_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure; HttpOnly"
)
end
end
@@ -539,6 +583,20 @@ static_resources:
cluster_name: authz_adapter_cluster
timeout: 0.5s
- name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: api
failure_mode_deny: false
rate_limited_as_resource_exhausted: true
enable_x_ratelimit_headers: DRAFT_VERSION_03
rate_limit_service:
transport_api_version: V3
grpc_service:
envoy_grpc:
cluster_name: ratelimit_cluster
timeout: 0.2s
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
@@ -685,6 +743,21 @@ static_resources:
address: authz-adapter
port_value: 9002
- name: ratelimit_cluster
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: ratelimit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ratelimit
port_value: 8081
admin:
access_log_path: /tmp/admin.log
address:
-626
View File
@@ -1,626 +0,0 @@
static_resources:
listeners:
- name: ingress_http
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
generate_request_id: true
use_remote_address: true
xff_num_trusted_hops: 1
route_config:
name: local_route
virtual_hosts:
- name: juwan_services
domains: [ "*" ]
routes:
- match:
path: /healthz
direct_response:
status: 200
body:
inline_string: ok
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/auth/login
route:
cluster: user_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: login
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/auth/register
route:
cluster: user_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: register
- generic_key:
descriptor_key: "period"
descriptor_value: "minute"
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/auth/forgot-password
route:
cluster: user_api_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/auth/reset-password
route:
cluster: user_api_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/auth/forgot-password/send
route:
cluster: email_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: forgot_password_send
- generic_key:
descriptor_key: "period"
descriptor_value: "minute"
- remote_address: {}
- actions:
- generic_key:
descriptor_value: forgot_password_send
- generic_key:
descriptor_key: "period"
descriptor_value: "hour"
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
prefix: /api/users
route:
cluster: user_api_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
prefix: /api/v1/shop
route:
cluster: shop_api_cluster
timeout: 30s
- match:
prefix: /api/v1/player
route:
cluster: player_api_cluster
timeout: 30s
- match:
prefix: /api/v1/games
route:
cluster: game_api_cluster
timeout: 30s
- match:
prefix: /api/v1/games
headers:
- name: :method
exact_match: GET
route:
cluster: game_api_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/email/verification-code/send
route:
cluster: email_api_cluster
timeout: 30s
rate_limits:
- actions:
- generic_key:
descriptor_value: verify_code_send
- generic_key:
descriptor_key: "period"
descriptor_value: "minute"
- remote_address: {}
- actions:
- generic_key:
descriptor_value: verify_code_send
- generic_key:
descriptor_key: "period"
descriptor_value: "hour"
- remote_address: {}
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
prefix: /api/v1/wallet
route:
cluster: wallet_api_cluster
timeout: 30s
- match:
prefix: /api/v1/players
route:
cluster: player_api_cluster
timeout: 30s
- match:
prefix: /api/v1/orders
route:
cluster: order_api_cluster
timeout: 30s
- match:
prefix: /api/v1/email
route:
cluster: email_api_cluster
timeout: 30s
- match:
prefix: /api/v1/auth
route:
cluster: user_api_cluster
timeout: 30s
- match:
prefix: /api/v1/upload
route:
cluster: objectstory_api_cluster
timeout: 30s
- match:
prefix: /api/v1/files
route:
cluster: objectstory_api_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
prefix: /api/email
route:
cluster: email_api_cluster
timeout: 30s
- match:
prefix: /api/v1/game
route:
cluster: game_api_cluster
timeout: 30s
- match:
prefix: /api/v1
route:
cluster: user_api_cluster
timeout: 30s
- match:
prefix: /
direct_response:
status: 404
body:
inline_string: gateway route not found
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
start_time: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
protocol: "%PROTOCOL%"
authority: "%REQ(:AUTHORITY)%"
user_agent: "%REQ(USER-AGENT)%"
request_id: "%REQ(X-REQUEST-ID)%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
bytes_received: "%BYTES_RECEIVED%"
bytes_sent: "%BYTES_SENT%"
duration_ms: "%DURATION%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_host: "%UPSTREAM_HOST%"
upstream_service_time_ms: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
route_name: "%ROUTE_NAME%"
http_filters:
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
local TOKEN_HEADER = "xsrf-token"
local TOKEN_COOKIE = "__Host-XSRF-TOKEN"
local GUARD_COOKIE = "__Host-XSRF-GUARD"
local seeded = false
local function seed_random()
if seeded then
return
end
seeded = true
math.randomseed(os.time())
end
local function split_cookie(header)
local out = {}
if not header then
return out
end
for pair in string.gmatch(header, "([^;]+)") do
local key, value = string.match(pair, "^%s*([^=]+)=?(.*)$")
if key ~= nil and value ~= nil then
out[string.lower(key)] = value
end
end
return out
end
local function is_safe_method(method)
return method == "GET" or method == "HEAD" or method == "OPTIONS"
end
local function build_token(request_id)
seed_random()
local rnd = tostring(math.random(100000, 999999))
local rid = request_id or "rid"
return tostring(os.time()) .. "-" .. rid .. "-" .. rnd
end
function envoy_on_request(request_handle)
local headers = request_handle:headers()
local method = headers:get(":method")
local cookie_header = headers:get("cookie")
local cookies = split_cookie(cookie_header)
local token_cookie = cookies[string.lower(TOKEN_COOKIE)]
local guard_cookie = cookies[string.lower(GUARD_COOKIE)]
request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_token_cookie", token_cookie == nil or token_cookie == "")
request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_guard_cookie", guard_cookie == nil or guard_cookie == "")
if token_cookie == nil or token_cookie == "" then
token_cookie = build_token(headers:get("x-request-id"))
request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", token_cookie)
else
request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", token_cookie)
end
if guard_cookie == nil or guard_cookie == "" then
guard_cookie = build_token(headers:get("x-request-id"))
request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", guard_cookie)
else
request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", guard_cookie)
end
if is_safe_method(method) then
return
end
local token_header = headers:get(TOKEN_HEADER)
if token_header == nil or token_header == "" then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"missing XSRF-TOKEN header"}'
)
return
end
if token_cookie == nil or token_cookie == "" or guard_cookie == nil or guard_cookie == "" then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"missing csrf cookies"}'
)
return
end
if token_header ~= token_cookie then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"xsrf token mismatch"}'
)
return
end
end
function envoy_on_response(response_handle)
local metadata = response_handle:streamInfo():dynamicMetadata():get("csrf")
if metadata == nil then
return
end
local token_value = metadata["token_value"]
local guard_value = metadata["guard_value"]
if metadata["need_set_token_cookie"] == true and token_value ~= nil and token_value ~= "" then
response_handle:headers():add(
"set-cookie",
TOKEN_COOKIE .. "=" .. token_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure"
)
end
if metadata["need_set_guard_cookie"] == true and guard_value ~= nil and guard_value ~= "" then
response_handle:headers():add(
"set-cookie",
GUARD_COOKIE .. "=" .. guard_value .. "; Path=/; Max-Age=7200; SameSite=Strict; Secure; HttpOnly"
)
end
end
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
juwan_user_jwt:
issuer: juwan-user-rpc
from_cookies:
- JToken
local_jwks:
inline_string: '{"keys":[{"kty":"oct","k":"TUdVeU1XRTNaRGhqTVRRNVpEZzFNV1ZpT1dVME1HTTNPVEUyTldWa1lUQmxPVEU1WldSa1pEVTFZall6T0dKak9XUmlOek0wTlRjNE5ESXlNamxrWlE","alg":"HS256","use":"sig","kid":"juwan-hs256-1"}]}'
forward: false
claim_to_headers:
- header_name: x-auth-user-id
claim_name: UserId
- header_name: x-auth-is-admin
claim_name: IsAdmin
rules:
- match:
path: /healthz
- match:
prefix: /api/v1
headers:
- name: :method
exact_match: OPTIONS
- match:
path: /api/v1/auth/login
- match:
path: /api/v1/auth/register
- match:
path: /api/v1/auth/forgot-password
- match:
path: /api/v1/auth/reset-password
- match:
path: /api/v1/auth/forgot-password/send
- match:
path: /api/v1/email/verification-code/send
- match:
prefix: /api/v1
requires:
provider_name: juwan_user_jwt
- match:
prefix: /api/users
requires:
provider_name: juwan_user_jwt
- match:
prefix: /api/email
requires:
provider_name: juwan_user_jwt
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: V3
failure_mode_allow: false
with_request_body:
max_request_bytes: 8192
allow_partial_message: true
grpc_service:
envoy_grpc:
cluster_name: authz_adapter_cluster
timeout: 0.5s
# RLS 全局过滤器
- name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: api
failure_mode_deny: false
rate_limited_as_resource_exhausted: true
enable_x_ratelimit_headers: DRAFT_VERSION_03
rate_limit_service:
transport_api_version: V3
grpc_service:
envoy_grpc:
cluster_name: ratelimit_cluster
timeout: 0.2s
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: user_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: user_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: user-api
port_value: 8888
- name: email_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: email_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: email-api
port_value: 8888
- name: shop_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: shop_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: shop-api
port_value: 8888
- name: game_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: game_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: game-api
port_value: 8888
- name: objectstory_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: objectstory_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: objectstory-api
port_value: 8888
- name: wallet_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: wallet_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: wallet-api
port_value: 8888
- name: order_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: order_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: order-api
port_value: 8888
- name: player_api_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: player_api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: player-api
port_value: 8888
- name: authz_adapter_cluster
connect_timeout: 0.5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: { }
load_assignment:
cluster_name: authz_adapter_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: authz-adapter
port_value: 9002
# RLS 集群
- name: ratelimit_cluster
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: ratelimit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ratelimit # RLS 地址
port_value: 8081 # RLS gRPC 端口
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901