add:
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
# Envoy Gateway 配置指南
|
||||
|
||||
## 概述
|
||||
|
||||
Envoy Gateway 作为 API 统一入口,提供以下功能:
|
||||
- **JWT 身份验证**:所有 API 请求(除登录/注册)都需要有效的 JWT token
|
||||
- **CSRF 防护**:防止跨站点请求伪造攻击
|
||||
- **速率限制**:防止 DDoS 攻击
|
||||
- **TLS 加密**:所有通信都加密
|
||||
- **负载均衡**:分担后端服务的流量
|
||||
|
||||
## 架构
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Client │
|
||||
└──────┬──────┘
|
||||
│ HTTP/HTTPS (Port 80/443)
|
||||
│
|
||||
┌──────▼────────────────┐
|
||||
│ Envoy Gateway │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ JWT Validator │ │ ◄─── JWT Verification (offline)
|
||||
│ │ CSRF Filter │ │
|
||||
│ │ Rate Limiter │ │
|
||||
│ │ Router │ │
|
||||
│ └────────────────┘ │
|
||||
└────────┬─────────────┘
|
||||
│ gRPC/HTTP
|
||||
┌────┴────┬──────────┐
|
||||
│ │ │
|
||||
┌───▼──┐ ┌──▼────┐ ┌──▼─────┐
|
||||
│User │ │Order │ │User │
|
||||
│API │ │API │ │RPC │
|
||||
└──────┘ └───────┘ └────────┘
|
||||
```
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 生成 TLS 证书
|
||||
|
||||
```bash
|
||||
# 为 Envoy 生成自签名证书(生产环境应使用正式证书)
|
||||
kubectl create secret tls envoy-tls \
|
||||
--cert=path/to/tls.crt \
|
||||
--key=path/to/tls.key \
|
||||
-n juwan
|
||||
|
||||
# 或生成自签名证书(仅用于测试)
|
||||
openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt \
|
||||
-days 365 -nodes -subj "/CN=api.juwan.local"
|
||||
|
||||
kubectl create secret tls envoy-tls \
|
||||
--cert=tls.crt \
|
||||
--key=tls.key \
|
||||
-n juwan
|
||||
```
|
||||
|
||||
### 2. 部署 Envoy Gateway
|
||||
|
||||
```bash
|
||||
# 应用 Envoy 配置
|
||||
kubectl apply -f deploy/k8s/envoy-gateway.yaml
|
||||
|
||||
# 查看部署状态
|
||||
kubectl get pods -n juwan -l app=envoy-gateway
|
||||
kubectl get svc -n juwan envoy-gateway
|
||||
```
|
||||
|
||||
### 3. 配置 JWKS 端点
|
||||
|
||||
在 user-rpc 中暴露 JWKS 端点,供 Envoy 验证 JWT:
|
||||
|
||||
#### 在 `app/users/rpc` 中添加 HTTP 路由(go-zero)
|
||||
|
||||
编辑 `app/users/rpc/internal/handler/` 或在 `main.go` 中:
|
||||
|
||||
```go
|
||||
// 在 rpc server 启动时,添加 HTTP 端点用于暴露 JWKS
|
||||
import (
|
||||
"net/http"
|
||||
"juwan-backend/app/users/rpc/internal/utils"
|
||||
)
|
||||
|
||||
// 在 main 函数中
|
||||
http.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
secretKey := os.Getenv("JWT_SECRET_KEY")
|
||||
if secretKey == "" {
|
||||
secretKey = "your-default-secret-key"
|
||||
}
|
||||
|
||||
jwksJSON, err := utils.GenerateJWKSEndpoint(secretKey, "default-key-id")
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate JWKS", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(jwksJSON))
|
||||
})
|
||||
|
||||
// 在单独的 goroutine 中启动 HTTP 服务器
|
||||
go func() {
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}()
|
||||
```
|
||||
|
||||
**或使用 Echo 框架(更推荐)**:
|
||||
|
||||
```go
|
||||
// 在 main.go 中
|
||||
import "github.com/labstack/echo/v4"
|
||||
|
||||
e := echo.New()
|
||||
e.GET("/.well-known/jwks.json", func(c echo.Context) error {
|
||||
secretKey := os.Getenv("JWT_SECRET_KEY")
|
||||
jwksJSON, _ := utils.GenerateJWKSEndpoint(secretKey, "default-key-id")
|
||||
return c.JSONBlob(http.StatusOK, []byte(jwksJSON))
|
||||
})
|
||||
|
||||
go func() {
|
||||
e.Start(":8080")
|
||||
}()
|
||||
```
|
||||
|
||||
### 4. 更新环境变量
|
||||
|
||||
在 K8s Secret 中配置 JWT_SECRET_KEY:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: jwt-secret
|
||||
namespace: juwan
|
||||
type: Opaque
|
||||
data:
|
||||
JWT_SECRET_KEY: "$(echo -n 'your-secret-key-change-this' | base64)"
|
||||
```
|
||||
|
||||
### 5. 验证 JWKS 端点
|
||||
|
||||
```bash
|
||||
# 端口转发
|
||||
kubectl port-forward -n juwan svc/user-rpc-svc 9001:9001
|
||||
|
||||
# 验证 JWKS 端点可访问
|
||||
curl http://localhost:9001/.well-known/jwks.json
|
||||
```
|
||||
|
||||
## JWT 验证流程
|
||||
|
||||
### 1. 登录获取 Token
|
||||
|
||||
```bash
|
||||
curl -X POST http://api.juwan.local/api/v1/users/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "testuser",
|
||||
"password": "password123"
|
||||
}'
|
||||
|
||||
# 响应:
|
||||
# {
|
||||
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
# "expires": 1708780800
|
||||
# }
|
||||
```
|
||||
|
||||
### 2. 使用 Token 访问受保护资源
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
https://api.juwan.local/api/v1/users/123
|
||||
|
||||
# Envoy 验证步骤:
|
||||
# 1. 从 Authorization header 提取 token
|
||||
# 2. 从 JWKS 端点获取公钥(缓存 5 分钟)
|
||||
# 3. 验证 token 签名
|
||||
# 4. 检查 token 过期时间
|
||||
# 5. 将验证后的用户信息添加到请求头(X-USER-ID)
|
||||
# 6. 转发请求到 user-api
|
||||
```
|
||||
|
||||
## CSRF 防护
|
||||
|
||||
### 配置说明
|
||||
|
||||
Envoy 的 CSRF 过滤器检查:
|
||||
- 只对 POST/PUT/DELETE/PATCH 请求进行检查
|
||||
- 检查 `Origin` 和 `Referer` header
|
||||
- 验证请求来自已知域名
|
||||
|
||||
### 跨域请求配置
|
||||
|
||||
```yaml
|
||||
# Envoy 配置中允许的来源
|
||||
additional_origins:
|
||||
- exact: "https://admin.juwan.local"
|
||||
- exact: "https://web.juwan.local"
|
||||
```
|
||||
|
||||
## Token 黑名单检查(可选)
|
||||
|
||||
如果需要验证 token 未被撤销,可启用额外的 RPC 验证:
|
||||
|
||||
```yaml
|
||||
# envoy-gateway.yaml 中取消注释
|
||||
- name: envoy.filters.http.ext_authz
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
|
||||
grpc_service:
|
||||
envoy_grpc:
|
||||
cluster_name: user_rpc_cluster
|
||||
failure_mode_allow: false
|
||||
```
|
||||
|
||||
然后在 user-rpc 中实现 ValidateToken RPC:
|
||||
|
||||
```protobuf
|
||||
rpc ValidateToken(ValidateTokenReq) returns(ValidateTokenResp);
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 1. JWT 验证失败
|
||||
|
||||
```bash
|
||||
# 查看 Envoy 日志
|
||||
kubectl logs -n juwan -l app=envoy-gateway -f
|
||||
|
||||
# 验证 JWKS 端点是否可访问
|
||||
kubectl exec -it -n juwan <envoy-pod> -- \
|
||||
curl http://user-rpc-svc:9001/.well-known/jwks.json
|
||||
```
|
||||
|
||||
### 2. 无法连接到后端服务
|
||||
|
||||
```bash
|
||||
# 验证服务发现
|
||||
kubectl get endpoints -n juwan
|
||||
|
||||
# 验证网络策略
|
||||
kubectl get networkpolicy -n juwan
|
||||
|
||||
# 测试连接
|
||||
kubectl exec -it -n juwan <envoy-pod> -- \
|
||||
curl http://user-api-svc:8888/health
|
||||
```
|
||||
|
||||
### 3. CSRF 错误
|
||||
|
||||
- 确保设置了 `Origin` 和 `Referer` header
|
||||
- 检查 `additional_origins` 配置是否包含你的域名
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. JWKS 缓存
|
||||
|
||||
```yaml
|
||||
cache_ttl:
|
||||
seconds: 300 # 缓存 5 分钟,减少 RPC 调用
|
||||
```
|
||||
|
||||
### 2. 连接池
|
||||
|
||||
```yaml
|
||||
http2_protocol_options: {} # 启用 HTTP/2 多路复用
|
||||
```
|
||||
|
||||
### 3. 速率限制调整
|
||||
|
||||
根据实际流量调整令牌桶参数:
|
||||
|
||||
```yaml
|
||||
token_bucket:
|
||||
max_tokens: 10000 # 最大令牌数
|
||||
tokens_per_fill: 10000 # 每次填充的令牌数
|
||||
fill_interval: 1s # 填充间隔
|
||||
```
|
||||
|
||||
## 监控和日志
|
||||
|
||||
### 访问日志
|
||||
|
||||
```bash
|
||||
# 查看访问日志
|
||||
kubectl logs -n juwan -l app=envoy-gateway --follow
|
||||
|
||||
# 格式包含:
|
||||
# - 请求时间、方法、路径
|
||||
# - 响应状态码、字节数
|
||||
# - 上游服务信息
|
||||
```
|
||||
|
||||
### Prometheus 指标
|
||||
|
||||
Envoy 在 `:9901/stats` 暴露 Prometheus 指标:
|
||||
|
||||
```bash
|
||||
kubectl port-forward -n juwan svc/envoy-gateway 9901:9901
|
||||
curl localhost:9901/stats | grep jwt
|
||||
```
|
||||
|
||||
## 生产环境检查清单
|
||||
|
||||
- [ ] 使用正式 TLS 证书(不是自签名)
|
||||
- [ ] 配置正确的 JWT_SECRET_KEY(强密码)
|
||||
- [ ] 启用 HTTPS(关闭 HTTP)
|
||||
- [ ] 配置网络策略限制访问
|
||||
- [ ] 启用访问日志和监控
|
||||
- [ ] 设置合理的速率限制
|
||||
- [ ] 测试 token 过期和刷新流程
|
||||
- [ ] 配置告警规则
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [Envoy JWT Authentication](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/jwt_authn/v3/config.proto)
|
||||
- [Envoy CSRF Protection](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/csrf/v3/csrf.proto)
|
||||
- [JWT RFC 7519](https://tools.ietf.org/html/rfc7519)
|
||||
@@ -0,0 +1,371 @@
|
||||
# Envoy 配置完整清单
|
||||
|
||||
## 📋 配置文件清单
|
||||
|
||||
### 1. Proto 更新
|
||||
- **文件**: [desc/rpc/users.proto](../../desc/rpc/users.proto)
|
||||
- **更改**: 添加了两个 RPC 方法
|
||||
- `ValidateToken()`: 验证 token 是否有效(检查黑名单)
|
||||
- `CheckPermission()`: 检查用户权限
|
||||
|
||||
### 2. Envoy 部署
|
||||
- **配置文件**: [envoy.yaml](./envoy.yaml)
|
||||
- HTTP 监听器(端口 8080)
|
||||
- HTTPS 监听器(端口 8443)
|
||||
- JWT 验证过滤器
|
||||
- CSRF 防护过滤器
|
||||
- 速率限制(DDoS 防护)
|
||||
- 路由配置
|
||||
|
||||
- **K8s 部署**: [../k8s/envoy-gateway.yaml](../k8s/envoy-gateway.yaml)
|
||||
- 2 个副本
|
||||
- 负载均衡器服务
|
||||
- Service Account 和 RBAC
|
||||
- Network Policy
|
||||
- ConfigMap 用于配置管理
|
||||
|
||||
### 3. 工具代码
|
||||
- **JWKS 生成**: [app/users/rpc/internal/utils/jwks.go](../../app/users/rpc/internal/utils/jwks.go)
|
||||
- `GenerateJWKSFromSecret()`: 从 JWT 密钥生成 JWKS
|
||||
- `GenerateJWKSEndpoint()`: 生成 JSON 输出供 Envoy 使用
|
||||
- `ExtractTokenMetadata()`: 提取 token 元数据
|
||||
|
||||
### 4. Dockerfile
|
||||
- **文件**: [Dockerfile](./Dockerfile)
|
||||
- **用途**: 构建 Envoy 容器镜像
|
||||
|
||||
### 5. 脚本
|
||||
- **文件**: [generate-jwks.sh](./generate-jwks.sh)
|
||||
- **用途**: 快速生成 JWKS JSON 文件
|
||||
|
||||
### 6. 文档
|
||||
- **文件**: [ENVOY_CONFIG_GUIDE.md](./ENVOY_CONFIG_GUIDE.md)
|
||||
- **内容**: 详细的配置和部署指南
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速部署步骤
|
||||
|
||||
### 步骤 1: 生成 TLS 证书
|
||||
|
||||
```bash
|
||||
# 测试环境:生成自签名证书
|
||||
openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt \
|
||||
-days 365 -nodes -subj "/CN=api.juwan.local"
|
||||
|
||||
# 创建 K8s Secret
|
||||
kubectl create secret tls envoy-tls \
|
||||
--cert=tls.crt \
|
||||
--key=tls.key \
|
||||
-n juwan
|
||||
```
|
||||
|
||||
### 步骤 2: 部署 Envoy Gateway
|
||||
|
||||
```bash
|
||||
# 应用部署文件
|
||||
kubectl apply -f deploy/k8s/envoy-gateway.yaml
|
||||
|
||||
# 验证部署
|
||||
kubectl get pods -n juwan -l app=envoy-gateway
|
||||
kubectl get svc -n juwan envoy-gateway
|
||||
|
||||
# 等待 LoadBalancer 获取外部 IP
|
||||
kubectl get svc -n juwan envoy-gateway -w
|
||||
```
|
||||
|
||||
### 步骤 3: 在 User RPC 中暴露 JWKS 端点
|
||||
|
||||
编辑 `app/users/rpc/rpcserver.go` 或 `main.go`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"http"
|
||||
"os"
|
||||
"juwan-backend/app/users/rpc/internal/utils"
|
||||
)
|
||||
|
||||
// 在启动 RPC server 前,添加 HTTP 端点
|
||||
func setupJWKSEndpoint() {
|
||||
secretKey := os.Getenv("JWT_SECRET_KEY")
|
||||
if secretKey == "" {
|
||||
secretKey = "your-default-secret-key"
|
||||
}
|
||||
|
||||
http.HandleFunc("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
jwksJSON, err := utils.GenerateJWKSEndpoint(secretKey, "default-key-id")
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate JWKS", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "public, max-age=300") // 缓存 5 分钟
|
||||
w.Write([]byte(jwksJSON))
|
||||
})
|
||||
|
||||
// 在独立的 goroutine 中启动 HTTP 服务器
|
||||
go func() {
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupJWKSEndpoint()
|
||||
|
||||
// ... 其他 RPC 启动代码 ...
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 4: 更新 User RPC 配置
|
||||
|
||||
编辑 `app/users/rpc/etc/pb.yaml`:
|
||||
|
||||
```yaml
|
||||
Name: pb.rpc
|
||||
ListenOn: 0.0.0.0:9001
|
||||
|
||||
Prometheus:
|
||||
Host: 0.0.0.0
|
||||
Port: 4001
|
||||
Path: /metrics
|
||||
|
||||
# ... 其他配置 ...
|
||||
|
||||
Jwt:
|
||||
SecretKey: "${JWT_SECRET_KEY:your-secret-jwt-key-change-this-in-production}"
|
||||
Issuer: "juwan-user-rpc"
|
||||
```
|
||||
|
||||
### 步骤 5: 构建并推送容器镜像
|
||||
|
||||
```bash
|
||||
# 构建 User API 镜像
|
||||
docker build -t your-registry/user-api:latest ./app/users/api/
|
||||
docker push your-registry/user-api:latest
|
||||
|
||||
# 构建 User RPC 镜像
|
||||
docker build -t your-registry/user-rpc:latest ./app/users/rpc/
|
||||
docker push your-registry/user-rpc:latest
|
||||
|
||||
# 构建 Envoy 镜像
|
||||
docker build -f deploy/envoy/Dockerfile -t your-registry/envoy-gateway:latest .
|
||||
docker push your-registry/envoy-gateway:latest
|
||||
```
|
||||
|
||||
### 步骤 6: 更新 K8s 部署
|
||||
|
||||
更新 `deploy/k8s/service/user/user-api.yaml` 和 `user-rpc.yaml`,确保使用正确的镜像和环境变量。
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试流程
|
||||
|
||||
### 1. 登录获取 Token
|
||||
|
||||
```bash
|
||||
# 获取 Envoy 外部 IP
|
||||
ENVOY_IP=$(kubectl get svc -n juwan envoy-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
|
||||
# 登录
|
||||
curl -k -X POST "https://$ENVOY_IP:443/api/v1/users/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "testuser",
|
||||
"password": "password123"
|
||||
}' | jq .
|
||||
|
||||
# 示例响应:
|
||||
# {
|
||||
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
# "expires": 1708780800
|
||||
# }
|
||||
```
|
||||
|
||||
### 2. 使用 Token 访问受保护资源
|
||||
|
||||
```bash
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
|
||||
curl -k -X GET "https://$ENVOY_IP:443/api/v1/users/123" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
### 3. 验证 CSRF 防护
|
||||
|
||||
```bash
|
||||
# POST 请求必须有正确的 Origin/Referer
|
||||
curl -k -X POST "https://$ENVOY_IP:443/api/v1/users/logout" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Origin: https://api.juwan.local" \
|
||||
-H "Referer: https://api.juwan.local/" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"userId": 123}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 验证检查清单
|
||||
|
||||
- [ ] Envoy 容器运行正常
|
||||
```bash
|
||||
kubectl logs -n juwan -l app=envoy-gateway
|
||||
```
|
||||
|
||||
- [ ] JWKS 端点可访问
|
||||
```bash
|
||||
kubectl exec -it -n juwan <envoy-pod> -- \
|
||||
curl http://user-rpc-svc:9001/.well-known/jwks.json
|
||||
```
|
||||
|
||||
- [ ] 后端服务健康
|
||||
```bash
|
||||
kubectl exec -it -n juwan <envoy-pod> -- \
|
||||
curl http://user-api-svc:8888/health
|
||||
```
|
||||
|
||||
- [ ] JWT 验证工作
|
||||
```bash
|
||||
# 不带 token 访问受保护资源应返回 401
|
||||
curl -k https://api.juwan.local/api/v1/users/123
|
||||
```
|
||||
|
||||
- [ ] CSRF 防护生效
|
||||
```bash
|
||||
# 缺少 Origin header 的 POST 应被拒绝
|
||||
curl -k -X POST https://api.juwan.local/api/v1/users/logout \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置调整
|
||||
|
||||
### 修改 JWT 密钥
|
||||
|
||||
```bash
|
||||
# 1. 更新 K8s Secret
|
||||
kubectl patch secret jwt-secret -n juwan \
|
||||
-p '{"data":{"JWT_SECRET_KEY":"'$(echo -n 'new-secret-key' | base64)'"}}'
|
||||
|
||||
# 2. 重启 User RPC 和 Envoy
|
||||
kubectl rollout restart deployment/user-rpc-svc -n juwan
|
||||
kubectl rollout restart deployment/envoy-gateway -n juwan
|
||||
```
|
||||
|
||||
### 调整 Envoy 速率限制
|
||||
|
||||
编辑 ConfigMap:
|
||||
```bash
|
||||
kubectl edit cm envoy-config -n juwan
|
||||
```
|
||||
|
||||
修改 `token_bucket` 参数:
|
||||
```yaml
|
||||
token_bucket:
|
||||
max_tokens: 5000 # 降低限制
|
||||
tokens_per_fill: 5000
|
||||
fill_interval: 1s
|
||||
```
|
||||
|
||||
### 添加信任的 CSRF 来源
|
||||
|
||||
编辑 ConfigMap:
|
||||
```yaml
|
||||
additional_origins:
|
||||
- exact: "https://admin.juwan.local"
|
||||
- exact: "https://web.juwan.local"
|
||||
- prefix: "https://app.juwan.local" # 支持前缀匹配
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 监控和日志
|
||||
|
||||
### 查看 Envoy 统计
|
||||
|
||||
```bash
|
||||
kubectl port-forward -n juwan svc/envoy-gateway 9901:9901
|
||||
curl localhost:9901/stats | grep -E "(jwt_authn|csrf|http_ratelimit)"
|
||||
```
|
||||
|
||||
### 实时日志
|
||||
|
||||
```bash
|
||||
kubectl logs -n juwan -l app=envoy-gateway -f
|
||||
|
||||
# 查看特定日志行
|
||||
kubectl logs -n juwan -l app=envoy-gateway | grep "401\|403"
|
||||
```
|
||||
|
||||
### 监控指标(集成 Prometheus)
|
||||
|
||||
```yaml
|
||||
# prometheus-scrape-config.yaml
|
||||
- job_name: 'envoy-gateway'
|
||||
static_configs:
|
||||
- targets: ['envoy-gateway.juwan.svc.cluster.local:9901']
|
||||
metrics_path: '/stats/prometheus'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文件位置
|
||||
|
||||
```
|
||||
deploy/
|
||||
├── envoy/
|
||||
│ ├── envoy.yaml ← Envoy 核心配置
|
||||
│ ├── ENVOY_CONFIG_GUIDE.md ← 详细指南
|
||||
│ ├── generate-jwks.sh ← JWKS 生成脚本
|
||||
│ ├── Dockerfile ← Envoy 镜像
|
||||
│ └── QUICK_REFERENCE.md ← 本文件
|
||||
├── k8s/
|
||||
│ ├── envoy-gateway.yaml ← K8s 部署清单
|
||||
│ └── secrets/jwt-secret.yaml ← JWT 密钥配置
|
||||
└── script/
|
||||
└── init-secrets.sh ← 初始化脚本
|
||||
|
||||
app/users/
|
||||
├── rpc/
|
||||
│ ├── internal/utils/
|
||||
│ │ ├── jwks.go ← JWKS 生成工具
|
||||
│ │ └── jwt.go ← JWT 管理器
|
||||
│ └── etc/pb.yaml ← RPC 配置
|
||||
└── api/
|
||||
└── etc/user-api.yaml ← API 配置
|
||||
|
||||
desc/
|
||||
└── rpc/users.proto ← Proto 定义(已更新)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤔 常见问题
|
||||
|
||||
1. **Envoy 无法连接到后端服务**
|
||||
- 检查 K8s Service DNS: `user-api-svc.juwan.svc.cluster.local`
|
||||
- 验证 NetworkPolicy 允许流量
|
||||
|
||||
2. **JWT 验证失败**
|
||||
- 确保 JWT_SECRET_KEY 一致
|
||||
- 检查 JWKS 端点是否可访问
|
||||
- 查看 Envoy 日志: `grep "jwt_authn" envoy.log`
|
||||
|
||||
3. **CSRF 防护过于严格**
|
||||
- 在 `additional_origins` 中添加允许的来源
|
||||
- 对于单页应用,确保发送 `Origin` header
|
||||
|
||||
4. **速率限制阻止正常流量**
|
||||
- 增加 `max_tokens` 和 `tokens_per_fill`
|
||||
- 针对特定客户端配置不同的限制
|
||||
|
||||
---
|
||||
|
||||
## 📞 获取帮助
|
||||
|
||||
- 查看 [Envoy 官方文档](https://www.envoyproxy.io/docs)
|
||||
- 查看 [JWT 规范](https://tools.ietf.org/html/rfc7519)
|
||||
- 检查 [CSRF 防护最佳实践](https://owasp.org/www-community/attacks/csrf)
|
||||
@@ -0,0 +1,331 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Envoy 快速部署脚本
|
||||
# 用途:自动化部署 Envoy Gateway 到 Kubernetes
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
NAMESPACE="${NAMESPACE:-juwan}"
|
||||
RELEASE_NAME="${RELEASE_NAME:-envoy-gateway}"
|
||||
TIMEOUT="${TIMEOUT:-300s}"
|
||||
CONTEXT="${CONTEXT:-}"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
# 检查依赖
|
||||
check_dependencies() {
|
||||
log_info "检查依赖..."
|
||||
|
||||
local missing_deps=()
|
||||
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
missing_deps+=("kubectl")
|
||||
fi
|
||||
|
||||
if ! command -v openssl &> /dev/null; then
|
||||
missing_deps+=("openssl")
|
||||
fi
|
||||
|
||||
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||||
log_error "缺少以下依赖: ${missing_deps[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "所有依赖已安装"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 生成 TLS 证书
|
||||
generate_tls_cert() {
|
||||
log_info "生成 TLS 证书..."
|
||||
|
||||
local cert_dir="certs"
|
||||
local key_file="$cert_dir/tls.key"
|
||||
local cert_file="$cert_dir/tls.crt"
|
||||
|
||||
# 创建 certs 目录
|
||||
mkdir -p "$cert_dir"
|
||||
|
||||
# 检查是否已存在证书
|
||||
if [ -f "$cert_file" ] && [ -f "$key_file" ]; then
|
||||
log_warn "证书已存在: $cert_file, $key_file"
|
||||
read -p "是否要重新生成? (y/n) " -t 10 -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_success "使用现有证书"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 生成自签名证书(仅用于测试)
|
||||
openssl req -x509 -newkey rsa:4096 \
|
||||
-keyout "$key_file" \
|
||||
-out "$cert_file" \
|
||||
-days 365 -nodes \
|
||||
-subj "/CN=api.juwan.local" \
|
||||
-addext "subjectAltName=DNS:api.juwan.local,DNS:*.juwan.local" \
|
||||
> /dev/null 2>&1
|
||||
|
||||
log_success "TLS 证书已生成"
|
||||
log_warn "警告: 这是自签名证书,仅用于测试环境"
|
||||
log_warn "生产环境应使用正式的 CA 签发证书"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 创建命名空间
|
||||
create_namespace() {
|
||||
log_info "创建 Kubernetes 命名空间..."
|
||||
|
||||
if kubectl get namespace "$NAMESPACE" &> /dev/null; then
|
||||
log_warn "命名空间已存在: $NAMESPACE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
kubectl create namespace "$NAMESPACE"
|
||||
log_success "命名空间已创建: $NAMESPACE"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 创建 TLS Secret
|
||||
create_tls_secret() {
|
||||
log_info "创建 TLS Secret..."
|
||||
|
||||
local cert_dir="certs"
|
||||
local key_file="$cert_dir/tls.key"
|
||||
local cert_file="$cert_dir/tls.crt"
|
||||
|
||||
# 检查证书文件
|
||||
if [ ! -f "$cert_file" ] || [ ! -f "$key_file" ]; then
|
||||
log_error "证书文件不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 Secret 是否已存在
|
||||
if kubectl get secret envoy-tls -n "$NAMESPACE" &> /dev/null; then
|
||||
log_warn "Secret 已存在,删除后重建"
|
||||
kubectl delete secret envoy-tls -n "$NAMESPACE"
|
||||
fi
|
||||
|
||||
# 创建 Secret
|
||||
kubectl create secret tls envoy-tls \
|
||||
-n "$NAMESPACE" \
|
||||
--cert="$cert_file" \
|
||||
--key="$key_file"
|
||||
|
||||
log_success "TLS Secret 已创建: envoy-tls"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 部署 Envoy Gateway
|
||||
deploy_envoy() {
|
||||
log_info "部署 Envoy Gateway..."
|
||||
|
||||
local manifest_file="deploy/k8s/envoy-gateway.yaml"
|
||||
|
||||
if [ ! -f "$manifest_file" ]; then
|
||||
log_error "找不到部署清单: $manifest_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 应用部署
|
||||
if [ -n "$CONTEXT" ]; then
|
||||
kubectl apply -f "$manifest_file" --context="$CONTEXT"
|
||||
else
|
||||
kubectl apply -f "$manifest_file"
|
||||
fi
|
||||
|
||||
log_success "Envoy Gateway 部署清单已应用"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 等待部署完成
|
||||
wait_deployment() {
|
||||
log_info "等待部署完成(超时: $TIMEOUT)..."
|
||||
|
||||
kubectl rollout status deployment/envoy-gateway \
|
||||
-n "$NAMESPACE" \
|
||||
--timeout="$TIMEOUT" || {
|
||||
log_error "部署超时"
|
||||
return 1
|
||||
}
|
||||
|
||||
log_success "部署已完成"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 验证部署
|
||||
verify_deployment() {
|
||||
log_info "验证部署..."
|
||||
|
||||
# 检查 Pod
|
||||
local pod_count=$(kubectl get pods -n "$NAMESPACE" \
|
||||
-l app=envoy-gateway \
|
||||
-o jsonpath='{.items | length}')
|
||||
|
||||
if [ "$pod_count" -eq 0 ]; then
|
||||
log_error "未找到 Envoy 容器"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "找到 $pod_count 个 Envoy 容器"
|
||||
|
||||
# 检查 Service
|
||||
local svc_status=$(kubectl get svc -n "$NAMESPACE" |
|
||||
grep envoy-gateway || echo "")
|
||||
|
||||
if [ -z "$svc_status" ]; then
|
||||
log_error "未找到 Service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Service 已创建"
|
||||
|
||||
# 显示 LoadBalancer IP
|
||||
log_info "等待 LoadBalancer IP..."
|
||||
local lb_ip=""
|
||||
for i in {1..30}; do
|
||||
lb_ip=$(kubectl get svc envoy-gateway -n "$NAMESPACE" \
|
||||
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$lb_ip" ] && [ "$lb_ip" != "null" ]; then
|
||||
log_success "LoadBalancer IP: $lb_ip"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ $i -eq 30 ]; then
|
||||
log_warn "未获得 LoadBalancer IP(可能在内网环境或使用 NodePort)"
|
||||
kubectl get svc -n "$NAMESPACE" envoy-gateway
|
||||
break
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 显示部署信息
|
||||
show_summary() {
|
||||
log_info "部署摘要"
|
||||
echo ""
|
||||
echo " Namespace: $NAMESPACE"
|
||||
echo " Release: $RELEASE_NAME"
|
||||
echo ""
|
||||
echo " Pods:"
|
||||
kubectl get pods -n "$NAMESPACE" -l app=envoy-gateway \
|
||||
-o custom-columns=NAME:.metadata.name,STATUS:.status.phase,IP:.status.podIP \
|
||||
| sed 's/^/ /'
|
||||
|
||||
echo ""
|
||||
echo " Service:"
|
||||
kubectl get svc -n "$NAMESPACE" envoy-gateway \
|
||||
-o custom-columns=NAME:.metadata.name,TYPE:.spec.type,IP:.spec.clusterIP,EXTERNAL_IP:.status.loadBalancer.ingress[0].ip \
|
||||
| sed 's/^/ /'
|
||||
|
||||
echo ""
|
||||
echo " 后续步骤:"
|
||||
echo " 1. 在 User RPC 中暴露 JWKS 端点 (/.well-known/jwks.json)"
|
||||
echo " 2. 配置 JWT_SECRET_KEY 环境变量"
|
||||
echo " 3. 测试 JWT 验证: curl -k https://<ENVOY_IP>/api/v1/users/login"
|
||||
echo ""
|
||||
echo " 文档:"
|
||||
echo " - 配置指南: deploy/envoy/ENVOY_CONFIG_GUIDE.md"
|
||||
echo " - 快速参考: deploy/envoy/QUICK_REFERENCE.md"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 清理部署
|
||||
cleanup() {
|
||||
log_warn "清理 Envoy Gateway..."
|
||||
|
||||
kubectl delete -f deploy/k8s/envoy-gateway.yaml -n "$NAMESPACE" || true
|
||||
kubectl delete secret envoy-tls -n "$NAMESPACE" || true
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo ""
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Envoy Gateway 快速部署脚本 ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# 解析命令行参数
|
||||
local cmd="${1:-deploy}"
|
||||
|
||||
case "$cmd" in
|
||||
deploy)
|
||||
check_dependencies || exit 1
|
||||
generate_tls_cert || exit 1
|
||||
create_namespace || exit 1
|
||||
create_tls_secret || exit 1
|
||||
deploy_envoy || exit 1
|
||||
wait_deployment || exit 1
|
||||
verify_deployment || exit 1
|
||||
show_summary
|
||||
log_success "Envoy Gateway 已成功部署!"
|
||||
;;
|
||||
cleanup)
|
||||
cleanup
|
||||
;;
|
||||
status)
|
||||
log_info "部署状态"
|
||||
kubectl get all -n "$NAMESPACE" -l app=envoy-gateway
|
||||
;;
|
||||
logs)
|
||||
log_info "Envoy 日志"
|
||||
kubectl logs -n "$NAMESPACE" -l app=envoy-gateway -f
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 <命令>"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " deploy 部署 Envoy Gateway(默认)"
|
||||
echo " cleanup 移除部署"
|
||||
echo " status 查看部署状态"
|
||||
echo " logs 查看 Envoy 日志"
|
||||
echo ""
|
||||
echo "环境变量:"
|
||||
echo " NAMESPACE K8s 命名空间(默认: juwan)"
|
||||
echo " RELEASE_NAME 发布名称(默认: envoy-gateway)"
|
||||
echo " TIMEOUT 部署超时(默认: 300s)"
|
||||
echo " CONTEXT K8s 上下文(可选)"
|
||||
echo ""
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,385 @@
|
||||
static_resources:
|
||||
listeners:
|
||||
# HTTP 监听器(重定向到 HTTPS)
|
||||
- name: listener_http
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_number: 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
|
||||
http_filters:
|
||||
# CSRF 防护过滤器
|
||||
- name: envoy.filters.http.local_ratelimit
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
|
||||
stat_prefix: http_local_rate_limiter
|
||||
token_bucket:
|
||||
max_tokens: 1000
|
||||
tokens_per_fill: 1000
|
||||
fill_interval: 1s
|
||||
filter_enabled:
|
||||
runtime_key: local_rate_limit_enabled
|
||||
default_value:
|
||||
numerator: 100
|
||||
denominator: HUNDRED
|
||||
filter_enforced:
|
||||
runtime_key: local_rate_limit_enforced
|
||||
default_value:
|
||||
numerator: 100
|
||||
denominator: HUNDRED
|
||||
|
||||
# 路由过滤器
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
route_config:
|
||||
name: local_route
|
||||
virtual_hosts:
|
||||
- name: backend
|
||||
domains: ["*"]
|
||||
routes:
|
||||
# 登录端点 - 不需要 JWT
|
||||
- match:
|
||||
path: /api/v1/users/login
|
||||
headers:
|
||||
- name: ":method"
|
||||
string_match:
|
||||
exact: "POST"
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
# 注册端点 - 不需要 JWT
|
||||
- match:
|
||||
path: /api/v1/users/register
|
||||
headers:
|
||||
- name: ":method"
|
||||
string_match:
|
||||
exact: "POST"
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
# 其他所有用户 API 端点 - 需要 JWT
|
||||
- match:
|
||||
prefix: /api/v1/users
|
||||
headers:
|
||||
- name: ":method"
|
||||
string_match:
|
||||
exact: "GET"
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
request_headers_to_add:
|
||||
- header:
|
||||
key: "x-verified-user"
|
||||
value: "%REQ(X-USER-ID)%"
|
||||
|
||||
# 订单 API - 需要 JWT
|
||||
- match:
|
||||
prefix: /api/v1/orders
|
||||
route:
|
||||
cluster: order_api_cluster
|
||||
timeout: 30s
|
||||
request_headers_to_add:
|
||||
- header:
|
||||
key: "x-verified-user"
|
||||
value: "%REQ(X-USER-ID)%"
|
||||
|
||||
# 健康检查端点
|
||||
- match:
|
||||
path: /health
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 10s
|
||||
|
||||
# 默认路由
|
||||
- match:
|
||||
prefix: /
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
direct_response:
|
||||
status: 404
|
||||
body:
|
||||
inline_string: "Not Found"
|
||||
|
||||
# HTTPS 监听器(需要配置 TLS 证书)
|
||||
- name: listener_https
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_number: 8443
|
||||
filter_chains:
|
||||
- transport_socket:
|
||||
name: envoy.transport_sockets.tls
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
|
||||
common_tls_context:
|
||||
tls_certificates:
|
||||
- certificate_chain:
|
||||
filename: /etc/envoy/certs/tls.crt
|
||||
private_key:
|
||||
filename: /etc/envoy/certs/tls.key
|
||||
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_https
|
||||
access_log:
|
||||
- name: envoy.access_loggers.file
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
|
||||
path: /var/log/envoy/access.log
|
||||
format: |
|
||||
[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%"
|
||||
%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT%
|
||||
"%DURATION%" "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
|
||||
"%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"
|
||||
|
||||
http_filters:
|
||||
# JWT 验证过滤器
|
||||
- name: envoy.filters.http.jwt_authn
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
|
||||
providers:
|
||||
jwt_provider:
|
||||
issuer: "juwan-user-rpc"
|
||||
audiences: "api.juwan.local"
|
||||
# 本地验证(离线模式)- 需要在 ConfigMap 中配置公钥
|
||||
local_jwks:
|
||||
inline_string: |
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "oct",
|
||||
"k": "YOUR-BASE64-ENCODED-SECRET-KEY"
|
||||
}
|
||||
]
|
||||
}
|
||||
# 也可以使用远程 JWKS(更推荐)
|
||||
# remote_jwks:
|
||||
# http_uri:
|
||||
# uri: "http://user-rpc-svc:9001/.well-known/jwks.json"
|
||||
# cluster: user_rpc_cluster
|
||||
# timeout: 5s
|
||||
# cache_ttl:
|
||||
# seconds: 300
|
||||
# payload_in_metadata: "JWT_PAYLOAD"
|
||||
rules:
|
||||
# 不需要验证的路由
|
||||
- match:
|
||||
prefix: /api/v1/users/login
|
||||
allow_missing_or_failed: true
|
||||
|
||||
- match:
|
||||
prefix: /api/v1/users/register
|
||||
allow_missing_or_failed: true
|
||||
|
||||
- match:
|
||||
path: /health
|
||||
allow_missing_or_failed: true
|
||||
|
||||
# 所有其他路由都需要有效的 JWT
|
||||
- match:
|
||||
prefix: /
|
||||
requires:
|
||||
provider_name: jwt_provider
|
||||
|
||||
# CSRF 防护过滤器
|
||||
- name: envoy.filters.http.csrf
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy
|
||||
filter_enabled:
|
||||
default_value:
|
||||
numerator: 100
|
||||
denominator: HUNDRED
|
||||
runtime_key: csrf_filter_enabled
|
||||
shadow_enabled:
|
||||
default_value:
|
||||
numerator: 0
|
||||
denominator: HUNDRED
|
||||
runtime_key: csrf_filter_shadow_enabled
|
||||
additional_origins:
|
||||
- exact: "https://admin.juwan.local"
|
||||
ignore_method_matches:
|
||||
- google_re2:
|
||||
regex: "^(GET|HEAD|OPTIONS|TRACE)$"
|
||||
|
||||
# 代理验证过滤器(可选 - 调用 RPC 验证 token 黑名单)
|
||||
# - name: envoy.filters.http.ext_authz
|
||||
# typed_config:
|
||||
# "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
|
||||
# grpc_service:
|
||||
# envoy_grpc:
|
||||
# cluster_name: user_rpc_cluster
|
||||
# failure_mode_allow: false
|
||||
# with_request_body:
|
||||
# max_request_bytes: 8192
|
||||
# allow_partial_message: false
|
||||
|
||||
# 本地速率限制(DDOS 防护)
|
||||
- name: envoy.filters.http.local_ratelimit
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
|
||||
stat_prefix: https_local_rate_limiter
|
||||
token_bucket:
|
||||
max_tokens: 10000
|
||||
tokens_per_fill: 10000
|
||||
fill_interval: 1s
|
||||
filter_enabled:
|
||||
runtime_key: local_rate_limit_enabled
|
||||
default_value:
|
||||
numerator: 100
|
||||
denominator: HUNDRED
|
||||
|
||||
# 路由过滤器
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
route_config:
|
||||
name: https_route
|
||||
virtual_hosts:
|
||||
- name: backend
|
||||
domains: ["*"]
|
||||
routes:
|
||||
# 登录和注册不需要 JWT
|
||||
- match:
|
||||
path: /api/v1/users/login
|
||||
headers:
|
||||
- name: ":method"
|
||||
string_match:
|
||||
exact: "POST"
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
path: /api/v1/users/register
|
||||
headers:
|
||||
- name: ":method"
|
||||
string_match:
|
||||
exact: "POST"
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
# 用户 API(带 JWT 验证)
|
||||
- match:
|
||||
prefix: /api/v1/users
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
request_headers_to_add:
|
||||
- header:
|
||||
key: "x-verified-user"
|
||||
value: "%REQ(X-USER-ID)%"
|
||||
|
||||
# 订单 API(带 JWT 验证)
|
||||
- match:
|
||||
prefix: /api/v1/orders
|
||||
route:
|
||||
cluster: order_api_cluster
|
||||
timeout: 30s
|
||||
request_headers_to_add:
|
||||
- header:
|
||||
key: "x-verified-user"
|
||||
value: "%REQ(X-USER-ID)%"
|
||||
|
||||
# 健康检查
|
||||
- match:
|
||||
path: /health
|
||||
route:
|
||||
cluster: user_api_cluster
|
||||
timeout: 10s
|
||||
|
||||
# 默认路由
|
||||
- match:
|
||||
prefix: /
|
||||
direct_response:
|
||||
status: 404
|
||||
body:
|
||||
inline_string: "Not Found"
|
||||
|
||||
clusters:
|
||||
# User API 集群
|
||||
- name: user_api_cluster
|
||||
connect_timeout: 10s
|
||||
type: STRICT_DNS
|
||||
dns_lookup_family: V4_ONLY
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: user_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: user-api-svc
|
||||
port_number: 8888
|
||||
health_checks:
|
||||
- timeout: 5s
|
||||
interval: 10s
|
||||
unhealthy_threshold: 2
|
||||
healthy_threshold: 2
|
||||
http_health_check:
|
||||
path: /health
|
||||
expected_statuses:
|
||||
- start: 200
|
||||
end: 299
|
||||
|
||||
# Order API 集群
|
||||
- name: order_api_cluster
|
||||
connect_timeout: 10s
|
||||
type: STRICT_DNS
|
||||
dns_lookup_family: V4_ONLY
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: order_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: order-api-svc
|
||||
port_number: 8889
|
||||
health_checks:
|
||||
- timeout: 5s
|
||||
interval: 10s
|
||||
unhealthy_threshold: 2
|
||||
healthy_threshold: 2
|
||||
http_health_check:
|
||||
path: /health
|
||||
expected_statuses:
|
||||
- start: 200
|
||||
end: 299
|
||||
|
||||
# User RPC 集群(用于 ext_authz 调用)
|
||||
- name: user_rpc_cluster
|
||||
connect_timeout: 10s
|
||||
type: STRICT_DNS
|
||||
dns_lookup_family: V4_ONLY
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: user_rpc_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: user-rpc-svc
|
||||
port_number: 9001
|
||||
http2_protocol_options: {}
|
||||
|
||||
admin:
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_number: 9901
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 生成 JWKS JSON 文件的脚本
|
||||
# 用于 Envoy JWT 验证
|
||||
|
||||
set -e
|
||||
|
||||
# 参数
|
||||
JWT_SECRET_KEY="${1:-your-secret-key-change-this-in-production}"
|
||||
OUTPUT_FILE="${2:-jwks.json}"
|
||||
KEY_ID="${3:-default-key-id}"
|
||||
|
||||
echo "生成 JWKS JSON..."
|
||||
echo "- Secret Key: ${JWT_SECRET_KEY:0:10}..."
|
||||
echo "- Key ID: $KEY_ID"
|
||||
echo "- Output: $OUTPUT_FILE"
|
||||
|
||||
# 对密钥进行 base64 编码(URL-safe 无填充)
|
||||
ENCODED_KEY=$(echo -n "$JWT_SECRET_KEY" | base64 | tr '+/' '-_' | sed 's/=//g')
|
||||
|
||||
# 生成 JWKS JSON
|
||||
cat > "$OUTPUT_FILE" <<EOF
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "oct",
|
||||
"use": "sig",
|
||||
"kid": "$KEY_ID",
|
||||
"k": "$ENCODED_KEY",
|
||||
"alg": "HS256"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "✓ JWKS 文件已生成: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "内容预览:"
|
||||
cat "$OUTPUT_FILE"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "配置说明:"
|
||||
echo "1. 在 user-rpc 的 .well-known/jwks.json 端点暴露此文件"
|
||||
echo "2. 在 Envoy 中配置远程 JWKS URI:"
|
||||
echo " remote_jwks:"
|
||||
echo " http_uri:"
|
||||
echo " uri: http://user-rpc-svc:9001/.well-known/jwks.json"
|
||||
echo ""
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
# JWT 和认证配置完整设置脚本
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔐 Juwan JWT 认证配置脚本"
|
||||
echo "===================================="
|
||||
|
||||
NAMESPACE="juwan"
|
||||
JWT_SECRET=$(openssl rand -hex 32)
|
||||
JWKS_KEY_ID="juwan-key-2026"
|
||||
|
||||
echo "✅ 生成 JWT 密钥..."
|
||||
echo " Secret: $JWT_SECRET"
|
||||
|
||||
# Step 1: 创建 JWT Secret
|
||||
echo ""
|
||||
echo "📝 创建 K8s Secret..."
|
||||
kubectl create secret generic jwt-secret \
|
||||
--from-literal=key=$JWT_SECRET \
|
||||
-n $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Step 2: 生成 JWKS JSON(包含公钥)
|
||||
# 注意:对于 HMAC 算法,JWKS 包含密钥本身
|
||||
JWKS_JSON=$(cat <<EOF
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "oct",
|
||||
"kid": "$JWKS_KEY_ID",
|
||||
"k": "$(echo -n $JWT_SECRET | base64 -w 0)",
|
||||
"alg": "HS256",
|
||||
"use": "sig"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "📝 创建 JWKS ConfigMap..."
|
||||
kubectl create configmap jwks-config \
|
||||
--from-literal=jwks.json="$JWKS_JSON" \
|
||||
-n $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
echo ""
|
||||
echo "✅ JWT 认证配置完成!"
|
||||
echo ""
|
||||
echo "后续步骤:"
|
||||
echo "1. 更新 Envoy ConfigMap,挂载 JWKS 文件"
|
||||
echo "2. 在各 API 服务中配置 JWT_SECRET 环境变量"
|
||||
echo "3. 登录端点使用此密钥签名 Token"
|
||||
echo ""
|
||||
echo "JWT 密钥已保存到 K8s Secret: jwt-secret"
|
||||
echo "JWKS 已保存到 K8s ConfigMap: jwks-config"
|
||||
Reference in New Issue
Block a user