migration/infra #1
22
.gitignore
vendored
22
.gitignore
vendored
@@ -42,3 +42,25 @@ terraform.rc
|
|||||||
# Optional: ignore plan files saved before destroying Terraform configuration
|
# Optional: ignore plan files saved before destroying Terraform configuration
|
||||||
# Uncomment the line below if you want to ignore planout files.
|
# Uncomment the line below if you want to ignore planout files.
|
||||||
# planout
|
# planout
|
||||||
|
**/.claude
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Secrets — voir .sops.yaml
|
||||||
|
# ============================================
|
||||||
|
# Les secrets vivent UNIQUEMENT dans des stacks/*/secrets.enc.yaml chiffrés,
|
||||||
|
# consommés à la volée par `sops exec-env`. Aucun secret déchiffré ne doit
|
||||||
|
# exister sur le filesystem : les règles ci-dessous sont une ceinture de
|
||||||
|
# sécurité, pas un mode de fonctionnement.
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
*.dec.yaml
|
||||||
|
*.decrypted.*
|
||||||
|
|
||||||
|
# La configuration NON sensible de chaque stack vit dans un fichier nommé
|
||||||
|
# `env_file` (sans extension) : il échappe donc volontairement aux règles
|
||||||
|
# ci-dessus et reste versionné en clair.
|
||||||
|
|
||||||
|
# Clés privées age (ne doivent jamais approcher le dépôt)
|
||||||
|
*.agekey
|
||||||
|
age-key.txt
|
||||||
|
keys.txt
|
||||||
|
|||||||
53
.sops.yaml
Normal file
53
.sops.yaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Politique de chiffrement SOPS
|
||||||
|
# ============================================================
|
||||||
|
# Deux périmètres, séparés par ACTEUR — c'est le garde-fou principal
|
||||||
|
# de ce dépôt :
|
||||||
|
#
|
||||||
|
# 1. stacks/*/secrets.env.yaml
|
||||||
|
# Déchiffrables par chioggia (déploiement automatique, sans humain)
|
||||||
|
# ET par l'admin. YAML PLAT uniquement (CLE: valeur) : `sops exec-env`
|
||||||
|
# ne sait pas exporter une structure imbriquée.
|
||||||
|
#
|
||||||
|
# 2. tout le reste
|
||||||
|
# Déchiffrables par l'admin UNIQUEMENT.
|
||||||
|
# Conséquence voulue : une compromission de chioggia ne donne PAS
|
||||||
|
# accès aux credentials OVH / registrar. Sans cette séparation,
|
||||||
|
# prendre le serveur reviendrait à prendre le domaine (donc les MX,
|
||||||
|
# le SPF, et la capacité d'émettre des certificats).
|
||||||
|
#
|
||||||
|
# Les credentials OpenTofu (OVH, AdGuard) restent gérés par `pass` :
|
||||||
|
# ils sont utilisés en interactif depuis le poste admin, jamais par une
|
||||||
|
# machine. Voir dns/ovh.tf et dns/local.tf.
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Générer une clé — une par machine, seule la PUBLIQUE arrive ici :
|
||||||
|
#
|
||||||
|
# poste admin : age-keygen | pass insert -m infra/age-key
|
||||||
|
# (publique : pass show infra/age-key | age-keygen -y)
|
||||||
|
# serveur : mkdir -p ~/.config/sops/age
|
||||||
|
# (umask 077; age-keygen -o ~/.config/sops/age/keys.txt)
|
||||||
|
# age-keygen -y ~/.config/sops/age/keys.txt
|
||||||
|
#
|
||||||
|
# Fournir la clé privée à sops :
|
||||||
|
# poste : export SOPS_AGE_KEY="$(pass show infra/age-key)"
|
||||||
|
# serveur : ~/.config/sops/age/keys.txt est l'emplacement par défaut,
|
||||||
|
# lu sans aucune variable d'environnement
|
||||||
|
#
|
||||||
|
# Rappel : ajouter une clé ici ne rechiffre RIEN.
|
||||||
|
# sops updatekeys -y stacks/*/secrets.env.yaml
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
keys:
|
||||||
|
- &lafrite_combava age18m2zl3pwgw97djshzcf4wmhrqxksl4erqse6edkpxpkg5rcz0ujs8u4c7s
|
||||||
|
- &waha_chioggia age1fh70nny5hzz8a8g9077kgfca0lhlh3wsjf4fjrv4klx5ucdtpdmsazuqdj
|
||||||
|
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: ^stacks/.*/secrets\.env\.yaml$
|
||||||
|
age:
|
||||||
|
- *lafrite_combava
|
||||||
|
- *waha_chioggia
|
||||||
|
|
||||||
|
- path_regex: .*
|
||||||
|
age:
|
||||||
|
- *lafrite_combava
|
||||||
93
README.md
93
README.md
@@ -2,6 +2,99 @@
|
|||||||
|
|
||||||
Configuration Terraform pour gérer l'infrastructure domestique.
|
Configuration Terraform pour gérer l'infrastructure domestique.
|
||||||
|
|
||||||
|
## Secrets (SOPS + age)
|
||||||
|
|
||||||
|
Les secrets sont chiffrés **dans** le dépôt. Rien de déchiffré ne doit jamais toucher
|
||||||
|
le disque : `sops exec-env` injecte les valeurs en variables d'environnement le temps
|
||||||
|
d'une commande.
|
||||||
|
|
||||||
|
### Créer une clé
|
||||||
|
|
||||||
|
Une clé par machine. La privée ne quitte jamais sa machine ; seule la **publique**
|
||||||
|
est recopiée dans `.sops.yaml`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# poste admin — la privée va dans pass, jamais sur le disque
|
||||||
|
age-keygen | pass insert -m infra/age-key
|
||||||
|
pass show infra/age-key | age-keygen -y # publique -> .sops.yaml
|
||||||
|
|
||||||
|
# serveur (chioggia, ou toute nouvelle machine) — sous le compte qui déploie
|
||||||
|
# les stacks, PAS root : sops lit ce chemin par défaut, sans aucune variable
|
||||||
|
# d'environnement à exporter
|
||||||
|
mkdir -p ~/.config/sops/age
|
||||||
|
(umask 077; age-keygen -o ~/.config/sops/age/keys.txt)
|
||||||
|
age-keygen -y ~/.config/sops/age/keys.txt # publique -> .sops.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
`age-keygen` écrit la clé privée sur stdout (trois lignes, dont `# public key:`)
|
||||||
|
et un rappel de la publique sur stderr — c'est bien la privée seule qui entre dans
|
||||||
|
`pass`. Vérifier avec `pass show infra/age-key`.
|
||||||
|
|
||||||
|
Ajouter ensuite la publique au bon `creation_rules` de `.sops.yaml`, **puis
|
||||||
|
rechiffrer l'existant** — sans quoi la nouvelle machine ne pourra rien lire :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sops updatekeys -y stacks/*/secrets.env.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Où sont les clés
|
||||||
|
|
||||||
|
| Machine | Clé privée | Comment sops la trouve |
|
||||||
|
|---|---|---|
|
||||||
|
| Poste admin | dans `pass`, sous `infra/age-key` | `export SOPS_AGE_KEY="$(pass show infra/age-key)"` |
|
||||||
|
| chioggia | `~/.config/sops/age/keys.txt` (600) du compte de déploiement | emplacement par défaut : rien à exporter |
|
||||||
|
|
||||||
|
Sur chioggia, le compte qui déploie est aussi celui qui déchiffre. Il est membre du
|
||||||
|
groupe `docker` et n'a donc jamais besoin de `sudo` — ni pour `git pull`, ni pour
|
||||||
|
`docker compose`, ni pour lire sa clé.
|
||||||
|
|
||||||
|
`.sops.yaml` définit qui déchiffre quoi : chioggia n'a accès qu'aux
|
||||||
|
`stacks/*/secrets.env.yaml`. Les credentials OVH et AdGuard restent gérés par `pass`
|
||||||
|
(voir `dns/`) et sont hors de sa portée — une compromission du serveur ne doit pas
|
||||||
|
donner le domaine.
|
||||||
|
|
||||||
|
### Recettes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# éditer un secret (rechiffré à la sortie de l'éditeur)
|
||||||
|
sops edit stacks/vault.opytex.org/secrets.env.yaml
|
||||||
|
|
||||||
|
# créer les secrets d'une nouvelle stack — le chemin n'a pas besoin d'exister,
|
||||||
|
# rien n'est écrit en clair
|
||||||
|
sops edit stacks/<nom>/secrets.env.yaml
|
||||||
|
|
||||||
|
# lire sans éditer
|
||||||
|
sops -d stacks/vault.opytex.org/secrets.env.yaml
|
||||||
|
|
||||||
|
# déployer, depuis le dossier de la stack sur chioggia
|
||||||
|
sops exec-env secrets.env.yaml 'docker compose up -d'
|
||||||
|
|
||||||
|
# après TOUTE modification de .sops.yaml (nouvelle machine, clé retirée).
|
||||||
|
# Accepte plusieurs fichiers malgré l'aide qui annonce `file` au singulier.
|
||||||
|
# Sans -y, sops demande une confirmation par fichier — mais montre le diff
|
||||||
|
# des destinataires, utile pour vérifier quelle creation_rules s'applique.
|
||||||
|
sops updatekeys -y stacks/*/secrets.env.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Le format est du **YAML plat** (`CLE: valeur`) : `exec-env` ne sait pas exporter une
|
||||||
|
structure imbriquée.
|
||||||
|
|
||||||
|
### Les pièges à connaître
|
||||||
|
|
||||||
|
- **`sops updatekeys` n'est pas automatique.** Modifier `.sops.yaml` ne rechiffre rien ;
|
||||||
|
les fichiers existants restent lisibles par les anciennes clés seulement.
|
||||||
|
- **Une clé illisible produit un message trompeur.** Mauvais propriétaire ou mauvais
|
||||||
|
`chmod` sur `keys.txt` donne `Recovery failed because no master key was able to
|
||||||
|
decrypt the file` — pas un mot sur les permissions. Vérifier `ls -l` sur la clé
|
||||||
|
avant de suspecter le chiffrement.
|
||||||
|
- **Les composes déclarent `VAR: ${VAR:?}`.** Un `docker compose up` lancé sans
|
||||||
|
`sops exec-env` échoue immédiatement, au lieu de recréer les conteneurs avec des
|
||||||
|
valeurs vides. Ne pas « simplifier » en `VAR: ${VAR}`.
|
||||||
|
- **Perdre les deux copies de la clé age = perdre tous les secrets du dépôt.**
|
||||||
|
Celle du poste vit dans `pass`, à sauvegarder hors ligne. Celle de chioggia est dans
|
||||||
|
un répertoire personnel : elle n'est PAS couverte par le backup tant qu'elle n'est
|
||||||
|
pas ajoutée à `MANUAL_DIRECTORIES` dans `backup.conf`.
|
||||||
|
|
||||||
## DNS
|
## DNS
|
||||||
|
|
||||||
### Gestion des DNS
|
### Gestion des DNS
|
||||||
|
|||||||
86
stacks/vault.opytex.org/docker-compose.yml
Normal file
86
stacks/vault.opytex.org/docker-compose.yml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Vaultwarden — vault.opytex.org
|
||||||
|
# ============================================================
|
||||||
|
# Déploiement :
|
||||||
|
# sops exec-env secrets.env.yaml 'docker compose up -d'
|
||||||
|
#
|
||||||
|
# Le nom du dossier détermine le nom du projet Compose (`vaultopytexorg`,
|
||||||
|
# les points étant retirés) et donc le préfixe des volumes. Le conserver
|
||||||
|
# à l'identique est ce qui permet de reprendre les données existantes.
|
||||||
|
#
|
||||||
|
# Configuration non sensible : env_file (versionné, en clair).
|
||||||
|
# Secrets : secrets.env.yaml (chiffré), injectés par `sops exec-env`.
|
||||||
|
|
||||||
|
services:
|
||||||
|
vaultwarden:
|
||||||
|
# Version en dur, jamais via une variable : c'est ce qui permet à Renovate
|
||||||
|
# de détecter les montées de version et d'ouvrir une PR.
|
||||||
|
image: vaultwarden/server:1.35.7
|
||||||
|
container_name: vaultwarden
|
||||||
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
volumes:
|
||||||
|
- vaultwarden_data:/data
|
||||||
|
env_file:
|
||||||
|
- env_file
|
||||||
|
environment:
|
||||||
|
# Les deux seules valeurs sensibles. `${VAR:?}` fait échouer le
|
||||||
|
# démarrage si `sops exec-env` a été oublié, au lieu de recréer le
|
||||||
|
# conteneur avec des valeurs vides.
|
||||||
|
#
|
||||||
|
# Régénérer le token admin en hash argon2 :
|
||||||
|
# docker run --rm -it vaultwarden/server:1.35.7 /vaultwarden hash --preset owasp
|
||||||
|
ADMIN_TOKEN: ${ADMIN_TOKEN:?}
|
||||||
|
SSO_CLIENT_SECRET: ${SSO_CLIENT_SECRET:?}
|
||||||
|
networks:
|
||||||
|
- traefik-proxy
|
||||||
|
- smtp
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=traefik-proxy
|
||||||
|
|
||||||
|
# --- Router principal (UI + API clients Bitwarden) ---
|
||||||
|
# `priority` explicite : sans elle, Traefik la déduit de la longueur de
|
||||||
|
# la règle — 23 pour Host(`vault.opytex.org`), contre 26 forcés sur le
|
||||||
|
# router /admin. Trois caractères de marge seulement : un domaine plus
|
||||||
|
# long ferait passer ce router devant, et /admin perdrait le forward auth.
|
||||||
|
- traefik.http.routers.vaultwarden.entrypoints=web-secure
|
||||||
|
- traefik.http.routers.vaultwarden.rule=Host(`vault.opytex.org`)
|
||||||
|
- traefik.http.routers.vaultwarden.priority=10
|
||||||
|
- traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.routers.vaultwarden.service=vaultwarden
|
||||||
|
- traefik.http.routers.vaultwarden.middlewares=crowdsec@file
|
||||||
|
|
||||||
|
# --- /admin : CrowdSec + forward auth Authentik ---
|
||||||
|
# Porte de secours si le SSO tombe (SSO_ONLY=true côté env_file).
|
||||||
|
- traefik.http.routers.vaultwarden-admin.entrypoints=web-secure
|
||||||
|
- traefik.http.routers.vaultwarden-admin.rule=Host(`vault.opytex.org`) && PathPrefix(`/admin`)
|
||||||
|
- traefik.http.routers.vaultwarden-admin.priority=26
|
||||||
|
- traefik.http.routers.vaultwarden-admin.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.routers.vaultwarden-admin.service=vaultwarden
|
||||||
|
- traefik.http.routers.vaultwarden-admin.middlewares=crowdsec@file,authentik@file
|
||||||
|
|
||||||
|
# --- Callback Authentik (outpost) ---
|
||||||
|
- traefik.http.routers.vaultwarden-outpost.entrypoints=web-secure
|
||||||
|
- traefik.http.routers.vaultwarden-outpost.rule=Host(`vault.opytex.org`) && PathPrefix(`/outpost.goauthentik.io/`)
|
||||||
|
- traefik.http.routers.vaultwarden-outpost.priority=25
|
||||||
|
- traefik.http.routers.vaultwarden-outpost.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.routers.vaultwarden-outpost.service=vaultwarden-outpost-svc
|
||||||
|
|
||||||
|
- traefik.http.services.vaultwarden.loadbalancer.server.port=80
|
||||||
|
- traefik.http.services.vaultwarden-outpost-svc.loadbalancer.server.url=http://authentik:9000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# ATTENTION : les labels de volume ne s'appliquent qu'à la CRÉATION. Le volume
|
||||||
|
# `vaultopytexorg_vaultwarden_data` existe déjà et n'héritera PAS de ce label :
|
||||||
|
# il faut l'ajouter à MANUAL_VOLUMES dans backup.conf.
|
||||||
|
vaultwarden_data:
|
||||||
|
labels:
|
||||||
|
backup.enable: "true"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-proxy:
|
||||||
|
external: true
|
||||||
|
smtp:
|
||||||
|
external: true
|
||||||
54
stacks/vault.opytex.org/env_file
Normal file
54
stacks/vault.opytex.org/env_file
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# VAULTWARDEN — configuration NON sensible
|
||||||
|
# =============================================================================
|
||||||
|
# Versionné en clair, volontairement : cette configuration doit rester
|
||||||
|
# relisible et diffable sans déchiffrer quoi que ce soit.
|
||||||
|
#
|
||||||
|
# Les deux valeurs sensibles (ADMIN_TOKEN, SSO_CLIENT_SECRET) sont dans
|
||||||
|
# secrets.env.yaml et injectées par `sops exec-env`.
|
||||||
|
#
|
||||||
|
# Ce fichier ne contient QUE ce que le conteneur consomme. Ce qui pilote le
|
||||||
|
# compose lui-même — version d'image, règles Traefik — est écrit en dur dans
|
||||||
|
# docker-compose.yml : `env_file:` n'alimente pas l'interpolation ${...}, et
|
||||||
|
# une version en dur est directement détectée par Renovate.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# --- Domaine ---
|
||||||
|
DOMAIN=https://vault.opytex.org
|
||||||
|
|
||||||
|
# --- Inscriptions ---
|
||||||
|
# false = seuls les comptes invités via admin peuvent s'inscrire
|
||||||
|
SIGNUPS_ALLOWED=false
|
||||||
|
SIGNUPS_VERIFY=true
|
||||||
|
|
||||||
|
# --- SSO OpenID Connect (Authentik) ---
|
||||||
|
# SSO_ONLY=true : désactive le login natif Bitwarden, tout passe par Authentik
|
||||||
|
SSO_ENABLED=true
|
||||||
|
SSO_ONLY=true
|
||||||
|
SSO_AUTHORITY=https://sso.opytex.org/application/o/vaultwarden/
|
||||||
|
SSO_CLIENT_ID=KGGkButSscxfnbo6KDAH5aj7VHMOcvwtELSehGPl
|
||||||
|
SSO_SCOPES=email profile offline_access
|
||||||
|
SSO_PKCE=true
|
||||||
|
|
||||||
|
# Pour faire marcher avec le sso
|
||||||
|
SSO_ORGANIZATIONS_INVITE=true
|
||||||
|
SSO_ORGANIZATIONS_ALL_COLLECTIONS=true
|
||||||
|
|
||||||
|
# --- Base de données ---
|
||||||
|
# SQLite dans le volume vaultwarden_data. Pas de service db, pas de dump :
|
||||||
|
# la sauvegarde repose entièrement sur celle du volume.
|
||||||
|
DATABASE_URL=/data/db.sqlite3
|
||||||
|
|
||||||
|
# --- SMTP (relais interne, réseau `smtp`) ---
|
||||||
|
SMTP_HOST=postfix
|
||||||
|
SMTP_FROM=noreply@opytex.org
|
||||||
|
SMTP_FROM_NAME=Vaultwarden
|
||||||
|
SMTP_SECURITY=off
|
||||||
|
SMTP_PORT=25
|
||||||
|
|
||||||
|
# --- Logs & sécurité ---
|
||||||
|
LOG_LEVEL=warn
|
||||||
|
EXTENDED_LOGGING=true
|
||||||
|
IP_HEADER=X-Forwarded-For
|
||||||
|
LOGIN_RATELIMIT_MAX_BURST=5
|
||||||
|
LOGIN_RATELIMIT_SECONDS=60
|
||||||
26
stacks/vault.opytex.org/secrets.env.yaml
Normal file
26
stacks/vault.opytex.org/secrets.env.yaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
ADMIN_TOKEN: ENC[AES256_GCM,data:PohQt8NlNAlUeWMdvqDdkUmHKJoqkLFE5bF3gZPvvDzxtaLWvtIdnnSkf/3mYJR+CSWjiObM6HgjhZ/WjzeR4324/bWVy5xXwH+o1TGXzXLtCruMrLPZFZDC99tmORa571gZ0DluRPI5EOEcYYQx3AQqggTVow==,iv:o4K3ydAFhFEo/aGhZSYdAWnJCqRGgg9p9zcbhPxzvvM=,tag:zH2t/bxKYi+C/XXU/j8z6Q==,type:str]
|
||||||
|
SSO_CLIENT_SECRET: ENC[AES256_GCM,data:elGebRAUOJemc0DGyCMS8f2+qU5CJZYW1Cu51936m7XNFy0FQZltJrtpSJm952UHtuoYvN9/TGKXFABEPs9R3vfb9EJ3jgV+Jw/Lvwa+HI2UqwQsxRPsJ11ihRAQuQRmIia+nzy0cH2/Kt8sNRxQhw/tR/Up70J10Y8MoXm5jCg=,iv:6D2YpUI0GlqsPDI5BeAu8hFv7LWJDAfJVDmXay14/38=,tag:fxxn9Qi3oJt7qBciG+Ct3g==,type:str]
|
||||||
|
sops:
|
||||||
|
age:
|
||||||
|
- enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOS2lpaE5RNlZmcjNLQWFh
|
||||||
|
YzRJalZaRC9TRVFnUlNGc21WdTJGVkpUMlFJCjNHVFp1V3N1b3Nla0Z2Q1NuaHZQ
|
||||||
|
NXpHSnFTTFpvVVhhakh6bnBFTHBCUzgKLS0tIEQ4NzNha1NaV3cyNTZST2JwaXQw
|
||||||
|
UWVCWWIxM2VkbkhTTWNGVERXV29yZTgKpkNFQGeW9XoS274YMeGEOUgLh1z6oT5k
|
||||||
|
hwQMD/9EYv2npMKGEPQlrKwIUhJly8m+t2TOjUp/1YS37Bd8MbNbBA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
recipient: age18m2zl3pwgw97djshzcf4wmhrqxksl4erqse6edkpxpkg5rcz0ujs8u4c7s
|
||||||
|
- enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxTmhYWloyd0dWZlFJdmpV
|
||||||
|
KzI4WjhQTVBjS3R3NzRvL3B6Z2xyM09qOHlVCmlST0hOenJUb3lSN0lFMk9pOU11
|
||||||
|
aGsvL0JWV0hJanZkN0t2VzFSWUFlT1UKLS0tIGdpY05pcmVqSllqTFk2MmJvZS9I
|
||||||
|
cHRabXdZTjhMeUFObHRyNGN3TjFxZWsKliW1EpTLHAk1INr177ezaMgyYg1gLV8B
|
||||||
|
X5QLAU85NtxNyb6GhYBHJprdM6Cw/RWXQoyfFig7FophJKzzXRHqdA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
recipient: age1fh70nny5hzz8a8g9077kgfca0lhlh3wsjf4fjrv4klx5ucdtpdmsazuqdj
|
||||||
|
lastmodified: "2026-08-12T14:10:42Z"
|
||||||
|
mac: ENC[AES256_GCM,data:URzoDTyKu6YtfV8fbKIK1hbyTBn1SlFOsGhZFekS3IAMS7zKAYJISFczpCvValQdDVcngYBfnPP+lgmXSxQHe6vVdo9TplCI0HIBNEtIqLJwPMXlc6hw9XTUvDnSpoB32mgIOtTNQeIlZ2T2EcuyCL44i202MmMuKFmafPUdEY4=,iv:OlaLkzxbN7isHawJ6iwFEG2L6uXU/WL6fzo9fDZ0qlI=,tag:CK2Dt2MME88Sz59tGzWi6w==,type:str]
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.13.3
|
||||||
Reference in New Issue
Block a user