SOPS e age nel 2026: Guida GitOps alla Gestione dei Secrets su Kubernetes

Guida pratica a SOPS e age per cifrare i secrets di Kubernetes in un flusso GitOps. Setup .sops.yaml multi-recipient, integrazione Flux CD e ArgoCD, chiavi hardware YubiKey e rotazione senza downtime.

SOPS + age Kubernetes: Guida GitOps 2026

Aggiornato: 13 agosto 2026

SOPS con age è oggi il modo più semplice e portabile per cifrare i secrets di Kubernetes prima di committarli in Git: il file YAML resta leggibile e diffabile, ma i valori sensibili sono protetti da envelope encryption AES-256-GCM, mentre le chiavi asimmetriche X25519 di age sostituiscono la complessità di GPG. In questa guida costruisco il flusso completo (installazione, .sops.yaml multi-recipient, integrazione con Flux e ArgoCD, YubiKey e rotazione), partendo dal threat model che dovrebbe tenerti sveglio la notte.

  • SOPS 3.9+ (progetto CNCF Sandbox dal 2023) cifra soltanto i valori nei file YAML/JSON/ENV, mantenendo le chiavi in chiaro per PR reviewabili e merge conflict gestibili.
  • age (X25519 + ChaCha20-Poly1305) è raccomandato dai maintainer SOPS al posto di GPG: chiavi di 60 caratteri, nessun keyserver, nessun agent stateful.
  • L'envelope encryption permette che più destinatari (fino a decine di chiavi age) decifrino lo stesso file senza ri-cifrare il payload quando si aggiunge o si rimuove un membro del team.
  • Flux CD supporta SOPS nativamente tramite il kustomize-controller; ArgoCD richiede il plugin argocd-vault-plugin o helm-secrets.
  • Il blast radius di un'age key compromessa si limita al perimetro dei file cui è destinataria: rotazione con sops updatekeys in secondi, senza toccare il payload cifrato.
  • YubiKey via age-plugin-yubikey sposta la chiave privata su hardware PIV: la chiave lascia il token solo per le operazioni di decrypt, mai in memoria persistente.

Il threat model: cosa rompe per primo

Ogni volta che progetto un sistema di secrets management chiedo la stessa cosa: quale singolo evento distrugge tutto? Con i Secrets Kubernetes nativi la risposta è semplice: chiunque legga etcd (o uno snapshot di backup non cifrato) legge le tue password in Base64, che non è cifratura. Con i secrets in chiaro nel repository Git la risposta è ancora più corta: un fork accidentale, un runner CI compromesso, un pull request pubblico partito per sbaglio.

SOPS con age difende una superficie specifica: i secrets nel repository e nella pipeline CI. Il file cifrato può stare in un repo pubblico senza esporre nulla; il diff mostra solo i valori cifrati che cambiano; anche un backup Git rubato non serve a nulla senza le chiavi private age dei destinatari. Il perimetro che non difende è altrettanto importante da capire: una volta che il payload arriva su Kubernetes come Secret nativo, torna in Base64 in etcd, quindi lo stack Kubernetes deve essere hardenato in modo indipendente (etcd cifrato at rest con KMS, RBAC stretto, audit log). Un tema che affronto nel dettaglio nella guida all'hardening dei servizi systemd: il principio "non fidarti mai di un plaintext che non hai messo tu" vale a ogni livello.

La regola operativa che uso: SOPS protegge il tragitto developer → Git → CI → cluster. Cosa succede dentro il cluster è problema di RBAC, admission control e cifratura etcd. Confondere i due layer porta ad architetture che si sentono sicure ma hanno una singola chiave che decifra tutto l'universo.

Cos'è SOPS e come funziona l'envelope encryption

SOPS (Secrets OPerationS) è uno strumento open-source nato in Mozilla nel 2017 e passato al CNCF Sandbox nel 2023. Fa una cosa sola e la fa bene: cifra selettivamente i valori in file strutturati (YAML, JSON, ENV, INI, dotenv, BINARY), lasciando le chiavi in chiaro. Il risultato è che un Secret Kubernetes cifrato con SOPS mostra ancora apiVersion, kind, metadata.name e le chiavi dei dati, mentre i valori diventano stringhe cifrate del tipo ENC[AES256_GCM,data:...,iv:...,tag:...,type:str]. Un revisore può guardare la PR e capire cosa cambia strutturalmente senza vedere i secrets.

