# JWT Secret + ETCD Encryption Deployment Guide

完整的 JWT 认证系统部署指南，包括密钥管理、RBAC 权限控制和 ETCD 加密。

## 部署顺序

### 第1步：创建 Secret 和 RBAC（必需）

创建 JWT 秘钥和服务账户权限：

```bash
kubectl apply -f deploy/k8s/secrets/jwt-secret.yaml
```

验证创建成功：

```bash
# 检查 Secret
kubectl get secret jwt-secret -n juwan
kubectl get secret jwt-secret -n juwan -o yaml

# 检查 ServiceAccounts
kubectl get sa user-rpc -n juwan
kubectl get sa envoy-gateway -n juwan

# 检查 RBAC 权限
kubectl get role jwt-secret-reader -n juwan
kubectl get rolebinding -n juwan -l app=jwt-secret-reader
```

### 第2步：更新 user-rpc 部署（依赖第1步）

已自动更新 `deploy/k8s/service/user/user-rpc.yaml`：

- ✅ 更新 `serviceAccountName` 从 `find-endpoints` → `user-rpc`
- ✅ 添加环境变量 `JWT_SECRET_KEY` 从 Secret `jwt-secret` 读取

应用更新：

```bash
kubectl apply -f deploy/k8s/service/user/user-rpc.yaml
```

验证部署：

```bash
# 检查 ServiceAccount 已正确绑定
kubectl get deployment user-rpc -n juwan -o yaml | grep -A 5 serviceAccountName

# 查看 Pod 是否以 user-rpc ServiceAccount 身份运行
kubectl get pod -n juwan -l app=user-rpc -o yaml | grep serviceAccount

# 验证环境变量已注入
kubectl exec -it POD_NAME -n juwan -- env | grep JWT_SECRET_KEY
```

### 第3步：更新 Envoy 网关部署（依赖第1步）

已自动更新 `deploy/k8s/envoy/envoy.yaml`：

- ✅ 添加 `serviceAccountName: envoy-gateway` 到 Deployment spec

应用更新：

```bash
kubectl apply -f deploy/k8s/envoy/envoy.yaml
```

验证部署：

```bash
# 检查 ServiceAccount 已正确绑定
kubectl get deployment envoy-gateway -n juwan -o yaml | grep -A 2 serviceAccountName

# 检查 Pod 状态
kubectl get pod -n juwan -l app=envoy-gateway
```

### 第4步：启用 ETCD 加密（强烈推荐用于生产环境）

这是一个集群级别的配置，需要在 Kubernetes 控制平面节点上执行。

**前提条件：**
- 具有 Kubernetes 集群管理员权限
- 可以访问控制平面节点
- 备份 ETCD 数据库

**步骤：**

1. **生成加密密钥**
   ```bash
   head -c 32 /dev/urandom | base64
   ```
   记录输出的 Base64 密钥。

2. **创建加密配置文件**
   
   在控制平面节点上，创建 `/etc/kubernetes/encryption-config.yaml`：

   ```yaml
   apiVersion: apiserver.config.k8s.io/v1
   kind: EncryptionConfiguration
   resources:
     - resources:
         - secrets
       providers:
         - aescbc:
             keys:
               - name: key1
                 secret: <BASE64_ENCODED_32_BYTE_KEY>
         - identity: {}
   ```

   替换 `<BASE64_ENCODED_32_BYTE_KEY>` 为第1步生成的密钥。

3. **修改 kube-apiserver 配置**
   
   在控制平面节点上，编辑 `/etc/kubernetes/manifests/kube-apiserver.yaml`：

   **添加参数：**
   ```yaml
   spec:
     containers:
     - name: kube-apiserver
       command:
         - kube-apiserver
         - --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
   ```

   **添加卷挂载：**
   ```yaml
   spec:
     containers:
     - name: kube-apiserver
       volumeMounts:
       - name: encryption-config
         mountPath: /etc/kubernetes
         readOnly: true
   
   volumes:
   - name: encryption-config
     hostPath:
       path: /etc/kubernetes
       type: DirectoryOrCreate
   ```

4. **重启 kube-apiserver**
   
   修改清单后，kubelet 会自动重启 kube-apiserver。检查状态：
   
   ```bash
   # 在控制平面节点
   kubectl get pods -n kube-system | grep kube-apiserver
   
   # 监控重启过程
   kubectl logs -n kube-system -l component=kube-apiserver -f
   ```

