Mozilla SOPS: GitOps-Native Secret Encryption, die tatsächlich funktioniert
Ein umfassender Leitfaden zu Mozilla SOPS für die Verwaltung verschlüsselter Secrets in Git-Repositories. Lerne age encryption, AWS CDK-Integration, AWS Lambda-Patterns und production-ready Security-Strategien für serverless Workflows.
Abstract
Mozilla SOPS (Secrets OPerationS) löst eine grundlegende Herausforderung in GitOps: Wie kannst du Secrets sicher in die Versionskontrolle committen und gleichzeitig die Developer Productivity aufrechterhalten? Im Gegensatz zu cloud-nativen Secret Stores verschlüsselt SOPS Dateien direkt in Git-Repositories und bewahrt dabei die YAML/JSON-Struktur, während sensible Werte geschützt werden. Dieser Leitfaden behandelt praktische Implementation-Patterns wie age encryption, AWS Lambda-Integration, AWS CDK-Workflows, AWS SAM-Patterns und CI/CD-Automatisierung für serverless Deployments in GitHub Actions, GitLab CI und Jenkins.
Die GitOps Secret Management Challenge
Wenn du mit Infrastructure as Code arbeitest, entsteht sofort ein Problem. Du musst deine serverless Konfigurationsdateien, Terraform-Variablen und Environment-Configs unter Versionskontrolle stellen. Aber diese Dateien enthalten Datenbank-Passwörter, API-Keys und Service-Credentials. In dem Moment, in dem du Secrets in Git commitest, hast du eine Security-Schwachstelle geschaffen.
Traditionelle Lösungen erzeugen Friction. HashiCorp Vault erfordert laufende Infrastruktur und API-Aufrufe zur Deployment-Zeit. AWS Secrets Manager kostet $0.40 pro Secret pro Monat und fügt Runtime-API-Aufrufe zu deinen Lambda-Funktionen hinzu. AWS Systems Manager Parameter Store ist kostenlos, erfordert aber dennoch Runtime-Fetching. Jeder Ansatz zieht Secrets aus deinem GitOps-Workflow heraus und in externe Systeme.
SOPS verfolgt einen anderen Ansatz. Es verschlüsselt Dateien direkt in deinem Git-Repository und hält Secrets versioniert zusammen mit deinem Code. Wenn du einen API-Key änderst und deine Lambda-Funktion aktualisierst, gehen beide Änderungen in denselben Commit. Wenn du einen Rollback machst, werden beide zusammen zurückgesetzt. Deine Git-History wird zu deinem Audit-Trail.
SOPS-Architektur verstehen
SOPS verwendet Envelope Encryption. Wenn du eine Datei verschlüsselst, generiert SOPS einen zufälligen 256-Bit-Data-Key und verschlüsselt deinen Dateiinhalt mit AES256-GCM. Dann verschlüsselt es diesen Data-Key mit einem oder mehreren Master-Keys (AWS KMS, age, PGP, GCP KMS oder Azure Key Vault) und speichert den verschlüsselten Data-Key in den Metadaten der Datei. Age verschlüsselt den SOPS-Data-Key mit X25519 + ChaCha20-Poly1305.
Für strukturierte Formate wie YAML und JSON verschlüsselt SOPS nur die Werte, nicht die Keys. Dies hält deine Dateistruktur für Code-Reviews sichtbar und ermöglicht es Tools, das Schema zu parsen, auch wenn die Werte verschlüsselt sind.
So sieht eine verschlüsselte YAML-Datei aus:
Du kannst immer noch die Datenbank-Konfigurationsstruktur sehen. Du weißt, dass es Host-, Port- und Password-Felder gibt. Aber die tatsächlichen Werte sind verschlüsselt. Git diff zeigt, welche Felder sich geändert haben, nicht nur dass "der verschlüsselte Blob sich geändert hat".
Installation und Setup
Die Installation variiert je nach Plattform, dauert aber weniger als fünf Minuten:
Für Container-Umgebungen verwendest du das offizielle Image:
Age: Die moderne Encryption-Wahl
SOPS unterstützt mehrere Key-Management-Systeme. Für Team-Umgebungen hat sich age (ausgesprochen wie "h-age") zur empfohlenen Wahl gegenüber PGP entwickelt.
Age-Public-Keys sind 62 Zeichen, Private-Keys sind 74 Zeichen lang. PGP-Keys sind 4096 Zeichen. Du kannst einen age-Public-Key in Slack kopieren und einfügen. PGP-Keys brechen über mehrere Zeilen. Age verwendet moderne Kryptographie (X25519 + ChaCha20-Poly1305). PGP kommt mit Jahrzehnten an Komplexität aus dem GPG-Keyring-System.
Generiere ein age-Key-Pair:
Der Private Key wird in ~/.config/sops/age/keys.txt gespeichert. Der Public Key ist das, was du mit Team-Mitgliedern teilst und in SOPS konfigurierst.
Um eine Datei mit age zu verschlüsseln:
Für Team-Distribution generiert jeder seinen eigenen age-Key und teilt seinen Public Key. Du konfigurierst SOPS so, dass es mit allen Public Keys der Team-Mitglieder verschlüsselt. Jeder mit seinem Private Key kann entschlüsseln.
Die .sops.yaml-Konfigurationsdatei
Das Erstellen einer .sops.yaml-Datei im Repository-Root eliminiert manuelles Key-Management. SOPS liest diese Datei, um anhand von Dateipfaden zu bestimmen, welche Keys verwendet werden sollen.
Hier ist eine production-ready Konfiguration:
Jetzt wird die Verschlüsselung automatisch:
Die pfadbasierten Regeln eliminieren menschliche Fehler. Developer müssen sich nicht merken, welche Keys für welche Umgebung verwendet werden.
AWS KMS-Integration
Für Production-Umgebungen bietet AWS KMS zentralisiertes Key-Management mit IAM-basierter Zugriffskontrolle und Audit-Logging über CloudTrail.
Erstelle einen KMS-Key:
Konfiguriere SOPS für KMS-Verwendung:
Für CI/CD-Umgebungen verwende IAM-Rollen statt Credentials zu speichern:
Die IAM-Rolle benötigt KMS-Decrypt-Berechtigungen:
Multi-Account AWS Setup
Enterprise-Umgebungen operieren selten innerhalb eines einzigen AWS-Accounts. Development-, Staging- und Production-Umgebungen laufen in separaten Accounts für Security-Isolation und Blast-Radius-Containment. SOPS unterstützt diese Architektur durch umgebungsspezifische KMS-Keys und Cross-Account-IAM-Berechtigungen.
Architektur-Überblick
Ein typisches Multi-Account-Setup trennt Umgebungen in verschiedene AWS-Accounts, jeder mit dedizierten KMS-Keys. Dies verhindert, dass Developer versehentlich auf Production-Secrets zugreifen, und bietet klare Security-Boundaries.
Diese Architektur erzwingt mehrere Security-Prinzipien. Developer benötigen explizite Cross-Account-Role-Assumption, um auf jede Umgebung zuzugreifen. KMS-Keys sind account-lokal, sodass die Kompromittierung einer Umgebung andere nicht offenlegt. CloudTrail-Logs in jedem Account bieten unabhängige Audit-Trails.
KMS-Keys pro Environment-Account
Jeder AWS-Account pflegt seinen eigenen KMS-Key. Die .sops.yaml-Konfiguration mapped Dateipfade zu account-spezifischen KMS-Keys.
Die Verzeichnisstruktur spiegelt die Account-Trennung wider:
Wenn du eine Datei in secrets/prod/ verschlüsselst, verwendet SOPS automatisch den Production-Account-KMS-Key. Keine manuelle Key-Auswahl erforderlich.
Cross-Account-KMS-Zugriff
Damit CI/CD-Pipelines Secrets über Accounts hinweg entschlüsseln können, müssen KMS-Key-Policies Cross-Account-Zugriff erlauben. Dies erfordert Konfiguration sowohl in der KMS-Key-Policy als auch in den IAM-Rollen-Berechtigungen.
KMS-Key-Policy im Production-Account (333333333333):
Die Bedingung beschränkt die KMS-Nutzung auf spezifische AWS-Services und verhindert direkten Key-Zugriff außerhalb legitimer Deployment-Kontexte.
IAM-Rolle im CI/CD-Account (444444444444):
Diese IAM-Policy erlaubt der CI/CD-Rolle sowohl die Entschlüsselung mit KMS-Keys als auch das Annehmen von Deployment-Rollen in Ziel-Accounts.
CI/CD mit Rollen-Assumption
GitHub-Actions-Workflows nehmen verschiedene Rollen für verschiedene Umgebungen an. Die AWS-Credentials-Action unterstützt Rollen-Chaining für Multi-Account-Deployments.
Der Workflow trennt Deployment-Jobs nach Umgebung. Jeder Job nimmt die entsprechende Account-Rolle an, bevor er Secrets entschlüsselt. Die needs-Abhängigkeit stellt sicher, dass Dev vor Production deployed wird. Das environment: production fügt manuelle Approval-Gates hinzu.
Developer-Workflow mit AWS-Profilen
Developer, die lokal arbeiten, benötigen AWS-Profilkonfigurationen für jeden Account. Die ~/.aws/config-Datei definiert Rollen-Assumption-Chains.
Production-Zugriff erfordert MFA. Wenn ein Developer SOPS-Befehle für Production-Secrets ausführt, fordert AWS ein MFA-Token an.
Secrets für verschiedene Umgebungen verschlüsseln:
Für lokale Tests entschlüsseln:
Die Profilauswahl erfolgt über die AWS_PROFILE-Umgebungsvariable. SOPS verwendet automatisch den richtigen KMS-Key basierend auf dem Dateipfad und nimmt die entsprechende Rolle basierend auf dem aktiven Profil an.
Security-Überlegungen
Multi-Account-SOPS-Deployments führen mehrere Security-Anforderungen ein, die durch IAM-Policies und organisatorische Kontrollen durchgesetzt werden müssen.
Principle of Least Privilege: Developer sollten nur auf die Umgebungen zugreifen, mit denen sie aktiv arbeiten. Ein Junior-Developer, der in Development-Umgebungen arbeitet, sollte keine Production-KMS-Decrypt-Berechtigungen haben. Rollen-Policies sollten diese Segregation widerspiegeln.
Das explizite Deny stellt sicher, dass dieser Developer Production-Secrets nicht entschlüsseln kann, selbst wenn höherstufige Policies Zugriff gewähren.
Audit-Logging mit CloudTrail: Jeder AWS-Account sollte CloudTrail aktiviert haben, wobei Logs an einen zentralisierten Security-Account gesendet werden. Dies erstellt einen unveränderlichen Audit-Trail aller KMS-Operationen.
CloudTrail-Logs zeigen, wer wann auf welchen KMS-Key zugegriffen hat. Dies ermöglicht die Erkennung unbefugter Zugriffsversuche oder Compliance-Audits.
Key-Policies vs. IAM-Policies: Verwende beide für Defense in Depth. KMS-Key-Policies definieren auf Ressourcenebene, wer den Key verwenden kann. IAM-Policies definieren, was die Identität tun kann. Beide müssen die Operation erlauben, damit sie erfolgreich ist.
Ein Production-KMS-Key sollte eine restriktive Key-Policy haben, die nur bestimmte Deployment-Rollen zulässt, selbst wenn anderswo breitere IAM-Policies existieren. Dies verhindert Privilege-Escalation durch IAM-Policy-Änderungen allein.
Break-Glass-Verfahren: Selbst mit MFA und restriktiven Policies erfordern Notfälle schnellen Production-Zugriff. Pflege eine Emergency-Access-Rolle mit zeitlich begrenzten Credentials und automatischer Alarmierung bei Verwendung.
Diese Policy erlaubt Emergency-Zugriff, aber nur innerhalb eines Zeitfensters. Wenn die Rolle angenommen wird, lösen CloudWatch Events Alarme an Security-Teams und Management aus.
AWS Lambda-Integration mit SOPS
Lambda-Funktionen benötigen Secrets zur Runtime, aber du möchtest diese Secrets zusammen mit deinem Funktionscode versionskontrollieren. SOPS ermöglicht dies, indem Secrets während des Deployments entschlüsselt werden, nicht zur Runtime.
AWS CDK-Integration (Empfohlener Ansatz)
AWS CDK bietet mehrere Patterns für SOPS-Integration, von einfach bis fortgeschritten. Für moderne serverless-Entwicklung ist CDK der empfohlene Ansatz.
Pattern 1: Direkte Umgebungsvariablen-Injection
Der einfachste Ansatz entschlüsselt SOPS zur CDK-Synthese-Zeit und injiziert Werte als Lambda-Umgebungsvariablen:
Synthesisiere und deploye:
Pattern 2: SSM Parameter Store-Befüllung
Für Secrets, die Runtime-Rotation benötigen, befülle SSM Parameter Store aus SOPS:
Lambda-Funktionen können diese Parameter zur Laufzeit lesen:
Pattern 3: cdk-sops-secrets Construct verwenden
Die Community-maintained cdk-sops-secrets-Bibliothek bietet eine elegantere Integration:
Pattern 4: Multi-Stack Secret Sharing
Für das Teilen von Secrets über mehrere Stacks:
AWS SAM-Integration
AWS SAM unterstützt Umgebungsvariablen aus Dateien. Entschlüssele SOPS-Secrets während des Deployments:
Deploye mit entschlüsselten Secrets:
Local Development Workflow
Für lokale Tests in CDK-Projekten entschlüssele Secrets temporär:
Verwende den SOPS-Exec-Modus, um Befehle mit entschlüsselter Umgebung auszuführen:
SSM Parameter Store vs. SOPS-Vergleich
Verwende SOPS wenn:
- Secrets sich mit Code-Deployments ändern
- Du Git-basierte Audit-Trails möchtest
- Secrets statisch sind (API-Keys, OAuth-Credentials)
- Team-Kollaboration an Secrets wichtig ist
- Kostenoptimierung Priorität hat
Verwende SSM Parameter Store wenn:
- Secrets unabhängig von Deployments rotieren
- Mehrere Services dieselben Secrets teilen
- Du AWS-native Secret-Rotation benötigst
- Runtime-Secret-Updates ohne Redeployment nötig sind
- Cross-Region Secret-Replikation erforderlich ist
Hybrid-Ansatz:
Terraform-Integration
Der Terraform-SOPS-Provider ermöglicht das Lesen verschlüsselter Variablendateien, während deine State-Datei sauber bleibt.
Konfiguriere den Provider:
Erstelle eine verschlüsselte Variablendatei:
Verschlüssele sie:
Referenziere in Terraform:
Das Passwort erscheint nie in Plaintext in deiner Terraform-State-Datei, weil wir ignore_changes verwenden. Für die initiale Erstellung entschlüsselt SOPS den Wert. Für nachfolgende Applies ignoriert Terraform Passwort-Änderungen.
Ein besseres Pattern ist SOPS zum Befüllen von AWS Secrets Manager zu verwenden und dann die Secret-ARN zu referenzieren:
Jetzt fetcht deine Application-Runtime Secrets aus Secrets Manager, aber die initialen Secret-Werte sind mit SOPS versionskontrolliert.
CI/CD-Integration-Patterns
GitHub Actions mit CDK (Primary Pattern)
AWS CDK entschlüsselt SOPS automatisch während der Synthese, dies ist der empfohlene Ansatz für moderne serverless-Deployments:
CDK-Code entschlüsselt SOPS während der Synthese, daher ist kein expliziter Entschlüsselungsschritt in CI/CD erforderlich.
Die IAM-Rolle benötigt CDK-Deployment-Berechtigungen plus KMS-Decrypt:
Multi-Environment CDK Deployment
Deploye in verschiedene Umgebungen mit umgebungsspezifischen Secrets:
GitHub Actions mit AWS SAM (Alternative Pattern)
Für AWS SAM-Deployments:
GitLab CI
GitLab CI verwendet ähnliche Patterns:
Das entschlüsselte Secrets-Artefakt ist für nachfolgende Stages verfügbar, läuft aber nach 10 Minuten ab.
Developer Experience und IDE-Integration
Verschlüsselte Dateien manuell zu bearbeiten wäre schmerzhaft. SOPS bietet einen Edit-Modus, der die Verschlüsselung transparent handhabt.
Setze deinen Editor:
Bearbeite eine verschlüsselte Datei:
SOPS entschlüsselt die Datei, öffnet sie in deinem Editor, wartet darauf, dass du speicherst und schließt, und verschlüsselt dann mit aktualisierten Werten neu. Du siehst während der Bearbeitung nie den verschlüsselten Inhalt.
Für VS Code installiere die SOPS-Extension:
Die Extension entschlüsselt Dateien automatisch, wenn du sie in VS Code öffnest, und verschlüsselt sie beim Speichern neu.
Für aussagekräftige Git-Diffs konfiguriere einen benutzerdefinierten Differ:
Jetzt zeigt git diff secrets.enc.yaml die tatsächlichen Wertänderungen, nicht verschlüsselte Blob-Unterschiede.
Pre-commit Hooks und Validation
Verhindere das Committen entschlüsselter Secrets mit Pre-commit-Hooks:
Dieser Hook verifiziert, dass Dateien, die dem Pattern entsprechen, verschlüsselt sind, bevor der Commit erlaubt wird.
Füge benutzerdefinierte Validation hinzu:
Der Hook verhindert sowohl das versehentliche Committen von Plaintext-Secrets als auch das Committen von Dateien, die behaupten verschlüsselt zu sein, es aber nicht sind.
Key-Rotation-Strategien
Age-Keys sollten alle 90 Tage rotiert werden. Die Automatisierung dieses Prozesses verhindert, dass es vergessen wird.
Generiere einen neuen age-Key:
Füge den neuen Key zu allen verschlüsselten Dateien hinzu:
Rotiere die Data-Keys (generiert neue zufällige Keys):
Entferne den alten Key:
Aktualisiere .sops.yaml:
Committe und verteile den neuen Private Key an dein Team über einen sicheren Kanal (Password-Manager, verschlüsselte E-Mail, sicheres Messaging).
Für KMS-Keys ist der Prozess ähnlich, beinhaltet aber das Erstellen eines neuen KMS-Keys, das Hinzufügen zu Dateien, das Rotieren und das Entfernen der alten KMS-Key-ARN.
Multi-Team-Zugriff mit Key Groups
Production-Umgebungen erfordern oft, dass mehrere Teams auf Secrets zugreifen. SOPS unterstützt dies durch Key Groups und Shamir's Secret Sharing.
Mit shamir_threshold: 2 erfordert die Entschlüsselung Keys von 2 der 3 Gruppen. Dies implementiert Separation of Duties. Ein Platform-Engineer kann Production-Secrets nicht alleine entschlüsseln. Das Security-Team auch nicht. Aber zwei beliebige Gruppen zusammen können entschlüsseln.
Der Data-Key wird mit Shamir's Secret Sharing in Fragmente aufgeteilt. Fragment 1 wird für das Platform-Team verschlüsselt, Fragment 2 für das Security-Team, Fragment 3 für Backup. Zwei beliebige Fragmente können den vollständigen Data-Key rekonstruieren.
Kostenvergleich und Trade-offs
Für ein Szenario mit 100 Secrets:
SOPS mit AWS KMS:
- KMS-Keys: 3 × 3
- API-Aufrufe: ~1,000 Entschlüsselungen/Monat = $0.03
- Git-Speicher: $0 (existierendes Repository)
- Gesamt: $3.03/Monat
AWS Secrets Manager:
- Secrets: 100 × 40
- API-Aufrufe: 10,000/Monat × 0.05
- Gesamt: $40.05/Monat
Einsparungen: $37/Monat (92% Reduktion)
Aber Kosten sind nicht die einzige Überlegung. Secrets Manager bietet automatisierte Rotation, während SOPS Scripting erfordert. Secrets Manager hat integrierte Audit-Logs über CloudTrail. SOPS verlässt sich auf Git-History.
SOPS gewinnt bei statischen Secrets (API-Keys, OAuth-Credentials, Datenbank-Verbindungsstrings, die sich selten ändern). Secrets Manager gewinnt bei dynamischen Secrets (Datenbank-Passwörter, die wöchentlich rotieren, Service-Credentials mit automatisierter Erneuerung).
Ein hybrider Ansatz funktioniert gut:
- Development und Staging: SOPS mit age-Keys
- Production statische Secrets: SOPS mit KMS
- Production dynamische Secrets: AWS Secrets Manager
- Datenbank-Root-Passwörter: Secrets Manager
- Third-Party-API-Keys: SOPS
Häufige Fallstricke und Lösungen
Fallstrick: Entschlüsselte Dateien committen
Füge zu .gitignore hinzu:
Fallstrick: Age Private Keys verlieren
Speichere Backups an mehreren Orten:
- Password-Manager (1Password, LastPass)
- Verschlüsseltes USB-Laufwerk im physischen Safe
- Offline gespeicherter Emergency-Recovery-Key
Erstelle einen Emergency-Key und füge ihn zu allen Production-Secrets hinzu:
Fallstrick: KMS-Permission-Probleme
Der Fehler "AccessDeniedException" beim Entschlüsseln bedeutet normalerweise, dass die IAM-Berechtigungen falsch sind. Verifiziere:
Stelle sicher, dass deine IAM-Rolle sowohl kms:Decrypt als auch kms:DescribeKey-Berechtigungen für den KMS-Key hat.
Fallstrick: Git Merge Conflicts
Wenn zwei Developer gleichzeitig dieselbe verschlüsselte Datei bearbeiten, erstellt Git einen Merge-Konflikt mit verschlüsselten Blobs.
Hinweis: Beim normalen Editieren entschlüsselt
sops secrets.enc.yamldie Datei, öffnet sie in deinem Editor und verschlüsselt automatisch neu, wenn du speicherst und beendest. Aber bei Merge-Konflikten musst du zwei verschiedene Versionen vergleichen, daher ist der manuelle decrypt → merge → re-encrypt Workflow erforderlich.
Die Auflösung erfordert:
Besser: Kommuniziere beim Bearbeiten gemeinsamer Secrets, oder teile große Dateien in kleinere domänenspezifische Dateien auf, um die Kollisionswahrscheinlichkeit zu reduzieren.
Key Takeaways
SOPS ermöglicht GitOps-Workflows für Serverless ohne externe Secret-Abhängigkeiten. Secrets werden mit Lambda-Code versioniert, zusammen deployed und zusammen zurückgesetzt. Deine Git-History wird zu deinem Audit-Trail.
Age-Verschlüsselung bietet eine moderne, einfache Alternative zu PGP. Die Keys sind kurz genug, um sie im Chat zu teilen. Das Tooling ist minimal. Die Onboarding-Zeit liegt unter 30 Minuten.
Die .sops.yaml-Konfigurationsdatei eliminiert manuelles Key-Management. Pfadbasierte Regeln wählen automatisch die richtigen Keys für jede Umgebung aus. Developer verschlüsseln Dateien, ohne KMS-ARNs zu kennen oder sich zu merken, welche Keys verwendet werden sollen.
AWS CDK ist der empfohlene Ansatz für moderne serverless-Entwicklung. CDK entschlüsselt SOPS während der Synthese, daher sind keine expliziten Decrypt-Schritte in CI/CD-Pipelines erforderlich. Die cdk-sops-secrets Construct-Bibliothek bietet noch elegantere Integration.
Für Lambda-Deployments entschlüsselt SOPS zur Build/Deploy-Zeit, nicht zur Runtime. Dies eliminiert Cold-Start-Overhead durch Secret-Fetching. Umgebungsvariablen werden während des Deployments in die Funktionskonfiguration eingebacken.
CDK, AWS SAM und Serverless Framework integrieren sich alle nahtlos mit SOPS. CDK entschlüsselt während der Synthese. SAM befüllt Secrets Manager aus SOPS-Dateien. Serverless Framework verwendet Plugins oder Pre-Deploy-Scripts.
Für Production-Umgebungen bietet die Kombination von KMS mit age sowohl zentralisiertes Management als auch Emergency Recovery. KMS handhabt die primäre Verschlüsselung mit IAM-basierter Zugriffskontrolle. Age bietet einen Backup-Entschlüsselungspfad, wenn KMS nicht verfügbar ist.
Die Kosteneinsparungen im Vergleich zu AWS Secrets Manager sind für statische Secrets erheblich. SOPS funktioniert am besten für Konfiguration, die sich mit Lambda-Deployments ändert (API-Keys, OAuth-Credentials). Verwende SSM Parameter Store oder Secrets Manager für dynamische Secrets, die sich unabhängig rotieren (Datenbank-Passwörter).
Ein Hybrid-Ansatz maximiert den Wert: SOPS für statische Secrets, SSM für dynamische Secrets. Dies balanciert Kostenoptimierung mit operationeller Flexibilität.
Pre-commit-Hooks und Validation sind obligatorisch, nicht optional. Ohne automatisierte Checks wird irgendwann jemand eine entschlüsselte Datei committen. Richte Guardrails ein, bevor dein Team SOPS in Production verwendet.