add: user auth accomplished

This commit is contained in:
wwweww
2026-02-26 02:17:07 +08:00
parent 300058ad01
commit 60b6f40f9f
54 changed files with 1601 additions and 2303 deletions
-320
View File
@@ -1,320 +0,0 @@
# 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)
-371
View File
@@ -1,371 +0,0 @@
# 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)
-331
View File
@@ -1,331 +0,0 @@
#!/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 "$@"
-385
View File
@@ -1,385 +0,0 @@
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
-48
View File
@@ -1,48 +0,0 @@
#!/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 ""
-55
View File
@@ -1,55 +0,0 @@
#!/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"
-33
View File
@@ -1,33 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: snowflake-sve
namespace: juwan
spec:
ClusterIP: None
selector:
app: snowflake
ports:
- port: 9000
targetPort: 9000
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: snowflake
namespace: juwan
spec:
serviceName: snowflake-svc
replicas: 3
selector:
matchLabels:
app: snowflake
template:
metadata:
labels:
app: snowflake
spec:
containers:
- name: snowflake
image:
-262
View File
@@ -1,262 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: juwan
---
apiVersion: v1
kind: ConfigMap
metadata:
name: envoy-config
namespace: juwan
data:
envoy.yaml: |
static_resources:
listeners:
- name: listener_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
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- 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:
- match:
prefix: /api/v1/users
route:
cluster: user_api_cluster
timeout: 30s
- match:
prefix: /api/v1/orders
route:
cluster: order_api_cluster
timeout: 30s
- match:
prefix: /health
route:
cluster: user_api_cluster
timeout: 10s
- match:
prefix: /
route:
cluster: user_api_cluster
timeout: 30s
clusters:
- name: user_api_cluster
connect_timeout: 5s
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.juwan.svc.cluster.local
port_value: 8888
health_checks:
- timeout: 3s
interval: 10s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /health
- name: order_api_cluster
connect_timeout: 5s
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.juwan.svc.cluster.local
port_value: 8889
health_checks:
- timeout: 3s
interval: 10s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: /health
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
---
apiVersion: v1
kind: Service
metadata:
name: envoy-gateway
namespace: juwan
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: admin
port: 9901
targetPort: 9901
protocol: TCP
selector:
app: envoy-gateway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: envoy-gateway
namespace: juwan
labels:
app: envoy-gateway
spec:
replicas: 2
selector:
matchLabels:
app: envoy-gateway
template:
metadata:
labels:
app: envoy-gateway
spec:
serviceAccountName: envoy-gateway
containers:
- name: envoy
image: envoyproxy/envoy:v1.27-latest
ports:
- name: http
containerPort: 8080
- name: admin
containerPort: 9901
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
readinessProbe:
httpGet:
path: /stats
port: 9901
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /stats
port: 9901
initialDelaySeconds: 10
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: envoy-config
configMap:
name: envoy-config
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: envoy-gateway
namespace: juwan
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: envoy-gateway
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: envoy-gateway
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: envoy-gateway
subjects:
- kind: ServiceAccount
name: envoy-gateway
namespace: juwan
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: envoy-gateway-network-policy
namespace: juwan
spec:
podSelector:
matchLabels:
app: envoy-gateway
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- to:
- podSelector:
matchLabels:
app: user-api
- podSelector:
matchLabels:
app: order-api
ports:
- protocol: TCP
port: 8888
- protocol: TCP
port: 8889
+294
View File
@@ -0,0 +1,294 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: envoy-config
namespace: juwan
data:
envoy.yaml: |
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
route_config:
name: local_route
virtual_hosts:
- name: juwan_services
domains: ["*"]
routes:
- match:
path: /healthz
direct_response:
status: 200
body:
inline_string: ok
- match:
prefix: /api/email
route:
cluster: email_api_cluster
timeout: 30s
- match:
prefix: /api/users
route:
cluster: user_api_cluster
timeout: 30s
- match:
prefix: /
direct_response:
status: 404
body:
inline_string: "gateway route not found"
http_filters:
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
local TOKEN_COOKIE = "csrf_token"
local GUARD_COOKIE = "csrf_guard"
local TOKEN_HEADER = "x-csrf-token"
local GUARD_HEADER = "x-csrf-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 csrf_token_cookie = cookies[TOKEN_COOKIE]
local csrf_guard_cookie = cookies[GUARD_COOKIE]
request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_token_cookie", csrf_token_cookie == nil or csrf_token_cookie == "")
request_handle:streamInfo():dynamicMetadata():set("csrf", "need_set_guard_cookie", csrf_guard_cookie == nil or csrf_guard_cookie == "")
if csrf_token_cookie == nil or csrf_token_cookie == "" then
csrf_token_cookie = build_token(headers:get("x-request-id"))
request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", csrf_token_cookie)
else
request_handle:streamInfo():dynamicMetadata():set("csrf", "token_value", csrf_token_cookie)
end
if csrf_guard_cookie == nil or csrf_guard_cookie == "" then
csrf_guard_cookie = build_token(headers:get("x-request-id"))
request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", csrf_guard_cookie)
else
request_handle:streamInfo():dynamicMetadata():set("csrf", "guard_value", csrf_guard_cookie)
end
if is_safe_method(method) then
return
end
local csrf_token_header = headers:get(TOKEN_HEADER)
local csrf_guard_header = headers:get(GUARD_HEADER)
if csrf_token_header == nil or csrf_guard_header == nil then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"missing csrf headers"}'
)
return
end
if csrf_token_cookie == nil or csrf_guard_cookie == nil then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"missing csrf cookies"}'
)
return
end
if csrf_token_header ~= csrf_token_cookie or csrf_guard_header ~= csrf_guard_cookie then
request_handle:respond(
{[":status"] = "403", ["content-type"] = "application/json"},
'{"code":403,"message":"csrf 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=/; SameSite=Strict"
)
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=/; SameSite=Strict"
)
end
end
- 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-svc.juwan.svc.cluster.local
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-svc.juwan.svc.cluster.local
port_value: 8888
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: envoy-gateway
namespace: juwan
labels:
app: envoy-gateway
spec:
replicas: 2
revisionHistoryLimit: 5
selector:
matchLabels:
app: envoy-gateway
template:
metadata:
labels:
app: envoy-gateway
spec:
containers:
- name: envoy
image: envoyproxy/envoy:v1.31-latest
imagePullPolicy: IfNotPresent
command: ["/usr/local/bin/envoy"]
args:
- "-c"
- "/etc/envoy/envoy.yaml"
- "--log-level"
- "info"
ports:
- containerPort: 8080
name: http
- containerPort: 9901
name: admin
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
volumes:
- name: envoy-config
configMap:
name: envoy-config
---
apiVersion: v1
kind: Service
metadata:
name: envoy-gateway
namespace: juwan
spec:
selector:
app: envoy-gateway
ports:
- name: http
port: 80
targetPort: 8080
- name: admin
port: 9901
targetPort: 9901
type: ClusterIP
+12 -3
View File
@@ -19,12 +19,13 @@ spec:
serviceAccountName: find-endpoints
containers:
- name: email-api
image: email
image: 103.236.53.208:4418/library/email-api@sha256:fe5c66f5bcb1a39652620df42351de3e48227920a34be3110a45eb13db327020
ports:
- containerPort: 8888
- containerPort: 4001
env:
- name: KAFKA_BROKER
value: "my-cluster-kafka-bootstrap.kafka.svc.cluster.local:9092"
value: "my-cluster-kafka-bootstrap.kafka:9092"
- name: REDIS_M_HOST
value: "user-redis-master.juwan:6379"
- name: REDIS_S_HOST
@@ -65,10 +66,18 @@ kind: Service
metadata:
name: email-api-svc
namespace: juwan
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "4001"
prometheus.io/path: "/metrics"
spec:
ports:
- port: 8888
- name: http
port: 8888
targetPort: 8888
- name: metrics
port: 4001
targetPort: 4001
selector:
app: email-api
+12 -3
View File
@@ -24,7 +24,9 @@ spec:
serviceAccountName: find-endpoints
containers:
- name: email-consumer
image: 103.236.53.208:4418/library/email-consumer@sha256:6fe8a3a57310a5e79feecc4bf38ac2c5b8c58a7f200f104f7bf4707b9db5fc13
image: 103.236.53.208:4418/library/email-mq@sha256:a9f76e8f4a17d1c00cefc429962037550e17feebb5cf38b28d360c91c8ba3e68
ports:
- containerPort: 4001
resources:
requests:
cpu: 100m
@@ -46,10 +48,17 @@ kind: Service
metadata:
name: email-consumer-svc
namespace: juwan
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "4001"
prometheus.io/path: "/metrics"
spec:
ports:
- port: 8080
targetPort: 8080
# - port: 8080
# targetPort: 8080
- name: metrics
port: 4001
targetPort: 4001
selector:
app: email-consumer
+3 -3
View File
@@ -1,5 +1,5 @@
apiVersion: apps/v1
kind: Deployment
kind: StatefulSet
metadata:
name: snowflake
namespace: juwan
@@ -71,7 +71,7 @@ metadata:
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
kind: StatefulSet
name: snowflake
minReplicas: 3
maxReplicas: 10
@@ -94,7 +94,7 @@ metadata:
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
kind: StatefulSet
name: snowflake
minReplicas: 3
maxReplicas: 10
+49 -49
View File
@@ -6,7 +6,7 @@ metadata:
labels:
app: user-api
spec:
replicas: 3
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
@@ -19,7 +19,7 @@ spec:
serviceAccountName: find-endpoints
containers:
- name: user-api
image: user-api:v1
image: 103.236.53.208:4418/library/user-api@sha256:a152f5fd13fc865ae3d9aeaa54eacad6bcaa0cb4f0ccb770fbb746be95360991
ports:
- containerPort: 8888
readinessProbe:
@@ -61,50 +61,50 @@ spec:
selector:
app: user-api
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-api-hpa-c
namespace: juwan
labels:
app: user-api-hpa-c
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-api-hpa-m
namespace: juwan
labels:
app: user-api-hpa-m
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
#---
#
#apiVersion: autoscaling/v2
#kind: HorizontalPodAutoscaler
#metadata:
# name: user-api-hpa-c
# namespace: juwan
# labels:
# app: user-api-hpa-c
#spec:
# scaleTargetRef:
# apiVersion: apps/v1
# kind: Deployment
# name: user-api
# minReplicas: 3
# maxReplicas: 10
# metrics:
# - type: Resource
# resource:
# name: cpu
# target:
# type: Utilization
# averageUtilization: 80
#
#---
#
#apiVersion: autoscaling/v2
#kind: HorizontalPodAutoscaler
#metadata:
# name: user-api-hpa-m
# namespace: juwan
# labels:
# app: user-api-hpa-m
#spec:
# scaleTargetRef:
# apiVersion: apps/v1
# kind: Deployment
# name: user-api
# minReplicas: 3
# maxReplicas: 10
# metrics:
# - type: Resource
# resource:
# name: memory
# target:
# type: Utilization
# averageUtilization: 80
+150 -142
View File
@@ -6,7 +6,7 @@ metadata:
labels:
app: user-rpc
spec:
replicas: 3
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
@@ -29,7 +29,7 @@ spec:
]
containers:
- name: user-rpc
image: 103.236.53.208:4418/library/user-rpc@sha256:57746256905acb5757153aef536ebfd19338b7f935f01ba1f538fbfd0a12f6f5
image: 103.236.53.208:4418/library/user-rpc@sha256:3d1d3cc02188a9b1a29a308a4867638b25b6e480e5a6bdaeb938f262f53969b7
ports:
- containerPort: 9001
- containerPort: 4001
@@ -114,143 +114,151 @@ spec:
selector:
app: user-rpc
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-rpc-hpa-c
namespace: juwan
labels:
app: user-rpc-hpa-c
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-rpc
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-rpc-hpa-m
namespace: juwan
labels:
app: user-rpc-hpa-m
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-rpc
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
# Redis 主从复制
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisReplication
metadata:
name: user-redis
namespace: juwan
spec:
clusterSize: 3
kubernetesConfig:
image: quay.io/opstree/redis:v7.0.12
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
redisSecret:
name: user-redis
key: password
redisExporter:
enabled: true
image: quay.io/opstree/redis-exporter:latest
imagePullPolicy: Always
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
storage:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
---
# Sentinel 监控
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisSentinel
metadata:
name: user-redis-sentinel
namespace: juwan
spec:
clusterSize: 3
kubernetesConfig:
image: quay.io/opstree/redis-sentinel:v7.0.12
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
podSecurityContext:
runAsUser: 1000
fsGroup: 1000
redisSentinelConfig:
redisReplicationName: user-redis
masterGroupName: mymaster
redisPort: "6379"
quorum: "2"
downAfterMilliseconds: "5000"
failoverTimeout: "10000"
parallelSyncs: "1"
---
# PostgreSQL 集群
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
namespace: juwan
name: user-db
spec:
instances: 3
backup:
barmanObjectStore:
destinationPath: s3://juwan-dev-pg-backups-zj/pg-data/
endpointURL: https://cn-nb1.rains3.com
s3Credentials:
accessKeyId:
name: rc-creds
key: SOucqRaJr4OyfcIu
secretAccessKey:
name: rc-creds
key: tn2Agj9EowMwuPA9y7TdSL0AXKsMEz
wal:
compression: gzip
storage:
size: 1Gi
monitoring:
enablePodMonitor: true
#---
#apiVersion: autoscaling/v2
#kind: HorizontalPodAutoscaler
#metadata:
# name: user-rpc-hpa-c
# namespace: juwan
# labels:
# app: user-rpc-hpa-c
#spec:
# scaleTargetRef:
# apiVersion: apps/v1
# kind: Deployment
# name: user-rpc
# minReplicas: 3
# maxReplicas: 10
# metrics:
# - type: Resource
# resource:
# name: cpu
# target:
# type: Utilization
# averageUtilization: 80
#
#---
#apiVersion: autoscaling/v2
#kind: HorizontalPodAutoscaler
#metadata:
# name: user-rpc-hpa-m
# namespace: juwan
# labels:
# app: user-rpc-hpa-m
#spec:
# scaleTargetRef:
# apiVersion: apps/v1
# kind: Deployment
# name: user-rpc
# minReplicas: 3
# maxReplicas: 10
# metrics:
# - type: Resource
# resource:
# name: memory
# target:
# type: Utilization
# averageUtilization: 80
#---
## Redis 主从复制
#apiVersion: redis.redis.opstreelabs.in/v1beta2
#kind: RedisReplication
#metadata:
# name: user-redis
# namespace: juwan
#spec:
# clusterSize: 3
# kubernetesConfig:
# image: quay.io/opstree/redis:v7.0.12
# imagePullPolicy: IfNotPresent
# resources:
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 500m
# memory: 512Mi
# redisSecret:
# name: user-redis
# key: password
#
# redisExporter:
# enabled: true
# image: quay.io/opstree/redis-exporter:latest
# imagePullPolicy: Always
# podSecurityContext:
# runAsUser: 1000
# fsGroup: 1000
# storage:
# volumeClaimTemplate:
# spec:
# accessModes: ["ReadWriteOnce"]
# resources:
# requests:
# storage: 1Gi
#
#---
## Sentinel 监控
#apiVersion: redis.redis.opstreelabs.in/v1beta2
#kind: RedisSentinel
#metadata:
# name: user-redis-sentinel
# namespace: juwan
#spec:
# clusterSize: 3
# kubernetesConfig:
# image: quay.io/opstree/redis-sentinel:v7.0.12
# imagePullPolicy: IfNotPresent
# resources:
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 500m
# memory: 512Mi
# podSecurityContext:
# runAsUser: 1000
# fsGroup: 1000
# redisSentinelConfig:
# redisReplicationName: user-redis
# masterGroupName: mymaster
# redisPort: "6379"
# quorum: "2"
# downAfterMilliseconds: "5000"
# failoverTimeout: "10000"
# parallelSyncs: "1"
#
#---
## PostgreSQL 集群
#apiVersion: postgresql.cnpg.io/v1
#kind: Cluster
#metadata:
# namespace: juwan
# name: user-db
#spec:
# instances: 3
# primaryUpdateStrategy: unsupervised
# bootstrap:
# initdb:
# database: app
# owner: app
# # 只在 PVC 为空时初始化
# postInitSQL:
# - CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
# backup:
# barmanObjectStore:
# destinationPath: s3://juwan-dev-pg-backups-zj/pg-data/
# endpointURL: https://cn-nb1.rains3.com
# s3Credentials:
# accessKeyId:
# name: rc-creds
# key: SOucqRaJr4OyfcIu
# secretAccessKey:
# name: rc-creds
# key: tn2Agj9EowMwuPA9y7TdSL0AXKsMEz
# wal:
# compression: gzip
# storage:
# size: 1Gi
# monitoring:
# enablePodMonitor: true