5. **验证加密是否启用**
   
   创建一个新 Secret 并检查它在 ETCD 中是否加密：
   
   ```bash
   # 创建测试 Secret
   kubectl create secret generic test-secret -n juwan --from-literal=key=value
   
   # 从 control plane 节点检查 ETCD 数据
   # 如果数据不可读并包含加密标记，说明加密已启用
   ```

6. **保存加密密钥**
   
   ⚠️ **关键：将加密密钥安全地保存在离线存储中**
   - 密钥丢失后，无法解密 ETCD 中的数据
   - 无法恢复任何 Secrets
   - 建议用密码管理工具（如 HashiCorp Vault）或 HSM 存储密钥

### 第5步：验证整个系统

完整的验证清单：

```bash
# 检查所有 Secrets 已创建
kubectl get secret -n juwan
kubectl get secret jwt-secret -n juwan -o jsonpath='{.data.secret-key}' | base64 -d

# 检查 ServiceAccounts 已创建
kubectl get sa -n juwan
kubectl describe sa user-rpc -n juwan
kubectl describe sa envoy-gateway -n juwan

# 检查 RBAC 权限
kubectl get role -n juwan
kubectl get rolebinding -n juwan
kubectl describe role jwt-secret-reader -n juwan

# 测试权限：user-rpc 可以读 jwt-secret
kubectl auth can-i get secrets --as=system:serviceaccount:juwan:user-rpc --resource-name=jwt-secret -n juwan

# 测试权限：envoy-gateway 可以读 jwt-secret
kubectl auth can-i get secrets --as=system:serviceaccount:juwan:envoy-gateway --resource-name=jwt-secret -n juwan

# 测试权限：其他 ServiceAccount 无法读取
kubectl auth can-i get secrets --as=system:serviceaccount:juwan:other-service -n juwan

# 检查 Deployments 已正确配置
kubectl get deployment user-rpc -n juwan -o yaml | grep -A 2 serviceAccountName
kubectl get deployment envoy-gateway -n juwan -o yaml | grep -A 2 serviceAccountName

# 检查 Pods 是否已启动并运行
kubectl get pods -n juwan -l app=user-rpc
kubectl get pods -n juwan -l app=envoy-gateway

# 查看 JWT Secret 是否已挂载到 Pod
kubectl exec -it $(kubectl get pod -n juwan -l app=user-rpc -o name | head -1) -n juwan -- env | grep JWT_SECRET_KEY
```

## 监控和日志

### 查看 Pod 日志

```bash
# user-rpc 日志
kubectl logs -n juwan -l app=user-rpc -f --all-containers=true

# Envoy 日志
kubectl logs -n juwan -l app=envoy-gateway -f
```

### 检查 Pod 事件

```bash
# 查看 Pod 创建和启动事件
kubectl describe pod -n juwan -l app=user-rpc
kubectl describe pod -n juwan -l app=envoy-gateway
```

### 权限问题排查

如果 Pod 无法读取 Secret：

```bash
# 检查 Pod 使用的 ServiceAccount
kubectl get pod POD_NAME -n juwan -o yaml | grep serviceAccountName

# 检查 RBAC 绑定
kubectl get rolebinding -n juwan -o wide

# 检查 Role 权限定义
kubectl get role jwt-secret-reader -n juwan -o yaml

# 尝试用 Pod 的身份读取 Secret（需要 kubectl-user-impersonate 或类似工具）
kubectl get secret jwt-secret --as=system:serviceaccount:juwan:user-rpc -n juwan
```

## 安全最佳实践

### 1. 密钥轮换

周期性更换 JWT 秘钥（建议每季度）：

```bash
# 1. 生成新密钥
NEW_KEY=$(head -c 32 /dev/urandom | base64)

# 2. 更新 Secret
kubectl create secret generic jwt-secret \
  --from-literal=secret-key=$NEW_KEY \
  --dry-run=client -o yaml | kubectl apply -f -

# 3. 重启 Pods 以加载新密钥（滚动更新）
kubectl rollout restart deployment/user-rpc -n juwan
kubectl rollout restart deployment/envoy-gateway -n juwan

# 4. 验证新 Pods 已启动并运行
kubectl rollout status deployment/user-rpc -n juwan
kubectl rollout status deployment/envoy-gateway -n juwan

# 5. 已颁发的旧令牌将变为无效
# 需要用户重新登录获取新令牌
```

