Files
docker-compose-updater/internal/auth/session.go
T
ilovintit cea9b941cf
Build and Push / build (push) Failing after 13m20s
Initial commit: docker-compose-updater
Go 项目,包含:
- 服务端 updater:两阶段协议,ECDSA 签名验证,AES-GCM 加密
- 发送端 dcu-send:Gitea Action CLI
- internal/auth:加解密/签名/会话管理
- internal/docker:Docker CLI 容器查找/拉取/重建
- action/:Gitea Action 定义
- deploy/Dockerfile:多阶段构建
- .gitea/workflows/build.yaml:CI/CD
2026-06-08 15:16:46 +08:00

137 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package auth
import (
"crypto/rand"
"fmt"
"sync"
"time"
)
// sessionEntry 存储一次 Phase 1 协商的会话密钥。
type sessionEntry struct {
key []byte // AES-256 会话密钥
expiry time.Time
}
// SessionManager 管理内存中的会话密钥(Nonce → AES Key)。
// 每个 Phase 1 请求生成一个 sessionPhase 2 消费后删除。
type SessionManager struct {
mu sync.Mutex
store map[string]*sessionEntry
ttl time.Duration
}
// NewSessionManager 创建会话密钥管理器。
// ttl: 密钥过期时间(如 30 秒)
func NewSessionManager(ttl time.Duration) *SessionManager {
sm := &SessionManager{
store: make(map[string]*sessionEntry),
ttl: ttl,
}
// 后台协程定期清理过期密钥
go sm.cleanupLoop()
return sm
}
// GenerateKey 生成 AES-256 会话密钥,用指定的 keyID 存储(通常用 nonce)。
func (sm *SessionManager) GenerateKey(keyID string) ([]byte, error) {
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
return nil, fmt.Errorf("generate session key: %w", err)
}
sm.mu.Lock()
sm.store[keyID] = &sessionEntry{
key: key,
expiry: time.Now().Add(sm.ttl),
}
sm.mu.Unlock()
return key, nil
}
// GetKey 获取并删除会话密钥(一次性使用)。
// 返回 nil 表示 keyID 不存在或已过期。
func (sm *SessionManager) GetKey(keyID string) []byte {
sm.mu.Lock()
defer sm.mu.Unlock()
entry, ok := sm.store[keyID]
if !ok {
return nil
}
delete(sm.store, keyID)
if time.Now().After(entry.expiry) {
return nil
}
return entry.key
}
// cleanupLoop 定期清理过期密钥。
func (sm *SessionManager) cleanupLoop() {
ticker := time.NewTicker(sm.ttl)
defer ticker.Stop()
for range ticker.C {
sm.mu.Lock()
now := time.Now()
for id, entry := range sm.store {
if now.After(entry.expiry) {
delete(sm.store, id)
}
}
sm.mu.Unlock()
}
}
// NonceCache 用于防重放攻击的 Nonce 缓存。
type NonceCache struct {
mu sync.Mutex
store map[string]time.Time
ttl time.Duration
}
// NewNonceCache 创建 Nonce 缓存。
// ttl: Nonce 有效时间(如 60 秒)
func NewNonceCache(ttl time.Duration) *NonceCache {
nc := &NonceCache{
store: make(map[string]time.Time),
ttl: ttl,
}
go nc.cleanupLoop()
return nc
}
// Check 检查并记录 Nonce。返回 true 表示 Nonce 有效(未使用过)。
// 返回 false 表示 Nonce 已存在(重放攻击)。
func (nc *NonceCache) Check(nonce string) bool {
nc.mu.Lock()
defer nc.mu.Unlock()
if _, exists := nc.store[nonce]; exists {
return false
}
nc.store[nonce] = time.Now().Add(nc.ttl)
return true
}
func (nc *NonceCache) cleanupLoop() {
ticker := time.NewTicker(nc.ttl)
defer ticker.Stop()
for range ticker.C {
nc.mu.Lock()
now := time.Now()
for n, expiry := range nc.store {
if now.After(expiry) {
delete(nc.store, n)
}
}
nc.mu.Unlock()
}
}