Skip to content

Lesson 3: Secrets Scanning (TruffleHog) & External Secrets Operator (ESO)

🧠 The Concept (Explain Like I'm 5)

  • The Mistake: A developer commits an AWS API key or Stripe secret to a GitHub repository. Even if they delete it in the next commit, the key stays in Git history forever, where automated hacker bots scrape it in 4 seconds.
  • TruffleHog (The Pre-Flight Metal Detector): A pre-commit hook that scans every line of code on your laptop before git commit allows it to leave your machine.
  • External Secrets Operator (The Armored Vault Courier): Instead of storing plain base64 secrets in Kubernetes YAML, you store the secret safely in AWS Secrets Manager. The External Secrets Operator (ESO) acts as an armored courier that fetches the secret and creates a native Kubernetes Secret inside the cluster in memory.

🏒 The Enterprise Context

  • Shifting Secrets Out of Git: Secrets should never exist in Git, even in encrypted form (e.g. sealed secrets) if rotation is required. AWS Secrets Manager allows automatic 30-day rotation of database passwords.
  • ESO vs Direct SDK Integration: If microservices call AWS Secrets Manager directly via AWS SDKs, every replica incurs AWS API calls, rate limits, and latency on startup. ESO caches secrets natively in Kubernetes etcd and re-syncs on a configurable schedule (e.g. refreshInterval: 1h).
  • Kubernetes Admission Webhooks (Mutating vs Validating):
  • Mutating Webhooks: Change the pod spec before saving (e.g. injecting the Vault Agent sidecar or adding default resource limits).
  • Validating Webhooks: Inspect the pod spec and answer Yes or No (e.g. Kyverno blocking unsigned images or images with tag :latest).

πŸ—ΊοΈ Visual Architecture: External Secrets Operator (ESO) Synchronization

sequenceDiagram
    autonumber
    actor SecurityAdmin as πŸ” Security Engineer
    participant AWS_SM as ☁️ AWS Secrets Manager
    participant ESO as πŸ€– External Secrets Operator (ESO)
    participant K8sSecret as ☸️ Native Kubernetes Secret
    participant Pod as πŸ“¦ Workload Application Pod

    SecurityAdmin->>AWS_SM: Stores / Rotates Database Password
    Note over ESO: Reconcile Loop (refreshInterval: 1h)
    ESO->>AWS_SM: GetSecretValue(SecretId="prod/payment/db") via IRSA
    AWS_SM-->>ESO: Returns JSON Payload {username: "app", password: "xyz"}
    ESO->>K8sSecret: Creates/Updates Secret "payment-db-secret" in etcd
    K8sSecret->>Pod: Injected as environment variable or mounted file!

πŸ’» Production Code: External Secrets Operator Manifests

1. The SecretStore (AWS Secrets Manager Provider via IRSA)

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-manager-store
  namespace: workloads
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-irsa-service-account # Authenticates via AWS IAM OIDC

2. The ExternalSecret (Declarative Secret Mapping)

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-database-credentials
  namespace: workloads
spec:
  refreshInterval: 1h # Automatically re-sync rotated passwords every hour
  secretStoreRef:
    name: aws-secrets-manager-store
    kind: SecretStore
  target:
    name: payment-db-secret # Name of native K8s Secret to create
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: prod/payment/database
        property: password
    - secretKey: DB_USER
      remoteRef:
        key: prod/payment/database
        property: username

πŸ›‘οΈ Pre-Commit Hook: Blocking Secrets with TruffleHog

Install and configure a .pre-commit-config.yaml file in every corporate repository:

repos:
  - repo: https://github.com/trufflesecurity/trufflehog
    rev: v3.63.0
    hooks:
      - id: trufflehog
        name: TruffleHog Secret Scanner
        entry: trufflehog git file://. --since-commit HEAD --only-verified --fail
        stages: [commit]
# Install pre-commit binary and activate hook
pip install pre-commit
pre-commit install

# Test scan against entire repository history
trufflehog git file://. --only-verified