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. With this separation, multiple master keys from different backends can 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, and more) 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. This makes 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. This enables 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, and more).

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


Security

Threat Model

SOPS operates at the file level within version-controlled repositories. Its security model depends on the strength of the master key backends, the integrity of the .sops.yaml configuration, and the safety of the encrypted data in transit and at rest.

Threat Vector Impact Mitigation
Compromised master key Decryption of all files encrypted with that key Key groups for quorum, key rotation
Tampered encrypted file Potential data corruption or injection HMAC-SHA256 MAC verification
Leaked .sops.yaml with key ARNs Reveals encryption key locations ARNs are not secrets. Access is gated by IAM/auth
Stale keys in metadata Former team members retain decryption ability Regular updatekeys and key rotation
CI/CD credential leak Decryptor access in build pipelines Short-lived tokens, scoped IAM roles
Binary file substitution Swapping encrypted blobs MAC covers entire document tree

Key Management Best Practices

age vs PGP

Criterion age PGP
Key format Single X25519 key pair Complex keyring with subkeys
Key management One file per key Keyserver sync, trust db, keyring
Cryptographic agility Modern (X25519, ChaCha20-Poly1305) RSA or Curve25519. Legacy defaults
Attack surface Minimal binary, no keyring Large attack surface (gpg agent, keyserver)
Key rotation Replace key file, run updatekeys Subkey rotation, keyring management
Recommendation Preferred for new deployments Legacy only. Migrate when possible

Recommendation

Use age for all new SOPS deployments. It provides simpler key management, a smaller attack surface, and modern cryptography. Retain PGP only for backward compatibility with existing encrypted files.

Key Rotation Strategy

  1. Master key rotation -- Generate new age keys or KMS keys. Run sops updatekeys <file> to add the new keys to existing encrypted files while preserving the current data key.
  2. Data key rotation -- Run sops rotate <file> to generate a new data key and re-encrypt all values. This is necessary after a suspected key compromise.
  3. Rotation cadence -- Rotate master keys annually or when team members leave. Rotate data keys on a shorter cadence for highly sensitive secrets.
# Add new age key to existing file (no re-encryption)
sops updatekeys secret.enc.yaml

# Full data key rotation (re-encrypts all values)
sops rotate --input-type yaml --output-type yaml secret.enc.yaml

Multi-Backend Redundancy

Always configure at least two master key backends for each file. The recommended pattern:

creation_rules:
  - path_regex: \.prod\.yaml$
    kms: arn:aws:kms:us-east-1:111122223333:key/prod-key
    age: >-
      age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c
    pgp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4

This gives the following: - AWS KMS -- Primary decryption path for CI/CD and automated systems. - age -- Offline decryption capability for disaster recovery. - PGP -- Additional offline recovery path.

If the AWS KMS key becomes unavailable (account suspension, region outage), decryption is still possible using the age or PGP keys.

.sops.yaml Configuration Security

The .sops.yaml file controls which keys encrypt which files. While the file itself does not contain secret material, its configuration has security implications.

Path Regex Accuracy

Make sure that path_regex patterns are precise to prevent mis-encryption:

# Good: precise pattern
creation_rules:
  - path_regex: ^secrets/prod/.*\.yaml$
    kms: arn:aws:kms:us-east-1:111122223333:key/prod-key

# Bad: overly broad pattern
creation_rules:
  - path_regex: .*\.yaml$
    kms: arn:aws:kms:us-east-1:111122223333:key/prod-key

An overly broad pattern can cause development files to be encrypted with production keys, or vice versa.

Key Groups for Separation of Duty

Key groups enforce that decryption requires at least one key from each group:

creation_rules:
  - path_regex: ^secrets/prod/.*\.yaml$
    key_groups:
      # Team A must participate
      - age:
          - age1_team_a_key
      # Team B must participate
      - age:
          - age1_team_b_key
      # Offline recovery key must exist
      - pgp:
          - FINGERPRINT_RECOVERY_KEY

With this configuration, no single team can decrypt production secrets alone.

File Permissions

# .sops.yaml should be readable by all team members but not writable casually
chmod 644 .sops.yaml

# Private age keys must be strictly protected
chmod 600 ~/.config/sops/age/keys.txt

CI/CD Integration Security

AWS KMS in CI/CD

Use IAM roles with minimal permissions for CI/CD pipelines:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt",
        "kms:DescribeKey",
        "kms:Encrypt"
      ],
      "Resource": ["arn:aws:kms:us-east-1:111122223333:key/prod-key"]
    }
  ]
}

KMS Encryption Context

SOPS supports AWS KMS encryption context, which binds the decryption to specific context values. Use this to prevent cross-environment decryption. SOPS automatically includes the file path as encryption context when configured.

age in CI/CD

Store the age private key as a CI/CD secret variable:

# GitHub Actions example
env:
  SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}

With the SOPS_AGE_KEY environment variable, SOPS can decrypt without writing the key to disk. This is safer than checking in the key file.

GPG Agent in CI/CD

PGP in CI/CD requires importing the private key into the GPG agent, which increases complexity and attack surface:

# Import private key (avoid this pattern when possible; prefer age)
echo "$SOPS_PGP_KEY" | gpg --import

Prefer age for CI/CD to prevent the complexity and security risks of GPG key management in ephemeral build environments.

MAC Verification

SOPS computes an HMAC-SHA256 Message Authentication Code over the encrypted document structure. This provides:

  • Integrity verification -- Any modification to the encrypted values, key names, or document structure is detected.
  • Tampering detection -- Swapping values between keys or inserting new encrypted values invalidates the MAC.
  • Rejection on failure -- SOPS refuses to decrypt if MAC verification fails.

The MAC key is derived from the data key and is stored alongside the encrypted data keys in the sops metadata block.

Partial Encryption Security

SOPS supports encrypting only specific parts of a file using encrypted_regex or encrypted_suffix:

# .sops.yaml
creation_rules:
  - path_regex: .*\.yaml$
    encrypted_regex: "^(password|secret|api_key|token)$"

Partial Encryption Risks

When using partial encryption, make sure that the unencrypted fields do not contain sensitive information. The MAC only covers encrypted values, so modifications to plaintext fields are not detected. Review which fields are excluded from encryption carefully.

Auditing

SOPS does not include a built-in audit log, but auditing is achievable through the master key backends and file metadata:

Key Usage Auditing

Each encrypted file contains a sops metadata block listing every master key and its encrypted data key fragment. Inspect it to identify which backends and key IDs protect a file:

# View the sops metadata block (shows all master key references)
sops --decrypt --extract '["sops"]' secret.enc.yaml

Backend-Level Audit Trails

Backend Audit Mechanism
AWS KMS CloudTrail logs all Decrypt and GenerateDataKey API calls with key ARN, user identity, and timestamp
GCP KMS Cloud Audit Logs for cryptoKeys.decrypt and cryptoKeys.encrypt
Azure Key Vault Azure Monitor activity log for key operations
age / PGP No server-side audit trail -- audit at the file-access level (git log, filesystem ACLs)

Key Hygiene Auditing

# List all keys referenced across encrypted files
grep -r '"sops":' --include="*.enc.*" -A 20 | grep -E '(arn|age|fp):'

# Identify files with stale or deprecated keys
sops updatekeys --show-diff secret.enc.yaml

Regularly review CloudTrail and Cloud Audit Logs for unauthorized decryption attempts, and run sops updatekeys on a schedule to remove stale master keys from encrypted file metadata.