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
164 lines
5.9 KiB
Go
164 lines
5.9 KiB
Go
package s3api
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync/atomic"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
)
|
|
|
|
// RequireKeyCommitmentEnv is the environment variable that flips the
|
|
// commitment check from "skip when missing" (the AWS-compatible default,
|
|
// needed for objects written before commitments shipped) to "reject when
|
|
// missing". Operators who have either re-encrypted all legacy objects or
|
|
// who never wrote any objects under the pre-commitment code path can opt
|
|
// in via this env var to close the silent downgrade vector that an
|
|
// attacker with write access to object metadata could otherwise exploit
|
|
// by stripping the commitment field.
|
|
const RequireKeyCommitmentEnv = "WEED_S3_REQUIRE_KEY_COMMITMENT"
|
|
|
|
// requireKeyCommitment is the runtime mirror of the env var, kept as an
|
|
// atomic so config-reload paths can flip it without a global mutex.
|
|
var requireKeyCommitment atomic.Bool
|
|
|
|
func init() {
|
|
if v := os.Getenv(RequireKeyCommitmentEnv); v == "1" || strings.EqualFold(v, "true") {
|
|
requireKeyCommitment.Store(true)
|
|
glog.V(1).Infof("SSE: %s=true; SSE objects without a key commitment will be rejected", RequireKeyCommitmentEnv)
|
|
}
|
|
}
|
|
|
|
// SetRequireKeyCommitment toggles strict-commitment enforcement at runtime.
|
|
// Used by tests and by future config-reload code paths.
|
|
func SetRequireKeyCommitment(require bool) {
|
|
requireKeyCommitment.Store(require)
|
|
}
|
|
|
|
// ComputeKeyCommitment computes an HMAC-SHA256 key commitment over the
|
|
// encryption parameters (IV + algorithm). This binds the ciphertext to the
|
|
// exact key material and IV that were used, preventing key-confusion and
|
|
// IV-manipulation attacks against unauthenticated AES-CTR.
|
|
//
|
|
// The commitment is stored alongside the IV in object metadata. On decrypt
|
|
// the commitment is re-derived and compared; a mismatch means the key or IV
|
|
// was tampered with.
|
|
func ComputeKeyCommitment(key []byte, iv []byte, algorithm string) []byte {
|
|
mac := hmac.New(sha256.New, key)
|
|
mac.Write(iv)
|
|
mac.Write([]byte(algorithm))
|
|
return mac.Sum(nil)
|
|
}
|
|
|
|
// VerifyKeyCommitment checks a previously stored commitment against the
|
|
// current key, IV, and algorithm. Returns nil on success.
|
|
//
|
|
// When the commitment is empty (legacy object written before commitments
|
|
// shipped), the default behaviour is to accept the object — this is the
|
|
// AWS-compatible path. Setting WEED_S3_REQUIRE_KEY_COMMITMENT=true (via
|
|
// env at startup or via SetRequireKeyCommitment at runtime) flips that
|
|
// to reject, closing the silent-downgrade vector at the cost of locking
|
|
// out un-migrated legacy objects.
|
|
func VerifyKeyCommitment(key []byte, iv []byte, algorithm string, commitment []byte) error {
|
|
if len(commitment) == 0 {
|
|
if requireKeyCommitment.Load() {
|
|
return fmt.Errorf("key commitment is required but missing from object metadata: %s set; legacy objects must be re-encrypted before this flag can be enabled", RequireKeyCommitmentEnv)
|
|
}
|
|
// Legacy data written before key commitments were added; skip.
|
|
return nil
|
|
}
|
|
expected := ComputeKeyCommitment(key, iv, algorithm)
|
|
if !hmac.Equal(expected, commitment) {
|
|
return fmt.Errorf("key commitment verification failed: encryption parameters may have been tampered with")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// isValidKMSKeyID performs basic validation of KMS key identifiers.
|
|
// Following Minio's approach: be permissive and accept any reasonable key format.
|
|
// Only reject keys with leading/trailing spaces or other obvious issues.
|
|
//
|
|
// This function is used across multiple S3 API handlers to ensure consistent
|
|
// validation of KMS key IDs in various contexts (bucket encryption, object operations, etc.).
|
|
func isValidKMSKeyID(keyID string) bool {
|
|
// Reject empty keys
|
|
if keyID == "" {
|
|
return false
|
|
}
|
|
|
|
// Following Minio's validation: reject keys with leading/trailing spaces
|
|
if strings.HasPrefix(keyID, " ") || strings.HasSuffix(keyID, " ") {
|
|
return false
|
|
}
|
|
|
|
// Also reject keys with internal spaces (common sense validation)
|
|
if strings.Contains(keyID, " ") {
|
|
return false
|
|
}
|
|
|
|
// Reject keys with control characters or newlines
|
|
if strings.ContainsAny(keyID, "\t\n\r\x00") {
|
|
return false
|
|
}
|
|
|
|
// Accept any reasonable length key (be permissive for various KMS providers)
|
|
if len(keyID) > 0 && len(keyID) <= s3_constants.MaxKMSKeyIDLength {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// ValidateIV validates that an initialization vector has the correct length for AES encryption
|
|
func ValidateIV(iv []byte, name string) error {
|
|
if len(iv) != s3_constants.AESBlockSize {
|
|
return fmt.Errorf("invalid %s length: expected %d bytes, got %d", name, s3_constants.AESBlockSize, len(iv))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateSSEKMSKey validates that an SSE-KMS key is not nil and has required fields
|
|
func ValidateSSEKMSKey(sseKey *SSEKMSKey) error {
|
|
if sseKey == nil {
|
|
return fmt.Errorf("SSE-KMS key cannot be nil")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateSSES3Key validates that an SSE-S3 key has valid structure and contents
|
|
func ValidateSSES3Key(sseKey *SSES3Key) error {
|
|
if sseKey == nil {
|
|
return fmt.Errorf("SSE-S3 key cannot be nil")
|
|
}
|
|
|
|
// Validate key bytes
|
|
if sseKey.Key == nil {
|
|
return fmt.Errorf("SSE-S3 key bytes cannot be nil")
|
|
}
|
|
if len(sseKey.Key) != SSES3KeySize {
|
|
return fmt.Errorf("invalid SSE-S3 key size: expected %d bytes, got %d", SSES3KeySize, len(sseKey.Key))
|
|
}
|
|
|
|
// Validate algorithm
|
|
if sseKey.Algorithm != SSES3Algorithm {
|
|
return fmt.Errorf("invalid SSE-S3 algorithm: expected %q, got %q", SSES3Algorithm, sseKey.Algorithm)
|
|
}
|
|
|
|
// Validate key ID (should not be empty)
|
|
if sseKey.KeyID == "" {
|
|
return fmt.Errorf("SSE-S3 key ID cannot be empty")
|
|
}
|
|
|
|
// IV validation is optional during key creation - it will be set during encryption
|
|
// If IV is set, validate its length
|
|
if len(sseKey.IV) > 0 && len(sseKey.IV) != s3_constants.AESBlockSize {
|
|
return fmt.Errorf("invalid SSE-S3 IV length: expected %d bytes, got %d", s3_constants.AESBlockSize, len(sseKey.IV))
|
|
}
|
|
|
|
return nil
|
|
}
|