This commit is contained in:
mangomqy
2025-11-13 02:54:06 +00:00
commit c5e51ed069
254 changed files with 54901 additions and 0 deletions

View File

@ -0,0 +1,356 @@
# 🔒 安全方案文档
## 概述
本项目实现了一套完整的敏感信息保护方案确保密码、证书、Token 等敏感数据在存储和传输过程中的安全性。
## 🎯 解决的安全问题
### 1. 硬编码敏感信息
**问题**:代码中硬编码了 Harbor 密码、K8s 证书等敏感信息
**解决方案**:全部移至环境变量,通过 `.env` 文件配置
### 2. 明文存储密码和证书
**问题**:数据库中以明文存储敏感数据
**解决方案**:使用 AES-256-GCM 加密存储
### 3. API 响应泄露敏感信息
**问题**API 返回完整的密码和证书数据
**解决方案**:自动脱敏,仅返回掩码(`••••••••`
### 4. 前端显示敏感信息
**问题**:前端表单可能显示原始密码
**解决方案**:显示掩码,修改时仅支持覆盖
## 🏗️ 架构设计
```
┌─────────────────────────────────────────────────────────────┐
│ 前端 (Frontend) │
│ • 显示脱敏数据(••••••••) │
│ • 修改时仅支持覆盖,不能查看原值 │
└─────────────────────────────────────────────────────────────┘
│ HTTPS
┌─────────────────────────────────────────────────────────────┐
│ REST API (Handler 层) │
│ • 接收请求中的明文敏感数据 │
│ • 响应时自动脱敏(调用 ToRegistryResponse/ToClusterResponse
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Service 层(业务逻辑) │
│ • 处理业务逻辑 │
│ • 不关心加密/解密细节 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Repository 层(数据持久化) │
│ • Create/Update: 自动加密敏感数据后存储 │
│ • GetByID/List: 自动解密敏感数据后返回 │
│ • 使用 AES-256-GCM 加密算法 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Database加密存储
│ • 密码加密存储Base64 编码的密文) │
│ • 证书加密存储Base64 编码的密文) │
│ • Token加密存储Base64 编码的密文) │
└─────────────────────────────────────────────────────────────┘
```
## 🔐 加密实现
### 加密算法
- **算法**AES-256-GCM (Galois/Counter Mode)
- **密钥长度**256 bits (由用户提供的密钥通过 SHA256 派生)
- **认证加密**:提供数据机密性和完整性
- **随机 Nonce**:每次加密使用随机 nonce同样的明文产生不同的密文
### 加密流程
```go
// 1. 用户配置加密密钥
encryptor := crypto.NewAESEncryptor(config.EncryptionKey)
// 2. Repository 创建时注入加密器
clusterRepo := mock.NewClusterRepositoryMock(encryptor)
registryRepo := mock.NewRegistryRepositoryMock(encryptor)
// 3. 存储时自动加密
func (r *RegistryRepositoryMock) Create(ctx context.Context, registry *entity.Registry) error {
encrypted := r.encryptRegistry(registry) // 加密敏感字段
r.registries[registry.ID] = encrypted
return nil
}
// 4. 读取时自动解密
func (r *RegistryRepositoryMock) GetByID(ctx context.Context, id string) (*entity.Registry, error) {
registry := r.registries[id]
return r.decryptRegistry(registry), nil // 解密敏感字段
}
```
### 加密的字段
#### Registry镜像仓库
-`Password` - Harbor/镜像仓库密码
#### ClusterKubernetes 集群)
-`CAData` - CA 证书
-`CertData` - 客户端证书
-`KeyData` - 客户端密钥
-`Token` - Bearer Token
## 🎭 脱敏显示
### DTO 转换
```go
// Registry 响应 - 自动脱敏
func ToRegistryResponse(registry *entity.Registry) *RegistryResponse {
response := &RegistryResponse{
Username: registry.Username, // 用户名不脱敏
Password: crypto.MaskSensitiveData(registry.Password), // 密码脱敏
HasPassword: registry.Password != "",
}
return response
}
// Cluster 响应 - 自动脱敏
func ToClusterResponse(cluster *entity.Cluster) *ClusterResponse {
response := &ClusterResponse{
CAData: crypto.MaskSensitiveData(cluster.CAData), // ••••••••
CertData: crypto.MaskSensitiveData(cluster.CertData), // ••••••••
KeyData: crypto.MaskSensitiveData(cluster.KeyData), // ••••••••
Token: crypto.MaskSensitiveData(cluster.Token), // ••••••••
HasCAData: cluster.CAData != "",
HasCertData: cluster.CertData != "",
}
return response
}
```
### API 响应示例
```json
{
"id": "registry-123",
"name": "Harbor Production",
"url": "https://harbor.example.com",
"username": "admin",
"password": "••••••••",
"has_password": true
}
```
## 🔧 配置指南
### 1. 生成加密密钥
```bash
# 生成强加密密钥
openssl rand -base64 32
# 生成 JWT 密钥
openssl rand -base64 32
```
### 2. 创建 .env 文件
```bash
# 复制模板
cp backend/.env.example backend/.env
# 编辑配置
nano backend/.env
```
### 3. 必填配置项
```bash
# 生产环境必须修改这些配置!
ENCRYPTION_KEY=<your-encryption-key-from-openssl>
JWT_SECRET=<your-jwt-secret-from-openssl>
```
### 4. 可选:配置默认资源
```bash
# 默认用户
DEFAULT_USER_USERNAME=admin
DEFAULT_USER_PASSWORD=your-secure-password
DEFAULT_USER_EMAIL=admin@example.com
# 默认集群
DEFAULT_CLUSTER_NAME=Production K8s
DEFAULT_CLUSTER_HOST=https://k8s.example.com:6443
DEFAULT_CLUSTER_CA_DATA=<base64-encoded-ca-cert>
DEFAULT_CLUSTER_CERT_DATA=<base64-encoded-client-cert>
DEFAULT_CLUSTER_KEY_DATA=<base64-encoded-client-key>
# 默认镜像仓库
DEFAULT_REGISTRY_NAME=Harbor Production
DEFAULT_REGISTRY_URL=https://harbor.example.com
DEFAULT_REGISTRY_USERNAME=admin
DEFAULT_REGISTRY_PASSWORD=your-harbor-password
```
## 🚀 使用指南
### 启动应用
```bash
cd backend
# 开发模式(使用 Mock 存储)
make run-mock
# 生产模式(使用实际数据库)
ADAPTER_MODE=prod DATABASE_URL="postgresql://..." make run-prod
```
### 验证加密
```bash
# 1. 创建一个 Registry
curl -X POST http://localhost:8080/api/v1/registries \
-H "Content-Type: application/json" \
-d '{
"name": "Test Registry",
"url": "https://registry.example.com",
"username": "admin",
"password": "MySecretPassword123"
}'
# 2. 获取 Registry密码已脱敏
curl http://localhost:8080/api/v1/registries/<registry-id>
# 响应示例:
# {
# "password": "••••••••", // 已脱敏
# "has_password": true
# }
```
## 📝 前端集成
### 显示脱敏数据
```typescript
// Registry 表单
<Input
type="password"
value={registry.password} // 显示为 ••••••••
placeholder="修改密码(留空保持不变)"
onChange={(e) => setPassword(e.target.value)}
/>
// 说明文字
{registry.has_password && (
<p className="text-sm text-gray-500">
当前已设置密码(加密存储)。输入新密码以覆盖。
</p>
)}
```
### 修改策略
- **查看时**:显示掩码 `••••••••`,不显示实际值
- **修改时**
- 输入新值 → 覆盖原值
- 留空 → 保持原值不变
- 无法查看原值
## 🔒 安全最佳实践
### ✅ DO应该做
1. **生产环境必须使用强密钥**
```bash
ENCRYPTION_KEY=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
```
2. **妥善保管 .env 文件**
- 不要提交到 Git已加入 `.gitignore`
- 使用密钥管理服务(如 AWS Secrets Manager, HashiCorp Vault
- 定期轮换密钥
3. **使用 HTTPS**
- 生产环境必须启用 TLS/SSL
- 使用有效的 SSL 证书
4. **定期审计**
- 定期检查访问日志
- 监控异常访问
### ❌ DON'T不应该做
1. ❌ 不要在代码中硬编码敏感信息
2. ❌ 不要将 `.env` 文件提交到版本控制
3. ❌ 不要在日志中打印敏感数据
4. ❌ 不要在前端缓存敏感信息
5. ❌ 不要使用默认密钥用于生产环境
## 🧪 测试
### 加密/解密测试
```bash
cd backend
go test ./internal/pkg/crypto -v
```
### 集成测试
```bash
# 运行所有测试
make test
# 测试加密存储
go test ./internal/adapter/output/persistence/mock -v
```
## 📊 性能考虑
- **加密开销**AES-GCM 加密非常快,对性能影响可忽略
- **内存使用**:每次读取时解密,不在内存中缓存明文
- **并发安全**Repository 使用 RWMutex 保护并发访问
## 🆘 故障排查
### 问题:解密失败
**原因**`ENCRYPTION_KEY` 发生变化
**解决方案**
1. 确保使用相同的加密密钥
2. 如果密钥丢失,需要重新创建所有敏感数据
### 问题API 返回空密码
**原因**:密码未设置或解密失败
**解决方案**
1. 检查 `has_password` 字段
2. 查看后端日志确认是否有解密错误
## 📚 相关文档
- [AES-GCM 加密算法](https://en.wikipedia.org/wiki/Galois/Counter_Mode)
- [Go crypto 包文档](https://pkg.go.dev/crypto)
- [OWASP 安全编码实践](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/)
## 🤝 贡献
如果发现安全问题,请:
1. 不要公开披露
2. 通过私密渠道联系维护者
3. 提供详细的复现步骤
## 📄 许可证
本项目的安全方案遵循项目主许可证。