Skip to content

S3 Encryption Proxy

The CloudTaser S3 proxy provides transparent client-side encryption for S3-compatible object storage. It runs as a sidecar container, intercepting S3 API calls to encrypt objects on upload and decrypt them on download. AWS (or any S3-compatible provider) only ever stores ciphertext.

The S3 proxy ensures that all data stored in S3 is encrypted with keys held exclusively in the EU-hosted OpenBao/Vault Transit engine. Even if AWS receives a CLOUD Act warrant, the stored objects are ciphertext that cannot be decrypted without the EU-hosted key encryption key (KEK).


How It Works

The proxy sits between the application and the S3 endpoint as a reverse proxy:

Application (localhost:8190) -> CloudTaser S3 Proxy -> S3 Endpoint (e.g., s3.eu-west-1.amazonaws.com)
  1. Application sends S3 requests to localhost:8190
  2. On PutObject: proxy generates a random DEK, encrypts the object body with AES-256-GCM, wraps the DEK via Vault Transit, stores the wrapped DEK and nonce as S3 object metadata
  3. On GetObject: proxy fetches the object from S3, reads the wrapped DEK from metadata, unwraps it via Vault Transit, decrypts the body, returns plaintext to the application
  4. On HeadObject: proxy passes through and returns metadata (including encryption metadata headers)
  5. Non-encryption operations (ListObjects, DeleteObject, etc.) pass through unmodified
  6. All upstream requests are re-signed with SigV4 using the pod's AWS credentials (IRSA)

Supported Operations

Operation Encryption Behavior
PutObject Encrypt body, wrap DEK, add metadata headers
GetObject Fetch from S3, unwrap DEK, decrypt body
HeadObject Pass through (returns metadata including encryption headers)
UploadPart (multipart) Encrypt each part independently
CreateMultipartUpload Pass through
CompleteMultipartUpload Pass through
AbortMultipartUpload Pass through
ListObjectsV2 Pass through
DeleteObject Pass through
All other operations Pass through

Encryption Protocol

  • Algorithm: AES-256-GCM
  • Per-object random DEK (32 bytes) and nonce (12 bytes)
  • DEK wrapped by Vault Transit engine -- the KEK never leaves the EU vault
  • Metadata storage: Encrypted DEK, nonce, and plaintext size stored as S3 object metadata headers (x-amz-meta-cloudtaser-*)
  • Multipart uploads: Each part is encrypted independently with its own DEK and nonce

Configuration

The proxy is configured via environment variables. When deployed as a sidecar by the CloudTaser operator, these are set automatically from pod annotations.

Required Environment Variables

Variable Description Default
CLOUDTASER_S3PROXY_S3_ENDPOINT Upstream S3 endpoint URL -- (required)
CLOUDTASER_S3PROXY_S3_REGION AWS region -- (required)
VAULT_ADDR OpenBao/Vault address -- (required)
CLOUDTASER_S3PROXY_TRANSIT_KEY Vault Transit engine key name -- (required)

Optional Environment Variables

Variable Description Default
CLOUDTASER_S3PROXY_LISTEN_ADDR Proxy listen address :8190
CLOUDTASER_S3PROXY_TRANSIT_MOUNT Vault Transit engine mount path transit
CLOUDTASER_S3PROXY_HEALTH_ADDR Health check listen address :8191
CLOUDTASER_S3PROXY_MAX_OBJECT_SIZE Maximum object size for single PutObject (bytes) 268435456 (256 MiB)
VAULT_TOKEN Vault token (development/testing only) --
VAULT_AUTH_METHOD Auth method: kubernetes or token kubernetes
VAULT_AUTH_ROLE Kubernetes auth role name --
VAULT_AUTH_MOUNT_PATH Auth mount path kubernetes
VAULT_SKIP_VERIFY Skip TLS verification for Vault (development only) false

Deployment via Operator

The operator injects the S3 proxy sidecar when the cloudtaser.io/s3-proxy: "true" annotation is set on the pod. The proxy listens on port 8190 with a health check on port 8191.

Pod Annotations

Annotation Description Required
cloudtaser.io/s3-proxy Set to "true" to enable S3 proxy sidecar Yes
cloudtaser.io/s3-proxy-endpoint Upstream S3 endpoint URL Yes
cloudtaser.io/s3-proxy-region AWS region Yes
cloudtaser.io/s3-proxy-transit-key Vault Transit key name Yes
cloudtaser.io/s3-proxy-transit-mount Vault Transit mount path No (default: transit)

Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    metadata:
      annotations:
        cloudtaser.io/inject: "true"
        cloudtaser.io/vault-address: "https://vault.eu.example.com:8200"
        cloudtaser.io/vault-role: "myapp"
        cloudtaser.io/secret-paths: "secret/data/myapp/config"
        cloudtaser.io/env-map: "api_key=API_KEY"
        cloudtaser.io/s3-proxy: "true"
        cloudtaser.io/s3-proxy-endpoint: "https://s3.eu-west-1.amazonaws.com"
        cloudtaser.io/s3-proxy-region: "eu-west-1"
        cloudtaser.io/s3-proxy-transit-key: "myapp-s3"
    spec:
      containers:
        - name: myapp
          env:
            - name: AWS_S3_ENDPOINT
              value: "http://localhost:8190"  # Point S3 client to the proxy

Vault Transit Setup

The proxy requires a Vault Transit engine with a key for DEK wrapping:

# Enable Transit engine (if not already enabled)
vault secrets enable transit

# Create a key for the S3 proxy
vault write -f transit/keys/myapp-s3 type=aes256-gcm96

# Grant the CloudTaser role access to the key
vault policy write cloudtaser-s3 - <<EOF
path "transit/encrypt/myapp-s3" {
  capabilities = ["update"]
}
path "transit/decrypt/myapp-s3" {
  capabilities = ["update"]
}
EOF

AWS Credentials

The proxy re-signs all requests with SigV4 before forwarding to the upstream S3 endpoint. AWS credentials must be available to the proxy. The recommended approach is IAM Roles for Service Accounts (IRSA):

serviceAccount:
  annotations:
    iam.gke.io/gcp-service-account: [email protected]
serviceAccount:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/myapp-s3
env:
  - name: AWS_ACCESS_KEY_ID
    value: "AKIA..."
  - name: AWS_SECRET_ACCESS_KEY
    value: "..."

See S3 Proxy Credentials for detailed setup instructions.

Validation

The proxy validates all required configuration at startup and exits with an error if any is missing:

  • S3Endpoint (CLOUDTASER_S3PROXY_S3_ENDPOINT)
  • S3Region (CLOUDTASER_S3PROXY_S3_REGION)
  • VaultAddr (VAULT_ADDR)
  • VaultTransitKey (CLOUDTASER_S3PROXY_TRANSIT_KEY)

The auth method must be "token" or "kubernetes". When using "token", VAULT_TOKEN is also required. MaxObjectSize must be positive.