Il cuore è l'envelope encryption: SOPS genera un DEK (Data Encryption Key) casuale a 256 bit, cifra il payload con AES-256-GCM e poi cifra il DEK con una o più chiavi wrapping (age, GPG, AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault). I DEK cifrati vengono salvati nel metadata del file. Il vantaggio pratico è enorme: aggiungere un nuovo destinatario significa ri-cifrare solo il DEK (60 byte), non l'intero payload che potrebbe pesare megabyte.

# Anatomia di un file SOPS cifrato
apiVersion: v1
kind: Secret
metadata:
    name: db-credentials
type: Opaque
data:
    username: ENC[AES256_GCM,data:xI2j...,tag:AbCd...,type:str]
    password: ENC[AES256_GCM,data:9kPq...,tag:ZzYy...,type:str]
sops:
    age:
        - recipient: age1qyqsz...   # public key sviluppatore 1
          enc: |
              -----BEGIN AGE ENCRYPTED FILE-----
              ...DEK cifrato con age1qyqsz...
              -----END AGE ENCRYPTED FILE-----
        - recipient: age1lggyh...   # public key CI/CD
          enc: |
              -----BEGIN AGE ENCRYPTED FILE-----
              ...stesso DEK cifrato con age1lggyh...
              -----END AGE ENCRYPTED FILE-----
    lastmodified: "2026-08-13T09:41:22Z"
    mac: ENC[AES256_GCM,data:...,type:str]
    version: 3.9.1

Il campo mac è un MAC calcolato sull'intero file: se qualcuno modifica una chiave in chiaro senza avere il DEK, la decrittazione fallisce con MAC mismatch. Questa proprietà di autenticazione è ciò che rende SOPS adatto a un repository pubblico: un attacker può alterare i byte, ma non può falsificare un file "valido".

Perché age ha vinto contro GPG per SOPS

age (pronunciato "ah-gheh", da Actually Good Encryption) è uno strumento di Filippo Valsorda (ex-crittografo di Cloudflare e maintainer del Go crypto team), nato con l'obiettivo esplicito di essere il rimpiazzo semplice e moderno di GPG per il caso d'uso "cifra questo file per queste persone". La documentazione ufficiale su age-encryption.org lo descrive in una riga: X25519 + ChaCha20-Poly1305, nessuna configurazione, nessuna opzione da sbagliare.

La superficie di attacco è la ragione principale per cui i maintainer SOPS raccomandano age nella documentazione ufficiale. GPG ha decine di algoritmi negoziabili, un web-of-trust che nessuno usa correttamente, keyservers che vengono avvelenati, agent con stato che va condiviso tra sessioni. Ogni configurazione è un potenziale downgrade attack. age ha una scelta di algoritmo, zero opzioni, chiavi che sono stringhe di 60 caratteri gestibili come una variabile d'ambiente:

# Generare una chiave age
$ age-keygen -o ~/.config/sops/age/keys.txt
Public key: age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpxjsu2r
$ cat ~/.config/sops/age/keys.txt
# created: 2026-08-13T09:15:32+02:00
# public key: age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpxjsu2r
AGE-SECRET-KEY-1XFPZYTQEV7YFVK5D3G3YAKS0HDNM6MMH8JWD5GLPGA5MZAWM6...

Confronta con il boot GPG minimo: gpg --gen-key chiede tipo di chiave, dimensione, scadenza, identità, passphrase, richiede entropia sufficiente, poi devi esportare la chiave, pubblicarla, gestire i sub-key. Ho contato: sono 12 decisioni contro 1. Nel mondo reale ogni decisione è un errore in attesa.

Un dettaglio pratico: age può anche cifrare direttamente contro chiavi pubbliche SSH ed25519 e RSA. Se il tuo team ha già le SSH pubkey su GitHub, puoi usare curl https://github.com/USER.keys come sorgente di destinatari senza distribuire nuove chiavi. Non lo raccomando in produzione (SSH keys vivono su disco senza PIN, spesso senza passphrase) ma per esperimenti è ottimo.

