Skip to content

Architecture

Overview

SOPS (Secrets OPerationS) is a file-level encryption tool that encrypts the values of structured data files while leaving the keys in plaintext. It supports YAML, JSON, ENV, INI, and BINARY formats and encrypts with multiple backends: AWS KMS, GCP KMS, Azure Key Vault, age, and PGP. SOPS is designed for managing encrypted secrets in version-controlled repositories.

Encryption Model

SOPS uses a two-tier encryption architecture: a randomly generated data key encrypts the file content, and one or more master keys encrypt the data key. This separation allows multiple master keys from different backends to protect the same file without duplicating the data encryption work.

graph TD
    subgraph "Master Keys (Envelope Encryption)"
        KMS["AWS KMS Key"]
        GKMS["GCP KMS Key"]
        AKV["Azure Key Vault Key"]
        AGE["age Public Key"]
        PGP["PGP Public Key"]
        HV["HashiCorp Vault Key"]
    end

    DK["Data Key (256-bit random)"]
    FILE["Encrypted File (YAML/JSON/ENV/INI/BINARY)"]
    META["sops.metadata block"]

    KMS -->|encrypts data key| DK
    GKMS -->|encrypts data key| DK
    AKV -->|encrypts data key| DK
    AGE -->|encrypts data key| DK
    PGP -->|encrypts data key| DK
    HV -->|encrypts data key| DK

    DK -->|encrypts values| FILE
    DK -->|stored encrypted in| META

    META -->|sops.kms[]| KMS
    META -->|sops.pgp[]| PGP
    META -->|sops.age[]| AGE
    META -->|sops.azure_kv[]| AKV
    META -->|sops.gcp_kms[]| GKMS
    META -->|sops.hc_vault[]| HV

Key Hierarchy

graph BT
    subgraph "Decryption Path"
        MK1["Master Key 1<br/>(AWS KMS)"]
        MK2["Master Key 2<br/>(age)"]
        MK3["Master Key 3<br/>(PGP)"]
        EDK["Encrypted Data Keys<br/>(stored in sops metadata)"]
        DK["Data Key (256-bit AES)"]
        PLAIN["Plaintext Values"]
    end

    MK1 -->|decrypts| EDK
    MK2 -->|decrypts| EDK
    MK3 -->|decrypts| EDK
    EDK -->|yields| DK
    DK -->|AES-256-GCM decrypts| PLAIN

    style DK fill:#f9f,stroke:#333,stroke-width:2px
    style PLAIN fill:#bfb,stroke:#333

Encryption Flow

When SOPS creates a new encrypted file:

  1. Generate data key -- SOPS generates a random 256-bit data key using a cryptographically secure random number generator.
  2. Encrypt data key with each master key -- SOPS invokes each configured master key backend (KMS, age, PGP, etc.) to encrypt the data key. Each backend produces an encrypted copy of the data key.
  3. Encrypt individual values -- SOPS encrypts each leaf value in the document individually using AES-256-GCM with the data key. Each value gets a unique IV (initialization vector).
  4. Compute MAC -- SOPS computes a Message Authentication Code (HMAC-SHA256) over the encrypted values to detect tampering.
  5. Store metadata -- All encrypted data keys, IVs, MACs, and backend references are stored in the sops metadata block embedded in the file.
sequenceDiagram
    participant User
    participant SOPS as SOPS CLI
    participant DK as Data Key Generator
    participant KMS as AWS KMS
    participant AGE as age
    participant FS as File System

    User->>SOPS: sops encrypt --kms arn --age key file.yaml
    SOPS->>DK: Generate 256-bit random data key
    DK-->>SOPS: data_key (plaintext)
    SOPS->>KMS: Encrypt data_key with KMS key
    KMS-->>SOPS: encrypted_data_key_kms
    SOPS->>AGE: Encrypt data_key with age public key
    AGE-->>SOPS: encrypted_data_key_age
    SOPS->>SOPS: Encrypt each value with AES-256-GCM
    SOPS->>SOPS: Compute HMAC-SHA256 MAC
    SOPS->>FS: Write file with sops metadata block

