357 lines
11 KiB
Markdown
357 lines
11 KiB
Markdown
# 🔒 安全方案文档
|
||
|
||
## 概述
|
||
|
||
本项目实现了一套完整的敏感信息保护方案,确保密码、证书、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/镜像仓库密码
|
||
|
||
#### Cluster(Kubernetes 集群)
|
||
- ✅ `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. 提供详细的复现步骤
|
||
|
||
## 📄 许可证
|
||
|
||
本项目的安全方案遵循项目主许可证。
|
||
|