Installazione di SOPS e age nel 2026

SOPS 3.9.1 e age 1.2.0 sono le versioni stabili al momento della scrittura. Su Ubuntu 24.04 LTS le versioni nei repository sono ancora vecchie (SOPS 3.7); consiglio i binari ufficiali da GitHub:

# Installazione SOPS 3.9.1 su Linux amd64
SOPS_VERSION=3.9.1
curl -LO https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64
sudo install -m 755 sops-v${SOPS_VERSION}.linux.amd64 /usr/local/bin/sops
sops --version
# sops 3.9.1 (latest)

# Installazione age 1.2.0
AGE_VERSION=1.2.0
curl -LO https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz
tar -xzf age-v${AGE_VERSION}-linux-amd64.tar.gz
sudo install -m 755 age/age age/age-keygen /usr/local/bin/
age --version    # v1.2.0
age-keygen --version

Su macOS con Homebrew: brew install sops age. Su Fedora 40+: dnf install sops age (attenzione alle versioni EPEL, spesso indietro di una minor). Per pipeline riproducibili in CI raccomando sempre binari con checksum verificato, non il pacchetto della distro, che cambia sotto i piedi.

# Verifica checksum in un job CI
SOPS_VERSION=3.9.1
SOPS_SHA256=0dfa1f34b0f27f7bc757b4d05b0c9beafb32d6f9dc0e4dfba5abd0efe58cd0dd
curl -LO https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64
echo "${SOPS_SHA256}  sops-v${SOPS_VERSION}.linux.amd64" | sha256sum -c -
sudo install -m 755 sops-v${SOPS_VERSION}.linux.amd64 /usr/local/bin/sops

Il checksum sopra è illustrativo: copia sempre quello pubblicato nella release GitHub del giorno. La sostituzione di un binario upstream è un vettore di supply chain reale.

Cifrare il primo Secret Kubernetes

Vediamo il flusso end-to-end. Ho un manifest Kubernetes con credenziali PostgreSQL da committare in Git in modo sicuro:

# 1. Generare la chiave age locale
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
export SOPS_AGE_RECIPIENTS=$(grep 'public key' ~/.config/sops/age/keys.txt | awk '{print $NF}')

# 2. Creare il Secret in chiaro (localmente, MAI committarlo)
cat > db-credentials.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
    name: postgres-prod
    namespace: payments
type: Opaque
stringData:
    POSTGRES_USER: payments_app
    POSTGRES_PASSWORD: 'p4ssw0rd-super-segret4-2026'
    POSTGRES_DSN: 'postgres://payments_app:[email protected]:5432/payments'
EOF

# 3. Cifrare in place
sops --encrypt --in-place --age "$SOPS_AGE_RECIPIENTS" \
     --encrypted-regex '^(data|stringData)$' \
     db-credentials.yaml

# 4. Verifica: le chiavi restano leggibili, i valori sono cifrati
cat db-credentials.yaml

Il flag --encrypted-regex '^(data|stringData)$' è cruciale per i manifest Kubernetes: dice a SOPS di cifrare solo i valori sotto le chiavi top-level data o stringData, lasciando in chiaro metadata.name, namespace, etichette. Senza questo flag SOPS cifrerebbe anche il nome del Secret, il che rompe la review nelle PR e (più grave) rompe alcuni controller Kubernetes che si aspettano di leggere il nome prima della decifratura.

Per modificare il file usa sempre sops db-credentials.yaml: apre l'editor con il contenuto decifrato in un file temporaneo, quando salvi ri-cifra automaticamente. Non usare vim direttamente sul file cifrato, non usare kubectl apply -f sul file cifrato (Kubernetes riceverebbe stringhe ENC[...] come password).

Configurare .sops.yaml per un team multi-recipient

Fissare i destinatari sulla command line non scala oltre uno sviluppatore. Il file .sops.yaml nella root del repository dichiara quali chiavi age (o KMS ARN, o fingerprint GPG) usare in base al path del file. SOPS lo legge automaticamente a ogni encrypt e updatekeys.

