snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
This commit is contained in:
568
weed/kms/local/local_kms.go
Normal file
568
weed/kms/local/local_kms.go
Normal file
@@ -0,0 +1,568 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/kms"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
// LocalKMSProvider implements a local, in-memory KMS for development and testing
|
||||
// WARNING: This is NOT suitable for production use - keys are stored in memory
|
||||
type LocalKMSProvider struct {
|
||||
mu sync.RWMutex
|
||||
keys map[string]*LocalKey
|
||||
defaultKeyID string
|
||||
enableOnDemandCreate bool // Whether to create keys on-demand for missing key IDs
|
||||
}
|
||||
|
||||
// LocalKey represents a key stored in the local KMS
|
||||
type LocalKey struct {
|
||||
KeyID string `json:"keyId"`
|
||||
ARN string `json:"arn"`
|
||||
Description string `json:"description"`
|
||||
KeyMaterial []byte `json:"keyMaterial"` // 256-bit master key
|
||||
KeyUsage kms.KeyUsage `json:"keyUsage"`
|
||||
KeyState kms.KeyState `json:"keyState"`
|
||||
Origin kms.KeyOrigin `json:"origin"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
// LocalKMSConfig contains configuration for the local KMS provider
|
||||
type LocalKMSConfig struct {
|
||||
DefaultKeyID string `json:"defaultKeyId"`
|
||||
Keys map[string]*LocalKey `json:"keys"`
|
||||
EnableOnDemandCreate bool `json:"enableOnDemandCreate"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the local KMS provider
|
||||
kms.RegisterProvider("local", NewLocalKMSProvider)
|
||||
}
|
||||
|
||||
// NewLocalKMSProvider creates a new local KMS provider
|
||||
func NewLocalKMSProvider(config util.Configuration) (kms.KMSProvider, error) {
|
||||
provider := &LocalKMSProvider{
|
||||
keys: make(map[string]*LocalKey),
|
||||
enableOnDemandCreate: true, // Default to true for development/testing convenience
|
||||
}
|
||||
|
||||
// Load configuration if provided
|
||||
if config != nil {
|
||||
if err := provider.loadConfig(config); err != nil {
|
||||
return nil, fmt.Errorf("failed to load local KMS config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a default key if none exists
|
||||
if len(provider.keys) == 0 {
|
||||
defaultKey, err := provider.createDefaultKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create default key: %v", err)
|
||||
}
|
||||
provider.defaultKeyID = defaultKey.KeyID
|
||||
glog.V(1).Infof("Local KMS: Created default key %s", defaultKey.KeyID)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// loadConfig loads configuration from the provided config
|
||||
func (p *LocalKMSProvider) loadConfig(config util.Configuration) error {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.enableOnDemandCreate = config.GetBool("enableOnDemandCreate")
|
||||
|
||||
// TODO: Load pre-existing keys from configuration if provided
|
||||
// For now, rely on default key creation in constructor
|
||||
|
||||
glog.V(2).Infof("Local KMS: enableOnDemandCreate = %v", p.enableOnDemandCreate)
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDefaultKey creates a default master key for the local KMS
|
||||
func (p *LocalKMSProvider) createDefaultKey() (*LocalKey, error) {
|
||||
keyID, err := generateKeyID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key ID: %w", err)
|
||||
}
|
||||
keyMaterial := make([]byte, 32) // 256-bit key
|
||||
if _, err := io.ReadFull(rand.Reader, keyMaterial); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key material: %w", err)
|
||||
}
|
||||
|
||||
key := &LocalKey{
|
||||
KeyID: keyID,
|
||||
ARN: fmt.Sprintf("arn:aws:kms:local:000000000000:key/%s", keyID),
|
||||
Description: "Default local KMS key for SeaweedFS",
|
||||
KeyMaterial: keyMaterial,
|
||||
KeyUsage: kms.KeyUsageEncryptDecrypt,
|
||||
KeyState: kms.KeyStateEnabled,
|
||||
Origin: kms.KeyOriginLocal,
|
||||
CreatedAt: time.Now(),
|
||||
Aliases: []string{"alias/seaweedfs-default"},
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.keys[keyID] = key
|
||||
|
||||
// Also register aliases
|
||||
for _, alias := range key.Aliases {
|
||||
p.keys[alias] = key
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GenerateDataKey implements the KMSProvider interface
|
||||
func (p *LocalKMSProvider) GenerateDataKey(ctx context.Context, req *kms.GenerateDataKeyRequest) (*kms.GenerateDataKeyResponse, error) {
|
||||
if req.KeySpec != kms.KeySpecAES256 {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeInvalidKeyUsage,
|
||||
Message: fmt.Sprintf("Unsupported key spec: %s", req.KeySpec),
|
||||
KeyID: req.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the key
|
||||
key, err := p.getKey(req.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key.KeyState != kms.KeyStateEnabled {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeKeyUnavailable,
|
||||
Message: fmt.Sprintf("Key %s is in state %s", key.KeyID, key.KeyState),
|
||||
KeyID: key.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a random 256-bit data key
|
||||
dataKey := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, dataKey); err != nil {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeKMSInternalFailure,
|
||||
Message: "Failed to generate data key",
|
||||
KeyID: key.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt the data key with the master key
|
||||
encryptedDataKey, err := p.encryptDataKey(dataKey, key, req.EncryptionContext)
|
||||
if err != nil {
|
||||
kms.ClearSensitiveData(dataKey)
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeKMSInternalFailure,
|
||||
Message: fmt.Sprintf("Failed to encrypt data key: %v", err),
|
||||
KeyID: key.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
return &kms.GenerateDataKeyResponse{
|
||||
KeyID: key.KeyID,
|
||||
Plaintext: dataKey,
|
||||
CiphertextBlob: encryptedDataKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Decrypt implements the KMSProvider interface
|
||||
func (p *LocalKMSProvider) Decrypt(ctx context.Context, req *kms.DecryptRequest) (*kms.DecryptResponse, error) {
|
||||
// Parse the encrypted data key to extract metadata
|
||||
metadata, err := p.parseEncryptedDataKey(req.CiphertextBlob)
|
||||
if err != nil {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeInvalidCiphertext,
|
||||
Message: fmt.Sprintf("Invalid ciphertext format: %v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Verify encryption context matches
|
||||
if !p.encryptionContextMatches(metadata.EncryptionContext, req.EncryptionContext) {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeInvalidCiphertext,
|
||||
Message: "Encryption context mismatch",
|
||||
KeyID: metadata.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the master key
|
||||
key, err := p.getKey(metadata.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key.KeyState != kms.KeyStateEnabled {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeKeyUnavailable,
|
||||
Message: fmt.Sprintf("Key %s is in state %s", key.KeyID, key.KeyState),
|
||||
KeyID: key.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt the data key
|
||||
dataKey, err := p.decryptDataKey(metadata, key)
|
||||
if err != nil {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeInvalidCiphertext,
|
||||
Message: fmt.Sprintf("Failed to decrypt data key: %v", err),
|
||||
KeyID: key.KeyID,
|
||||
}
|
||||
}
|
||||
|
||||
return &kms.DecryptResponse{
|
||||
KeyID: key.KeyID,
|
||||
Plaintext: dataKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DescribeKey implements the KMSProvider interface
|
||||
func (p *LocalKMSProvider) DescribeKey(ctx context.Context, req *kms.DescribeKeyRequest) (*kms.DescribeKeyResponse, error) {
|
||||
key, err := p.getKey(req.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &kms.DescribeKeyResponse{
|
||||
KeyID: key.KeyID,
|
||||
ARN: key.ARN,
|
||||
Description: key.Description,
|
||||
KeyUsage: key.KeyUsage,
|
||||
KeyState: key.KeyState,
|
||||
Origin: key.Origin,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetKeyID implements the KMSProvider interface
|
||||
func (p *LocalKMSProvider) GetKeyID(ctx context.Context, keyIdentifier string) (string, error) {
|
||||
key, err := p.getKey(keyIdentifier)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return key.KeyID, nil
|
||||
}
|
||||
|
||||
// Close implements the KMSProvider interface
|
||||
func (p *LocalKMSProvider) Close() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Clear all key material from memory
|
||||
for _, key := range p.keys {
|
||||
kms.ClearSensitiveData(key.KeyMaterial)
|
||||
}
|
||||
p.keys = make(map[string]*LocalKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getKey retrieves a key by ID or alias, creating it on-demand if it doesn't exist
|
||||
func (p *LocalKMSProvider) getKey(keyIdentifier string) (*LocalKey, error) {
|
||||
p.mu.RLock()
|
||||
|
||||
// Try direct lookup first
|
||||
if key, exists := p.keys[keyIdentifier]; exists {
|
||||
p.mu.RUnlock()
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Try with default key if no identifier provided
|
||||
if keyIdentifier == "" && p.defaultKeyID != "" {
|
||||
if key, exists := p.keys[p.defaultKeyID]; exists {
|
||||
p.mu.RUnlock()
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
p.mu.RUnlock()
|
||||
|
||||
// Key doesn't exist - create on-demand if enabled and key identifier is reasonable
|
||||
if keyIdentifier != "" && p.enableOnDemandCreate && p.isReasonableKeyIdentifier(keyIdentifier) {
|
||||
glog.V(1).Infof("Creating on-demand local KMS key: %s", keyIdentifier)
|
||||
key, err := p.CreateKeyWithID(keyIdentifier, fmt.Sprintf("Auto-created local KMS key: %s", keyIdentifier))
|
||||
if err != nil {
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeKMSInternalFailure,
|
||||
Message: fmt.Sprintf("Failed to create on-demand key %s: %v", keyIdentifier, err),
|
||||
KeyID: keyIdentifier,
|
||||
}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, &kms.KMSError{
|
||||
Code: kms.ErrCodeNotFoundException,
|
||||
Message: fmt.Sprintf("Key not found: %s", keyIdentifier),
|
||||
KeyID: keyIdentifier,
|
||||
}
|
||||
}
|
||||
|
||||
// isReasonableKeyIdentifier determines if a key identifier is reasonable for on-demand creation
|
||||
func (p *LocalKMSProvider) isReasonableKeyIdentifier(keyIdentifier string) bool {
|
||||
// Basic validation: reasonable length and character set
|
||||
if len(keyIdentifier) < 3 || len(keyIdentifier) > 100 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Allow alphanumeric characters, hyphens, underscores, and forward slashes
|
||||
// This covers most reasonable key identifier formats without being overly restrictive
|
||||
for _, r := range keyIdentifier {
|
||||
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
|
||||
(r >= '0' && r <= '9') || r == '-' || r == '_' || r == '/') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Reject keys that start or end with separators
|
||||
if keyIdentifier[0] == '-' || keyIdentifier[0] == '_' || keyIdentifier[0] == '/' ||
|
||||
keyIdentifier[len(keyIdentifier)-1] == '-' || keyIdentifier[len(keyIdentifier)-1] == '_' || keyIdentifier[len(keyIdentifier)-1] == '/' {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// encryptedDataKeyMetadata represents the metadata stored with encrypted data keys
|
||||
type encryptedDataKeyMetadata struct {
|
||||
KeyID string `json:"keyId"`
|
||||
EncryptionContext map[string]string `json:"encryptionContext"`
|
||||
EncryptedData []byte `json:"encryptedData"`
|
||||
Nonce []byte `json:"nonce"` // Renamed from IV to be more explicit about AES-GCM usage
|
||||
}
|
||||
|
||||
// encryptDataKey encrypts a data key using the master key with AES-GCM for authenticated encryption
|
||||
func (p *LocalKMSProvider) encryptDataKey(dataKey []byte, masterKey *LocalKey, encryptionContext map[string]string) ([]byte, error) {
|
||||
block, err := aes.NewCipher(masterKey.KeyMaterial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate a random nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare additional authenticated data (AAD) from the encryption context
|
||||
// Use deterministic marshaling to ensure consistent AAD
|
||||
var aad []byte
|
||||
if len(encryptionContext) > 0 {
|
||||
var err error
|
||||
aad, err = marshalEncryptionContextDeterministic(encryptionContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal encryption context for AAD: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt using AES-GCM
|
||||
encryptedData := gcm.Seal(nil, nonce, dataKey, aad)
|
||||
|
||||
// Create metadata structure
|
||||
metadata := &encryptedDataKeyMetadata{
|
||||
KeyID: masterKey.KeyID,
|
||||
EncryptionContext: encryptionContext,
|
||||
EncryptedData: encryptedData,
|
||||
Nonce: nonce,
|
||||
}
|
||||
|
||||
// Serialize metadata to JSON
|
||||
return json.Marshal(metadata)
|
||||
}
|
||||
|
||||
// decryptDataKey decrypts a data key using the master key with AES-GCM for authenticated decryption
|
||||
func (p *LocalKMSProvider) decryptDataKey(metadata *encryptedDataKeyMetadata, masterKey *LocalKey) ([]byte, error) {
|
||||
block, err := aes.NewCipher(masterKey.KeyMaterial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare additional authenticated data (AAD)
|
||||
var aad []byte
|
||||
if len(metadata.EncryptionContext) > 0 {
|
||||
var err error
|
||||
aad, err = marshalEncryptionContextDeterministic(metadata.EncryptionContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal encryption context for AAD: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt using AES-GCM
|
||||
nonce := metadata.Nonce
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return nil, fmt.Errorf("invalid nonce size: expected %d, got %d", gcm.NonceSize(), len(nonce))
|
||||
}
|
||||
|
||||
dataKey, err := gcm.Open(nil, nonce, metadata.EncryptedData, aad)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt with GCM: %w", err)
|
||||
}
|
||||
|
||||
return dataKey, nil
|
||||
}
|
||||
|
||||
// parseEncryptedDataKey parses the encrypted data key blob
|
||||
func (p *LocalKMSProvider) parseEncryptedDataKey(ciphertextBlob []byte) (*encryptedDataKeyMetadata, error) {
|
||||
var metadata encryptedDataKeyMetadata
|
||||
if err := json.Unmarshal(ciphertextBlob, &metadata); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ciphertext blob: %v", err)
|
||||
}
|
||||
return &metadata, nil
|
||||
}
|
||||
|
||||
// encryptionContextMatches checks if two encryption contexts match
|
||||
func (p *LocalKMSProvider) encryptionContextMatches(ctx1, ctx2 map[string]string) bool {
|
||||
if len(ctx1) != len(ctx2) {
|
||||
return false
|
||||
}
|
||||
for k, v := range ctx1 {
|
||||
if ctx2[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// generateKeyID generates a random key ID
|
||||
func generateKeyID() (string, error) {
|
||||
// Generate a UUID-like key ID
|
||||
b := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes for key ID: %w", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]), nil
|
||||
}
|
||||
|
||||
// CreateKey creates a new key in the local KMS (for testing)
|
||||
func (p *LocalKMSProvider) CreateKey(description string, aliases []string) (*LocalKey, error) {
|
||||
keyID, err := generateKeyID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key ID: %w", err)
|
||||
}
|
||||
keyMaterial := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, keyMaterial); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := &LocalKey{
|
||||
KeyID: keyID,
|
||||
ARN: fmt.Sprintf("arn:aws:kms:local:000000000000:key/%s", keyID),
|
||||
Description: description,
|
||||
KeyMaterial: keyMaterial,
|
||||
KeyUsage: kms.KeyUsageEncryptDecrypt,
|
||||
KeyState: kms.KeyStateEnabled,
|
||||
Origin: kms.KeyOriginLocal,
|
||||
CreatedAt: time.Now(),
|
||||
Aliases: aliases,
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.keys[keyID] = key
|
||||
for _, alias := range aliases {
|
||||
// Ensure alias has proper format
|
||||
if !strings.HasPrefix(alias, "alias/") {
|
||||
alias = "alias/" + alias
|
||||
}
|
||||
p.keys[alias] = key
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// CreateKeyWithID creates a key with a specific keyID (for testing only)
|
||||
func (p *LocalKMSProvider) CreateKeyWithID(keyID, description string) (*LocalKey, error) {
|
||||
keyMaterial := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, keyMaterial); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key material: %w", err)
|
||||
}
|
||||
|
||||
key := &LocalKey{
|
||||
KeyID: keyID,
|
||||
ARN: fmt.Sprintf("arn:aws:kms:local:000000000000:key/%s", keyID),
|
||||
Description: description,
|
||||
KeyMaterial: keyMaterial,
|
||||
KeyUsage: kms.KeyUsageEncryptDecrypt,
|
||||
KeyState: kms.KeyStateEnabled,
|
||||
Origin: kms.KeyOriginLocal,
|
||||
CreatedAt: time.Now(),
|
||||
Aliases: []string{}, // No aliases by default
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Register key with the exact keyID provided
|
||||
p.keys[keyID] = key
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// marshalEncryptionContextDeterministic creates a deterministic byte representation of encryption context
|
||||
// This ensures that the same encryption context always produces the same AAD for AES-GCM
|
||||
func marshalEncryptionContextDeterministic(encryptionContext map[string]string) ([]byte, error) {
|
||||
if len(encryptionContext) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Sort keys to ensure deterministic output
|
||||
keys := make([]string, 0, len(encryptionContext))
|
||||
for k := range encryptionContext {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// Build deterministic representation with proper JSON escaping
|
||||
var buf strings.Builder
|
||||
buf.WriteString("{")
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
// Marshal key and value to get proper JSON string escaping
|
||||
keyBytes, err := json.Marshal(k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal encryption context key '%s': %w", k, err)
|
||||
}
|
||||
valueBytes, err := json.Marshal(encryptionContext[k])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal encryption context value for key '%s': %w", k, err)
|
||||
}
|
||||
buf.Write(keyBytes)
|
||||
buf.WriteString(":")
|
||||
buf.Write(valueBytes)
|
||||
}
|
||||
buf.WriteString("}")
|
||||
|
||||
return []byte(buf.String()), nil
|
||||
}
|
||||
Reference in New Issue
Block a user