321 lines
7.9 KiB
Markdown
321 lines
7.9 KiB
Markdown
# 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)
|