### 2. 审计和监控

在生产环境中启用 Kubernetes 审计日志来跟踪 Secret 访问：

```yaml
# kube-apiserver 审计策略示例
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # 记录 secret 资源的访问
  - level: RequestResponse
    verbs: ["get", "list", "watch"]
    resources: ["secrets"]
  # 记录所有认证失败
  - level: RequestResponse
    omitStages:
      - RequestReceived
    userGroups: ["system:unauthenticated"]
  - level: Metadata
    omitStages:
      - RequestReceived
```

### 3. 网络策略

使用 NetworkPolicy 限制 Pods 之间的通信：

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: jwt-secret-access
  namespace: juwan
spec:
  podSelector:
    matchLabels:
      app: jwt-secret-reader
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: user-rpc
        - podSelector:
            matchLabels:
              app: envoy-gateway
      ports:
        - protocol: TCP
          port: 443  # API Server
```

## 灾难恢复

### 备份 Secret 和 RBAC 配置

```bash
# 备份 JWT Secret
kubectl get secret jwt-secret -n juwan -o yaml > jwt-secret-backup.yaml

# 备份 RBAC 配置
kubectl get role jwt-secret-reader -n juwan -o yaml > jwt-role-backup.yaml
kubectl get rolebinding -n juwan -l app=jwt-secret-reader -o yaml > jwt-rolebinding-backup.yaml

# 加密备份文件
gpg --symmetric jwt-secret-backup.yaml
```

### 恢复步骤

如果 Secret 被意外删除：

```bash
# 从备份恢复
kubectl apply -f jwt-secret-backup.yaml

# 重启 Pods 以重新加载 Secret
kubectl rollout restart deployment/user-rpc -n juwan
kubectl rollout restart deployment/envoy-gateway -n juwan
```

## 常见问题

### Q: Pod 无法启动，显示 "failed to pull secret"

A: 检查：
1. Secret 是否存在：`kubectl get secret jwt-secret -n juwan`
2. ServiceAccount 是否绑定了 RBAC：`kubectl describe rolebinding -n juwan`
3. Secret 名称和命名空间是否正确

### Q: 加密后如何验证 ETCD 中的数据已加密？

A: 从 control plane 节点：
```bash
# 直接读取 ETCD（如果配置了加密，数据应该不可读）
sudo strings /var/lib/etcd/member/snap/db | grep -i secret
```

### Q: 能否更改加密密钥而不重新创建 ETCD？

A: 可以，但流程复杂：
1. 更新 encryption-config.yaml 中的新密钥
2. 将新密钥添加到提供程序列表（保持旧密钥）
3. 重启 kube-apiserver
4. 触发重新加密：`kubectl get all --all-namespaces -o json | kubectl apply -f -`

### Q: 如何在 Minikube 中启用 ETCD 加密？

A: 参考 `ENCRYPTION.md` 中的 Minikube 特定说明部分。

## 相关文件

- `jwt-secret.yaml` - Secret 和 RBAC 配置
- `ENCRYPTION.md` - ETCD 加密详细文档
- `README.md` - 快速参考指南
- `/deploy/k8s/service/user/user-rpc.yaml` - user-rpc Deployment 配置
- `/deploy/k8s/envoy/envoy.yaml` - Envoy 网关 Deployment 配置

## 下一步

部署完成后：

1. **集成 JWT 验证到 RPC Handlers**
   - 实现 gRPC unary interceptor
   - 验证令牌有效性
   - 处理令牌刷新逻辑

2. **集成 JWT 验证到 Envoy**
   - 扩展 Lua filter 进行令牌验证
   - 返回 401（无效令牌）或 200（有效令牌）

3. **端到端测试**
   - 创建用户和登录
   - 生成和验证 JWT
   - 测试令牌刷新和撤销
   - 验证 ETCD 加密

4. **生产部署**
   - 启用审计日志
   - 配置密钥轮换计划
   - 建立备份和恢复流程
   - 监控 Secret 访问