# .sops.yaml: configurazione team + separazione ambienti
creation_rules:
  # Secrets produzione: solo tech-lead + CI produzione
  - path_regex: clusters/prod/.*\.enc\.ya?ml$
    encrypted_regex: '^(data|stringData)$'
    age: >-
      age1qyqsz...tech-lead,
      age1lggyh...ci-prod

  # Secrets staging: tutto il team + CI staging
  - path_regex: clusters/staging/.*\.enc\.ya?ml$
    encrypted_regex: '^(data|stringData)$'
    age: >-
      age1qyqsz...tech-lead,
      age1p9dh4...alice-dev,
      age1zvkyg...bob-dev,
      age1w8x2m...carol-dev,
      age1lggyh...ci-staging

  # Fallback: catch-all che rifiuta la cifratura senza policy esplicita
  - path_regex: .*
    age: age1qyqsz...tech-lead

Tre pattern che uso in ogni deployment:

  1. Separazione per ambiente: staging accessibile all'intero team, produzione solo al subset che ha bisogno operativo. Il principle of least privilege applicato ai secrets.
  2. Chiave CI separata per ambiente: il runner GitHub Actions che deploya in produzione ha una chiave age diversa dal runner che deploya in staging. Un token GitHub compromesso non decifra automaticamente la produzione.
  3. Fallback esplicito: l'ultimo creation_rules impedisce che qualcuno crei un file cifrato "sciolto" fuori dai path previsti. Rende il perimetro auditabile con grep.

