iam-system/ ├── buf.gen.yaml ├── buf.yaml ├── cmd │ └── server │ └── main.go ├── config │ └── config.yaml ├── internal │ ├── api │ │ └── rpc_service.go │ ├── auth │ │ ├── middleware.go │ │ └── signature_v4.go │ ├── crypto │ │ ├── key_crypto.go │ │ └── key_rotation.go │ ├── model │ │ ├── accesskey.go │ │ ├── policy.go │ │ └── user.go │ ├── policy │ │ └── engine.go │ ├── service │ │ ├── accesskey.go │ │ ├── policy.go │ │ └── user.go │ ├── store │ │ ├── accesskey_store.go │ │ ├── policy_store.go │ │ ├── postgres.go │ │ └── user_store.go │ └── util │ └── util.go ├── migrations │ ├── 000001_init_schema.down.sql │ └── 000001_init_schema.up.sql ├── proto │ └── iam.proto └── scripts └── generate_keys.sh
安全密钥生成
./scripts/generate_keys.shSQL迁移工具使用说明
- 安装 migrate 工具:
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest- 执行迁移:
migrate -path /Users/mac/workspace/vgo_micro_service/vgo-iam/migrations/ -database "postgres://vgo_user:vgo_password@postgres:5432/vgo_iam?sslmode=disable" up- 回滚迁移:
migrate -path migrations -database "postgres://username:password@localhost:5432/iam_db?sslmode=disable" down 1- 使用 Docker 执行迁移
docker run -v $(pwd)/migrations:/migrations --network host migrate/migrate \
-path=/migrations/ \
-database "postgres://username:password@localhost:5432/iam_db?sslmode=disable" upgo build -ldflags "-X github.com/vera-byte/vgo-iam/internal/version.Version=1.2.3 -X github.com/vera-byte/vgo-iam/internal/version.Commit=$(git rev-parse HEAD) -X github.com/vera-byte/vgo-iam/internal/version.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
docker-compose exec postgres pg_dump -U iam_user -d iam_db > backup.sql关键设计说明 数据完整性保障:
使用外键约束确保关联数据完整性
添加 ON DELETE CASCADE 自动清理关联数据
使用 CHECK 约束限制状态字段值
事务支持:init admin 命令现已支持数据库事务,确保管理员用户初始化过程中的数据一致性。如果在创建用户、设置密码、创建策略或生成访问密钥的任何步骤中发生错误,系统将自动回滚所有已执行的操作,保证数据库状态的完整性
性能优化:
为所有查询条件创建索引
为状态字段和关联ID字段创建索引
审计字段:
包含 created_at 和 updated_at 时间戳
预留 last_used_at 用于后续审计
安全考虑:
密钥字段使用 encrypted_ 前缀明确标识加密数据
密码字段预留但不强制使用
扩展性设计:
策略文档使用 JSONB 类型存储,支持灵活的策略定义
表结构设计支持后续添加审计日志等功能
这套迁移文件提供了完整的 IAM 系统数据存储方案,支持用户管理、策略管理和访问密钥管理等核心功能,同时考虑了性能、安全性和可扩展性需求。
VGO-IAM 系统实现了一套基于 HMAC-SHA256 的 V4 签名算法,类似于 AWS IAM 的访问控制机制。该系统通过 gRPC 提供服务,使用 metadata 传递认证信息。
加签过程主要在客户端完成,核心步骤如下:
首先,客户端需要准备要发送的请求数据,并将其序列化为 JSON 字符串。
// 示例:将请求对象序列化为JSON
reqData, err := json.Marshal(req)
if err != nil {
log.Fatalf("序列化请求数据失败: %v", err)
}签名生成通过 signature.SignV4 函数完成,该函数实现了以下步骤:
-
获取当前时间戳:使用当前时间的 Unix 时间戳
-
构建待签名字符串:格式为
IAM-HMAC-SHA256\n{timestamp}\n{requestDataHash},其中requestDataHash是请求数据的 SHA256 哈希值 -
派生签名密钥:使用多重 HMAC-SHA256 运算派生密钥:
- 首先,使用日期和基础密钥生成日期密钥
- 然后,使用日期密钥和区域(default)生成区域密钥
- 接着,使用区域密钥和服务名称(iam)生成服务密钥
- 最后,使用服务密钥和请求类型(request)生成最终签名密钥
-
计算最终签名:使用派生的签名密钥对"待签名字符串"进行 HMAC-SHA256 运算,并将结果进行 Base64 编码
// 签名核心代码
func SignV4(accessKeyID, secretKey, requestData string) SignV4Result {
timestamp := time.Now().Unix()
// 构建待签字符串
stringToSign := buildStringToSign(timestamp, requestData)
// 计算签名
return SignV4Result{
SecretKey: secretKey,
AccessKeyID: accessKeyID,
Signature: calculateSignature(stringToSign, secretKey, timestamp),
Timestamp: timestamp,
}
}最后,将生成的签名信息添加到 gRPC 调用的 metadata 中,以便服务端验证:
// 将签名信息添加到metadata
md := metadata.Pairs(
"access-key-id", signer.AccessKeyID,
"signature", signer.Signature,
"x-iam-date", strconv.FormatInt(signer.Timestamp, 10),
"request-data", string(reqData),
)
ctx = metadata.NewOutgoingContext(ctx, md)验签过程在服务端进行,主要通过 AccessKeyInterceptor 拦截器和 VerifySignV4 函数完成:
服务端从 gRPC 调用的 metadata 中提取访问密钥 ID、签名、时间戳和请求数据:
accessKeyID := vgokit.GetMetadataValue(ctx, "access-key-id")
sign := vgokit.GetMetadataValue(ctx, "signature")
timestamp := vgokit.GetMetadataValue(ctx, "x-iam-date")
requestData := vgokit.GetMetadataValue(ctx, "request-data")首先验证时间戳是否在允许的时间窗口内(默认 ±5 分钟),防止重放攻击:
func verifyTimestamp(timestamp int64) bool {
// 转换为时间
t := time.Unix(timestamp, 0)
// 检查时间戳是否在允许范围内(±5分钟)
now := time.Now()
diff := now.Sub(t)
return diff.Abs() <= 5*time.Minute
}根据访问密钥 ID 从存储中获取对应的密钥信息,并检查密钥状态是否为 "active":
// 获取访问密钥
ak, err := akStore.GetByAccessKeyID(accessKeyID)
if err != nil {
return nil, status.Error(codes.Unauthenticated, "非法访问,请检查访问密钥是否正确")
}
// 验证密钥状态
if ak.Status != "active" {
return nil, status.Error(codes.PermissionDenied, "非法访问,请检查密钥状态是否正确")
}最后,使用 VerifySignV4 函数验证签名是否有效:
// 验证签名
valid, err := signature.VerifySignV4(sign, requestData, timestamp, string(ak.SecretAccessKey))
if err != nil || !valid {
return nil, status.Error(codes.Unauthenticated, "签名验证失败,请检查签名后再试!")
}VerifySignV4 函数的验证逻辑与加签逻辑对称:
- 解析时间戳
- 验证时间窗口
- 重新构建待签名字符串
- 使用相同的算法计算签名
- 比较计算得到的签名与请求中的签名是否一致
该签名机制具有以下安全特性:
- 防重放攻击:通过时间戳验证,确保请求只能在有限时间内有效
- 密钥派生机制:使用多重 HMAC-SHA256 派生密钥,增强安全性
- 完整性验证:通过对请求数据进行哈希和签名,确保数据在传输过程中未被篡改
- 密钥状态检查:验证访问密钥是否处于活动状态
如果遇到如下错误:
pq: permission denied for schema public
可以通过以下 SQL 命令解决:
-- 连接到 vgo_iam 数据库
\c vgo_iam
-- 授权 schema 使用权限
GRANT USAGE ON SCHEMA public TO vgo_iam;
-- 授权在 schema 内创建表的权限
GRANT CREATE ON SCHEMA public TO vgo_iam;