Decryption Flow

  1. Read sops metadata -- SOPS parses the sops block from the encrypted file to discover available master keys.
  2. Attempt master key decryption -- SOPS iterates through the available backends, attempting to decrypt the data key. It tries each encrypted data key until one succeeds (using available credentials).
  3. Verify MAC -- Once the data key is recovered, SOPS recomputes the MAC and verifies it against the stored MAC to detect tampering.
  4. Decrypt values -- Each leaf value is decrypted using AES-256-GCM with the data key and its stored IV.
  5. Reconstruct document -- SOPS reassembles the plaintext document structure, replacing encrypted values with their decrypted content.

Supported Backends

Backend Key Type Use Case
AWS KMS Symmetric encryption key Teams using AWS; supports IAM roles, profiles, and encryption context
GCP KMS Symmetric encryption key Teams using GCP; supports service account authentication
Azure Key Vault RSA or AES key Teams using Azure; supports service principal authentication
age Public/private key pair (X25519) Local and CI/CD use; no cloud dependency, simple key management
PGP Public/private key pair Legacy deployments; complex keyring management
HashiCorp Vault Transit engine key Organizations already running Vault; centralized key management

Recommended Backend

age is the recommended backend for most use cases due to its simplicity, modern cryptography, and lack of keyring complexity. PGP is considered legacy. Cloud KMS backends are ideal when the organization already uses the respective cloud provider.

Key Groups

Key groups provide a quorum-based decryption policy. Instead of requiring any single master key, SOPS can be configured to require at least one master key from each group for decryption.

# .sops.yaml
creation_rules:
  - path_regex: .*keygroups.*
    key_groups:
      # Group 1: Cloud KMS keys
      - kms:
          - arn: arn:aws:kms:us-east-1:111122223333:key/dev-key
        pgp:
          - fingerprint1
      # Group 2: Offline recovery keys
      - pgp:
          - fingerprint3
          - fingerprint4
      # Group 3: age backup keys
      - age:
          - age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c

With this configuration, decryption requires at least one valid key from each of the three groups. This enforces geographic or organizational separation of trust.

.sops.yaml Configuration

The .sops.yaml file is a declarative configuration file placed at the root of a repository. It maps file path patterns to encryption keys, automating key selection when creating new encrypted files.

creation_rules:
  # Development files: use dev KMS key + age
  - path_regex: \.dev\.yaml$
    kms: arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500
    age: age129h70qwx39k7h5x6l9hg566nwm53527zvamre8vep9e3plsm44uqgy8gla

  # Production files: use prod KMS key + PGP + age
  - path_regex: \.prod\.yaml$
    kms: arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e
    pgp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    age: age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0sw

  # Catchall: use global KMS key + PGP
  - kms: arn:aws:kms:us-east-1:777788889999:key/global-key
    pgp: 3333CCCC

Rules are evaluated sequentially and the first match wins. SOPS supports matching by path_regex or exact name.

File Format Support

Format Extension Behavior
YAML .yaml, .yml Encrypts leaf values; keys and structure remain plaintext
JSON .json Encrypts leaf values; keys and structure remain plaintext
ENV .env Encrypts values; variable names remain plaintext
INI .ini Encrypts values; section headers and keys remain plaintext
BINARY Any Encrypts the entire file content as a blob

Value-Level Encryption

Unlike full-file encryption tools, SOPS encrypts individual values within structured files. This provides several advantages:

  • Diff-friendly -- Since keys are plaintext, git diff shows which keys changed, making secret rotation reviewable.
  • Partial decryption -- SOPS can extract or set individual values without decrypting the entire file.
  • Structured MAC -- Each value has its own authentication tag, enabling precise tampering detection.

Key Rotation

SOPS supports two key rotation mechanisms:

  • sops updatekeys -- Syncs the encrypted file with the current .sops.yaml creation rules. Adds new master keys and removes old ones while preserving the data key.
  • sops rotate -- Generates a new data key and re-encrypts all values. Old master keys are preserved for decryption continuity.

Rotation vs Updatekeys

updatekeys does not re-encrypt the data; it only updates which master keys can decrypt the existing data key. rotate re-encrypts everything with a new data key. Use rotate for true cryptographic rotation after a suspected key compromise.

MAC (Message Authentication Code)

SOPS computes an HMAC-SHA256 MAC over the encrypted document to detect tampering. The MAC covers: - All encrypted values. - The document tree structure. - The nonce/IV for each encrypted value.

If the MAC verification fails during decryption, SOPS refuses to proceed. This prevents an attacker from modifying encrypted content or swapping values between keys.


How It Works

Envelope encryption, value-only encryption model, and KMS integration.