Quando aggiungi una persona al team: aggiungi la sua public key al .sops.yaml, poi esegui sops updatekeys clusters/staging/*.enc.yaml. Il comando ri-cifra solo il DEK per ogni file, aggiungendo l'entry del nuovo destinatario. Nessuna decifratura del payload, nessun re-encrypt di dati sensibili. Operazione locale in millisecondi anche su centinaia di file.

Integrare SOPS con Flux CD e ArgoCD

Flux CD ha il supporto SOPS più elegante: il kustomize-controller decifra nativamente al momento della reconciliation. Bastano due risorse: un Secret che contiene la chiave age privata del cluster e una Kustomization che punta ad essa:

# 1. Iniettare la chiave age privata del cluster come Secret nel namespace flux-system
kubectl create secret generic sops-age \
  --namespace=flux-system \
  --from-file=age.agekey=/percorso/locale/cluster-age.key

# 2. Kustomization con decrypt esplicito
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: payments-app
  namespace: flux-system
spec:
  interval: 5m
  path: ./clusters/prod/payments
  prune: true
  sourceRef:
    kind: GitRepository
    name: infra-repo
  decryption:
    provider: sops
    secretRef:
      name: sops-age

Il file age.agekey è il segreto di segreti, la chiave privata che decifra tutto in produzione. Va generata offline, iniettata nel cluster una volta sola tramite un canale sicuro (kubectl da workstation trusted), e mai committata da nessuna parte. Documentazione completa nella guida ufficiale Flux CD.

ArgoCD non ha supporto SOPS nativo. Le due strade praticabili:

  • argocd-vault-plugin con backend SOPS: intercetta i manifest prima di kubectl apply e sostituisce placeholder <path:secret#key> con i valori decifrati.
  • helm-secrets per chart Helm: wrappa helm template chiamando SOPS prima. Funziona bene se già usi Helm, aggiunge complessità se sei su Kustomize puro.

Il paradigma resta identico: la chiave privata age vive dentro il cluster in un Secret Kubernetes, protetta da RBAC stretto (solo il ServiceAccount del controller GitOps può leggerla) e idealmente dentro un namespace con NetworkPolicy che blocca traffico egress. Se hai già lavorato sulla difesa in profondità dei container Linux con seccomp e AppArmor, applica gli stessi principi al pod del controller: profilo AppArmor restrittivo, seccomp default, filesystem read-only, capability drop totale.

age-plugin-yubikey: chiavi private su hardware

La chiave privata age su disco è comoda ma vulnerabile: un backup rsync in chiaro, un tar della home, un dump di /root e la chiave è compromessa. age-plugin-yubikey sposta la chiave privata sull'applet PIV di una YubiKey 5/5C, così la chiave non lascia mai il token, le operazioni di decrypt richiedono touch fisico e opzionalmente PIN.

# Setup one-off della YubiKey (richiede age-plugin-yubikey installato)
age-plugin-yubikey --generate \
    --slot 1 \
    --name "aisha-prod-yubikey" \
    --pin-policy always \
    --touch-policy always

# Output: nuova identità e recipient
# Identity: AGE-PLUGIN-YUBIKEY-1QYQQZQ...
# Recipient: age1yubikey1q...

# Aggiungi il recipient al .sops.yaml e ri-cifra
sops updatekeys clusters/prod/*.enc.yaml

Il file di identità (~/.config/sops/age/keys.txt) contiene ora un URI AGE-PLUGIN-YUBIKEY-... anziché la chiave privata in chiaro. SOPS invoca il plugin al momento della decifratura, il plugin dialoga con la YubiKey, la YubiKey chiede touch, il DEK viene decifrato dentro il chip. Anche un attacker con root sulla mia workstation non estrae la chiave; al massimo può forzarmi a mettere il dito sul token mentre guardo.

Per team di alto valore uso questo pattern anche in CI: una YubiKey fisica connessa a un runner self-hosted, con policy touch never ma pin cached. La chiave resta sul token, il touch non serve per operazioni automatiche, ma un attacker che ruba il disco del runner non ottiene la chiave. È una difesa in più senza rompere l'automazione.

Rotazione delle chiavi e revoca di un membro del team

Le chiavi ruotano per tre motivi: un membro del team lascia, un dispositivo viene perso, un incidente di sicurezza. Con SOPS + age la procedura è meccanica:

# Scenario: Bob lascia il team, la sua chiave age va revocata

# 1. Rimuovi la public key di Bob dal .sops.yaml
vim .sops.yaml   # elimina 'age1zvkyg...bob-dev'

# 2. Ri-cifra tutti i file interessati (aggiorna solo il DEK, non il payload)
find clusters/ -name '*.enc.yaml' -exec sops updatekeys -y {} \;

# 3. Commit
git add .sops.yaml clusters/
git commit -m 'sops: revoca chiave age di bob-dev'
git push

# 4. IMPORTANTE: ruota anche il payload
# Il DEK vecchio esiste ancora nella cronologia Git.
# Bob (o chiunque abbia la sua chiave) può decifrare la storia.
# Se i secrets sono a lungo termine (API key, credenziali DB), CAMBIALI.

Il punto 4 è quello che la maggior parte dei tutorial omette e che mi fa alzare un sopracciglio ogni volta. Rimuovere una chiave da .sops.yaml non revoca l'accesso ai commit passati. Se Bob ha clonato il repo prima del suo ultimo giorno, ha ancora tutti i secrets di allora. La rotazione della chiave protegge solo i futuri commit. I secrets attuali che valgono qualcosa (credenziali database, chiavi API, token) vanno ruotati alla fonte, cambiati sul provider, poi il nuovo valore cifrato con la nuova policy.

Questa è esattamente la stessa logica per cui un token GitHub compromesso non si "revoca" cancellandolo dal repo: si revoca su github.com, e finché non lo fai il token cammina. La regola generale del threat modeling: ciò che è stato letto una volta, considera che sia stato copiato ovunque.

SOPS vs Sealed Secrets vs External Secrets Operator vs Vault

La scelta dello strumento dipende dal threat model e dalla maturità operativa del team. Questa è la matrice che uso per decidere:

CaratteristicaSOPS + ageSealed SecretsExternal Secrets OperatorHashiCorp Vault
Dove vivono i secretsGit (cifrati)Git (cifrati per cluster)Backend esterno (referenze in Git)Server Vault dedicato
Lock-in al clusterNessunoSì (master key controller)NessunoNessuno
Secrets dinamici / TTLNoNoSì (via backend)Sì nativo
Rotazione automaticaManuale con updatekeysRotazione periodica master keyBackend-drivenAutomatica per tipo
Audit log accessiVia cronologia GitNessunoVia backendDettagliato nativo
Multi-cloud / portabileNo
Complessità operativaBassaBassaMediaAlta
Best fitGitOps, team piccoli-medi, multi-toolCluster singolo, GitOps minimaleTeam che già usano cloud secret managerEnterprise, compliance stringente

La mia euristica per la scelta:

  • SOPS + age se hai bisogno di portabilità (Kubernetes + Terraform + Ansible dallo stesso repo), team con <20 sviluppatori, workflow GitOps chiaro e nessuna infrastruttura KMS gestita.
  • Sealed Secrets se hai un solo cluster stabile, GitOps puro, nessuna intenzione di usare i secrets fuori da Kubernetes.
  • External Secrets Operator se sei già dentro un cloud provider (AWS/GCP/Azure) e usi il loro secret manager per motivi indipendenti: l'ESO ti evita di duplicare la sorgente di verità.
  • HashiCorp Vault se hai requisiti di compliance (SOX, PCI, HIPAA) che richiedono audit log dettagliato di ogni accesso, o se ti servono credenziali dinamiche a breve durata (DB roles, cloud IAM, PKI).

Vale la pena notare che questi strumenti si combinano. Un pattern comune che ho implementato: SOPS + age per bootstrap del cluster (il primo Secret che contiene le credenziali Vault), poi Vault + ESO per tutto il resto. SOPS risolve il problema del "come cifri il segreto che ti serve per parlare con il gestore dei segreti". Ricorsione elegante.

Domande frequenti

SOPS è sicuro per repository Git pubblici?

Sì, se e solo se rispetti due condizioni: usi --encrypted-regex per non far trapelare informazioni sensibili nelle chiavi in chiaro (nomi di Secret rivelatori, label che indicano l'ambiente), e non commetti mai in Git la chiave privata age dei destinatari. Il payload cifrato con AES-256-GCM è considerato sicuro anche contro attori con risorse statuali per decenni. Il rischio pratico non è la crittografia, è la fuga della chiave privata attraverso un backup, un dump della home, o un ex-dipendente.

Cosa succede se perdo la chiave age privata?

Se sei l'unico destinatario nel .sops.yaml, perdi accesso permanente a quei file, e la crittografia funziona, motivo per cui non c'è recovery. La difesa è procedurale: mai un solo destinatario in produzione. Configura sempre almeno una chiave "break-glass" archiviata offline (YubiKey in cassaforte, o chiave stampata su carta con QR code), più le chiavi operative del team. Il recupero è la ragione principale per cui .sops.yaml con più recipient non è opzionale.

Posso usare SOPS con Terraform e Ansible, non solo Kubernetes?

Sì, ed è uno dei suoi vantaggi principali su Sealed Secrets. Il provider Terraform carlpett/sops legge file cifrati come sorgente di variabili; Ansible integra SOPS tramite il lookup plugin community.sops.sops; per script bash generici basta sops --decrypt file.enc.yaml. La stessa .sops.yaml governa la policy per Kubernetes manifests, tfvars, playbook variables, dotenv, ossia una singola sorgente di verità per la crittografia a livello di repository.

Come faccio a evitare che uno sviluppatore committi il file in chiaro per errore?

Tre livelli di difesa: (1) convenzione di naming *.enc.yaml per i file cifrati, e .gitignore aggressivo che blocca *.plain.yaml, *-decrypted.yaml; (2) pre-commit hook sops-pre-commit che rifiuta il commit se il file non contiene la sezione sops:; (3) scan del repo in CI con gitleaks o trufflehog che cattura credential shape (regex per API key, private key headers) anche in file che non ci si aspettava.

Qual è la differenza principale tra SOPS e Sealed Secrets?

SOPS è agnostico rispetto a Kubernetes: cifra qualsiasi file YAML/JSON/ENV e lo committa in Git. La decifratura avviene dove serve (CI, workstation, controller). Sealed Secrets è Kubernetes-specifico: il controller nel cluster è l'unico che può decifrare, con una master key locale al cluster. Se hai un solo cluster e vivi dentro Kubernetes, Sealed Secrets è più semplice. Se hai Terraform, Ansible, multi-cluster o vuoi portabilità, SOPS vince nettamente.

Aisha Okonkwo
Sull'Autore Aisha Okonkwo

Infrastructure security architect at a hyperscaler. Spends her days on Zero Trust, secrets management, and yelling at unencrypted backups.