Files
rustfs/crates/e2e_test/src/kms
唐小鸭 e2e6a2535a fix(kms): classify KMS/SSE error contracts and SSE-S3 headers (#7697)
* fix(sse): classify bare SSE-KMS writes when no KMS is available

A `aws:kms` request without a key id, on a bucket without a default key,
returned `500 InternalError` whenever no KMS service was running: the
"no KMS key available" branch exited with an untyped storage error before
the availability classification that the keyed form already received.

Route that branch through the same split: `503 ServiceUnavailable` while
a configured KMS is stopped, `400 InvalidRequest` when KMS was never
configured, and `400 InvalidRequest` naming the missing key id when a
running KMS has no default key. `CreateMultipartUpload` shares the path.

Adds a unit test for the bare form and an e2e module that stops KMS
through the admin API, runs a master-key-only node, and runs a Local KMS
without a default key; refreshes the e2e-full selection digests.

(cherry picked from commit c3259dadc3d603a9185a5b0ad9f83dfb884e61c8)

* fix(sse): keep KMS error classes on the encrypted read path

GetObject, CopyObject and UploadPartCopy on an SSE-KMS object whose key
no longer exists answered `500 InternalError` ("KMS key not found") while
PutObject under the same key already answered `400 KMS.NotFoundException`.
The read path carries its classification through ecstore's
`EncryptionResolutionErrorKind`, which had no kind for a missing key, a
denied KMS grant or a missing backend capability, so all three folded
onto `DecryptionFailed` and the S3 layer reported an internal fault.

Add `KeyNotFound`, `AccessDenied` and `NotImplemented` kinds, map them on
both sides of the boundary, and give an envelope the configured backend
cannot unwrap a diagnosable message while keeping its `500`.

Unit tests cover the kind round trip and the reader wrapping; a new e2e
test deletes a key immediately and checks GET/Copy return 400 with
`KMS.NotFoundException` while HEAD stays 200. The e2e-full selection
digests are refreshed from the current listing (the previous digests
predated the delete-authorization tests) and the e2e `create_default_key`
helper is updated to the accepted `EncryptDecrypt` spelling.

(cherry picked from commit 2523a9814e97caea318d4ff1a51bef3a4d4445b2)

* fix(kms): classify key-management errors on the admin routes

`POST /kms/keys`, the legacy `create-key` alias and `generate-data-key`
reported every backend refusal as `500`: a blank key name (which each
backend failed on differently, the Local backend by writing a key file
with an empty stem), a name already taken, an unknown key, a disabled key
and a capability the backend lacks. `delete` and the lifecycle routes
already classified the same errors.

Refuse a blank or whitespace name in `KmsManager::create_key` before any
backend sees it, and share one `KmsError` to status mapping across
create, delete and generate-data-key (400 for validation and key state,
404 for an unknown key, 409 for a taken name, 501 for a missing
capability, 500 only for damaged material). The XML-error routes carry
the same status explicitly since s3s derives none for a custom code.

The read-only Static backend now reports create, delete and
cancel-deletion as `UnsupportedCapability`, matching its rotate and
enable/disable answers, so the admin API returns 501 for all of them.

(cherry picked from commit e33cac5493c4d9d6662e0d2980b58ba2b24a6d1b)

* fix(sse): stop SSE-S3 responses from naming the wrapping KMS key

`x-amz-server-side-encryption-aws-kms-key-id` is defined for `aws:kms`
objects only, but PutObject, CopyObject, CreateMultipartUpload and
GetObject returned it for `AES256` objects too, carrying the KMS key that
wraps the SSE-S3 data key (the service default, or the literal `default`
on a node without KMS). The write paths copied `kms_key_id` from the
encryption material unconditionally, and the single-decrypt GET
classification did the same after resolving the key for authorization.

Add `EncryptionMaterial::response_kms_key_id`, which yields the id only
for SSE-KMS, use it at the four write-response sites, and gate the GET
classification the same way. CompleteMultipartUpload and HeadObject
already omitted the header.

Unit tests pin both directions; a new e2e test covers Put/Get/Head/Copy
and CreateMultipartUpload for AES256 with an aws:kms control. The
e2e-full selection digests are refreshed from the current listing.

(cherry picked from commit 29d793a63352b0b60fd53c565e80fdbede8964bb)

* fix(s3): validate PutBucketEncryption rules before storing them

A default-encryption rule naming an unknown `SSEAlgorithm` (for example
`AES128`), a rule without `ApplyServerSideEncryptionByDefault`, an empty
rule list, or a `KMSMasterKeyID` on an `AES256` rule was stored as
written: the only algorithm check on the route decided whether to fill
in the default KMS key. `GetBucketEncryption` then advertised that
configuration while the write path encrypted header-less writes under
its `AES256` fallback, so the bucket's declared and actual schemes
disagreed. Two comments claimed the route already refused unknown
algorithms.

Validate the configuration before any of it is applied: `MalformedXML`
for a malformed rule set or unknown algorithm, `InvalidArgument` for a
key id on a non-KMS rule, and nothing stored on refusal. Correct the two
comments to describe when the AES256 fallback is still reachable.

Unit tests cover every refusal and the accepted shapes; an e2e test
checks the refusals leave the previous configuration in place. The
e2e-full selection digests are refreshed from the current listing.

(cherry picked from commit 29e4486dce41197ed93f5253cdbabc57d27a4ddb)

* test(e2e): refresh e2e-full selection for the combined KMS/SSE fixes

* test: align two unit tests with the new KMS and bucket-encryption contracts

`scheduled_deletion_carries_a_deadline_and_can_be_cancelled` still
expects the state error (`InvalidOperation`) for cancelling a key that
is not pending deletion; only the Static backend's mutations moved to
`UnsupportedCapability`. The uninitialized-store PutBucketEncryption
test now sends a well-formed AES256 rule so it reaches the store lookup
instead of the new configuration validation.
2026-09-12 23:48:10 +08:00
..

KMS End-to-End Tests

This directory contains the integration suites used to validate the full RustFS KMS (Key Management Service) workflow.

📁 Test Overview

kms_local_test.rs

End-to-end coverage for the local KMS backend:

  • Auto-start and configure the local backend
  • Configure KMS through the dynamic configuration API
  • Verify SSE-C (client-provided keys)
  • Exercise S3-compatible encryption/decryption
  • Validate key lifecycle management

kms_vault_test.rs

End-to-end coverage for the Vault backend:

  • Launch a Vault dev server automatically
  • Configure the transit engine and encryption keys
  • Configure KMS via the dynamic configuration API
  • Run the full Vault integration flow
  • Validate token authentication and encryption operations

kms_comprehensive_test.rs

Full KMS capability suite (currently disabled because of AWS SDK compatibility issues):

  • Bucket encryption configuration: SSE-S3 and SSE-KMS defaults
  • All SSE encryption modes:
    • SSE-S3 (S3-managed server-side encryption)
    • SSE-KMS (KMS-managed server-side encryption)
    • SSE-C (client-provided keys)
  • Object operations: upload, download, and validation for every SSE mode
  • Multipart uploads: cover each SSE mode
  • Object replication: cross-mode replication scenarios
  • Complete KMS API management:
    • Key lifecycle (create, list, describe, delete, cancel delete)
    • Direct encrypt/decrypt operations
    • Data key generation and handling
    • KMS service lifecycle (start, stop, status)

kms_integration_test.rs

Broad integration tests that exercise:

  • Multiple backends
  • KMS lifecycle management
  • Error handling and recovery
  • Note: currently disabled because of AWS SDK compatibility gaps

🚀 Running Tests

Prerequisites

  1. System dependencies

    # macOS
    brew install vault awscurl
    
    # Ubuntu/Debian
    apt-get install vault
    pip install awscurl
    
  2. Build RustFS (from the repository root)

    python3 scripts/e2e_binary.py build
    

Run individual suites

Run every command below from the repository root through scripts/e2e_binary.py run; plain cargo test -p e2e_test fails with a missing E2E run receipt.

Local backend

python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_local_kms_end_to_end -- --nocapture

Vault backend

python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_vault_kms_end_to_end -- --nocapture

High availability

python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_vault_kms_high_availability -- --nocapture

Comprehensive features (disabled)

# Disabled due to AWS SDK compatibility gaps
# python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_comprehensive_kms_functionality -- --nocapture
# python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_sse_modes_compatibility -- --nocapture
# python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_kms_api_comprehensive -- --nocapture

Run all KMS suites

python3 scripts/e2e_binary.py run -- cargo test -p e2e_test kms -- --nocapture

Run serially (avoid port conflicts)

python3 scripts/e2e_binary.py run -- cargo test -p e2e_test kms -- --nocapture --test-threads=1

🔧 Configuration

Environment variables

# Optional: custom RustFS port (default 9050)
export RUSTFS_TEST_PORT=9050

# Optional: custom Vault port (default 8200)
export VAULT_TEST_PORT=8200

# Optional: enable verbose logging
export RUST_LOG=debug

Required binaries

Tests look for:

  • RustFS server – the binary verified by scripts/e2e_binary.py (default target/debug/rustfs)
  • vault – Vault CLI (must be on PATH)
  • /Users/dandan/Library/Python/3.9/bin/awscurl – AWS SigV4 helper

📋 Test Flow

Local backend

  1. Prepare environment – create temporary directories and key storage paths
  2. Start RustFS – launch the server with KMS enabled
  3. Wait for readiness – confirm the port listener and S3 API
  4. Configure KMS – send configuration via awscurl to the admin API
  5. Start KMS – activate the KMS service
  6. Exercise functionality
    • Create a test bucket
    • Run SSE-C encryption with client-provided keys
    • Validate encryption/decryption behavior
  7. Cleanup – stop processes and remove temporary files

Vault backend

  1. Launch Vault – start the dev-mode server
  2. Configure Vault
    • Enable the transit secrets engine
    • Create the rustfs-master-key
  3. Start RustFS – run the server with KMS enabled
  4. Configure KMS – point RustFS at Vault (address, token, transit config, key path)
  5. Exercise functionality – complete the encryption/decryption workflow
  6. Cleanup – stop all services

🛠️ Troubleshooting

Common issues

Q: RustFS server failed to become ready

lsof -i :9050
kill -9 <PID>  # Free the port if necessary

Q: Vault fails to start

which vault
vault version

Q: awscurl authentication fails

ls /Users/dandan/Library/Python/3.9/bin/awscurl
# Or install elsewhere
pip install awscurl
which awscurl  # Update the path in tests accordingly

Q: Tests time out

RUST_LOG=debug python3 scripts/e2e_binary.py run -- cargo test -p e2e_test test_local_kms_end_to_end -- --nocapture

Debug tips

  1. Enable verbose logs

    RUST_LOG=rustfs_kms=debug,rustfs=info python3 scripts/e2e_binary.py run -- cargo test -p e2e_test kms -- --nocapture
    
  2. Keep temporary files – comment out cleanup logic to inspect generated configs

  3. Pause execution – add std::thread::sleep for manual inspection during tests

  4. Monitor ports

    netstat -an | grep 9050
    curl http://127.0.0.1:9050/health/ready
    

📊 Coverage

Functional

  • ✅ Dynamic KMS configuration
  • ✅ Local and Vault backends
  • ✅ AWS S3-compatible encryption APIs
  • ✅ Key lifecycle management
  • ✅ Error handling and recovery paths
  • ✅ High-availability behavior

Encryption modes

  • ✅ SSE-C (customer-provided)
  • ✅ SSE-S3 (S3-managed)
  • ✅ SSE-KMS (KMS-managed)

S3 operations

  • ✅ Object upload/download (SSE-C)
  • 🚧 Multipart uploads (pending AWS SDK fixes)
  • 🚧 Object replication (pending AWS SDK fixes)
  • 🚧 Bucket encryption defaults (pending AWS SDK fixes)

KMS API

  • ✅ Basic key management (create/list)
  • 🚧 Full key lifecycle (pending AWS SDK fixes)
  • 🚧 Direct encrypt/decrypt (pending AWS SDK fixes)
  • 🚧 Data key operations (pending AWS SDK fixes)
  • ✅ Service lifecycle (configure/start/stop/status)

Authentication

  • ✅ Vault token auth
  • 🚧 Vault AppRole auth

🔄 CI Integration

Designed to run inside CI/CD pipelines:

- name: Run KMS E2E Tests
  run: |
    sudo apt-get update
    sudo apt-get install -y vault
    pip install awscurl

    python3 scripts/e2e_binary.py build
    python3 scripts/e2e_binary.py run -- cargo test -p e2e_test kms -- --nocapture --test-threads=1

📚 References


These suites ensure KMS stability and reliability, building confidence for production deployments.