Encryption Model

SOPS uses envelope encryption: a data encryption key (DEK) encrypts the file values, and the DEK itself is encrypted by the master key (age, KMS, etc.).

flowchart TB
    subgraph File["Encrypted File"]
        Meta["sops metadata\n(encrypted DEK, key fingerprints)"]
        Keys["YAML/JSON keys\n(plaintext)"]
        Values["Values\n(AES-256-GCM encrypted)"]
    end

    subgraph Master["Master Key Layer"]
        Age["age key"]
        KMS["AWS/GCP KMS"]
        VaultT["Vault Transit"]
    end

    Master -->|"encrypt DEK"| Meta
    Meta -->|"DEK decrypts"| Values

    style Values fill:#c62828,color:#fff
    style Keys fill:#2e7d32,color:#fff

What Gets Encrypted

# Before encryption
apiVersion: v1
kind: Secret
metadata:
  name: myapp          # ← NOT encrypted (structure visible)
data:
  username: admin      # ← value ENCRYPTED
  password: s3cr3t     # ← value ENCRYPTED

# After: sops --encrypt
apiVersion: v1
kind: Secret
metadata:
  name: myapp          # ← still plaintext (keys visible)
data:
  username: ENC[AES256_GCM,data:abc123...]  # ← encrypted
  password: ENC[AES256_GCM,data:xyz789...]  # ← encrypted
sops:
  age:
    - recipient: age1abc...
      enc: |
        -----BEGIN AGE ENCRYPTED FILE-----
        ...encrypted DEK...

Sources

Value-Level Encryption Detail

Each leaf value in the document is encrypted independently with AES-256-GCM:

  1. Per-value IV: A unique initialization vector (12 bytes) is generated for each value using a cryptographically secure RNG
  2. AES-GCM encrypt: The value is encrypted with the data key and its unique IV. GCM produces both ciphertext and a 16-byte authentication tag.
  3. Stored format: ENC[AES256_GCM,data:<base64-ciphertext>,iv:<base64-iv>,tag:<base64-authtag>]
  4. MAC covers structure: The HMAC-SHA256 MAC is computed over the entire document tree (keys, encrypted values, IVs, tags), not individual values

This per-value approach enables diff-friendly encryption: git diff shows which keys changed because the keys remain in plaintext.

Decryption Path

When decrypting, SOPS follows this sequence:

  1. Parse metadata: Read the sops block to discover available encrypted data keys
  2. Attempt decryption: Try each encrypted data key in order (KMS → age → PGP → Vault) until one succeeds using available credentials
  3. Verify MAC: Recompute HMAC-SHA256 over the encrypted document and compare against the stored MAC. Abort if mismatch.
  4. Decrypt values: For each ENC[...] value, decrypt using AES-256-GCM with the data key and the stored IV
  5. Reconstruct: Replace encrypted values with plaintext, remove the sops metadata block

Partial Encryption

SOPS supports encrypting only specific fields using regex or suffix matching:

# .sops.yaml - only encrypt fields matching the regex
creation_rules:
  - path_regex: .*\.yaml$
    encrypted_regex: "^(password|secret|api_key|token|private_key)$"

With partial encryption, unencrypted fields remain readable and diffable, while sensitive values are protected. The MAC only covers encrypted values, so modifications to plaintext fields are not detected by SOPS (use Git integrity for those fields instead).


Benchmarks

Scope

Performance characteristics, scaling limits, and resource consumption for SOPS.

Encryption Performance

File Size Encrypt Time Decrypt Time Notes
1KB (few secrets) < 100ms < 100ms Typical config file
10KB 100-200ms 100-200ms Large config
100KB 200-500ms 200-500ms Unusual for secrets

Key Provider Performance

Provider Encrypt Decrypt Notes
age < 50ms < 50ms Fastest, local-only
PGP 50-100ms 50-100ms Local key
AWS KMS 100-300ms 100-300ms Network roundtrip
GCP KMS 100-300ms 100-300ms Network roundtrip
Azure Key Vault 100-500ms 100-500ms Regional latency

GitOps Integration

Tool Decrypt Method Overhead
Flux sops-controller < 1s per secret
ArgoCD argocd-vault-plugin 1-3s per secret
Helm Secrets helm-secrets plugin < 1s per secret

Sourcing Status

Unsourced Performance Data

The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.

Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.

Sources