cleanup: archive stale secrets/ + secrets-issuance/, add Infisical bootstrap script, update SOPS paths to archive/
This commit is contained in:
9
archive/secrets-issuance/backup.service
Normal file
9
archive/secrets-issuance/backup.service
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Snapshot secrets-issuance state to /mnt/library
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/opt/secrets-issuance/secrets-issuance/backup.sh
|
||||
Nice=10
|
||||
TimeoutStartSec=300
|
||||
51
archive/secrets-issuance/backup.sh
Executable file
51
archive/secrets-issuance/backup.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
# secrets-issuance backup — snapshot the per-client age keys + denylist +
|
||||
# admin token to /mnt/library so they survive an LXC-105-only failure.
|
||||
#
|
||||
# Destination is under /mnt/library/.secrets-issuance-backup/ (dot-prefix to
|
||||
# stay out of any indexer's path). Mode 0700 root-owned. Privileged LXCs
|
||||
# that mount /mnt/library would still be able to read it as their root maps
|
||||
# to host root — accept that trade today; an encrypted-tarball variant
|
||||
# (separate offline age key) is a follow-up if the LAN trust model changes.
|
||||
#
|
||||
# Retention: 14 daily snapshots. Each snapshot is the WHOLE /var/lib/
|
||||
# secrets-issuance directory (small — ~few KiB per client), so restore is
|
||||
# just `tar xf <snap> -C /`.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SRC=/var/lib/secrets-issuance
|
||||
DEST=/mnt/library/.secrets-issuance-backup
|
||||
RETAIN=14
|
||||
|
||||
if [ ! -d "$SRC" ]; then
|
||||
echo "[backup] source $SRC missing — nothing to do" >&2
|
||||
exit 0
|
||||
fi
|
||||
if [ ! -d /mnt/library ]; then
|
||||
echo "[backup] /mnt/library not mounted — can't write backup" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install -d -m 0700 "$DEST"
|
||||
chown root:root "$DEST"
|
||||
|
||||
stamp=$(date +%Y%m%d-%H%M%S)
|
||||
out="$DEST/secrets-issuance-$stamp.tar.gz"
|
||||
|
||||
# Include the admin token too (recoverable separately, but trivial size and
|
||||
# saves a step when restoring after total LXC 105 loss).
|
||||
tar czf "$out" \
|
||||
-C / \
|
||||
var/lib/secrets-issuance \
|
||||
$([ -f /etc/secrets-issuance/admin-token ] && echo etc/secrets-issuance/admin-token || true)
|
||||
chmod 0600 "$out"
|
||||
|
||||
# Drop oldest beyond retention.
|
||||
ls -1t "$DEST"/secrets-issuance-*.tar.gz 2>/dev/null \
|
||||
| tail -n +$((RETAIN + 1)) \
|
||||
| xargs -r rm -f
|
||||
|
||||
count=$(ls -1 "$DEST"/secrets-issuance-*.tar.gz 2>/dev/null | wc -l)
|
||||
size=$(du -sh "$out" | cut -f1)
|
||||
echo "[backup] wrote $out ($size); kept $count snapshots"
|
||||
11
archive/secrets-issuance/backup.timer
Normal file
11
archive/secrets-issuance/backup.timer
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daily secrets-issuance backup
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 03:30:00
|
||||
RandomizedDelaySec=10min
|
||||
Persistent=true
|
||||
Unit=secrets-issuance-backup.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
66
archive/secrets-issuance/deploy/deploy.sh
Executable file
66
archive/secrets-issuance/deploy/deploy.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
# Install/update secrets-issuance on LXC 105 (apps). Idempotent.
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR=${REPO_DIR:-/opt/secrets-issuance}
|
||||
|
||||
cd "$REPO_DIR"
|
||||
echo "[deploy] git pull"
|
||||
git pull --ff-only
|
||||
|
||||
echo "[deploy] ensure python venv + deps"
|
||||
if [ ! -d /opt/secrets-issuance/.venv ]; then
|
||||
python3 -m venv /opt/secrets-issuance/.venv
|
||||
fi
|
||||
/opt/secrets-issuance/.venv/bin/pip install --quiet --upgrade pip
|
||||
/opt/secrets-issuance/.venv/bin/pip install --quiet pyyaml
|
||||
|
||||
echo "[deploy] verify dependencies (age, shred)"
|
||||
command -v age-keygen >/dev/null || { echo "age-keygen not installed: apt install -y age" >&2; exit 1; }
|
||||
command -v shred >/dev/null || { echo "shred not installed: apt install -y coreutils" >&2; exit 1; }
|
||||
|
||||
echo "[deploy] state dir"
|
||||
install -d -m 700 /var/lib/secrets-issuance
|
||||
install -d -m 700 /var/lib/secrets-issuance/keys
|
||||
|
||||
echo "[deploy] admin token dir"
|
||||
install -d -m 700 /etc/secrets-issuance
|
||||
if [ ! -s /etc/secrets-issuance/admin-token ]; then
|
||||
head -c 32 /dev/urandom | base64 > /etc/secrets-issuance/admin-token
|
||||
chmod 600 /etc/secrets-issuance/admin-token
|
||||
echo "[deploy] generated admin token at /etc/secrets-issuance/admin-token"
|
||||
fi
|
||||
|
||||
echo "[deploy] install systemd units"
|
||||
install -m 644 secrets-issuance/server.service \
|
||||
/etc/systemd/system/secrets-issuance.service
|
||||
install -m 644 secrets-issuance/deploy/webhook/secrets-issuance-deploy.service \
|
||||
/etc/systemd/system/secrets-issuance-deploy.service
|
||||
install -m 755 secrets-issuance/backup.sh /usr/local/bin/secrets-issuance-backup
|
||||
install -m 644 secrets-issuance/backup.service \
|
||||
/etc/systemd/system/secrets-issuance-backup.service
|
||||
install -m 644 secrets-issuance/backup.timer \
|
||||
/etc/systemd/system/secrets-issuance-backup.timer
|
||||
|
||||
if [ ! -d /opt/homelab-context/.git ]; then
|
||||
echo " /opt/homelab-context is not a git clone — run bootstrap.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl daemon-reload
|
||||
if systemctl is-active --quiet secrets-issuance.service; then
|
||||
systemctl restart secrets-issuance.service
|
||||
fi
|
||||
if systemctl is-active --quiet secrets-issuance-deploy.service; then
|
||||
systemctl restart secrets-issuance-deploy.service
|
||||
fi
|
||||
|
||||
echo "[deploy] daemon-reload + enable backup timer"
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now secrets-issuance-backup.timer
|
||||
|
||||
echo "[deploy] done"
|
||||
echo "First-time enable:"
|
||||
echo " systemctl enable --now secrets-issuance.service secrets-issuance-deploy.service"
|
||||
echo "Webhook first-time setup:"
|
||||
echo " $REPO_DIR/secrets-issuance/deploy/webhook/install.sh"
|
||||
30
archive/secrets-issuance/deploy/webhook/install.sh
Executable file
30
archive/secrets-issuance/deploy/webhook/install.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
# First-time setup for the secrets-issuance deploy webhook. Generates a
|
||||
# secret, installs the systemd unit, and starts it.
|
||||
set -euo pipefail
|
||||
|
||||
SECRET_DIR=/etc/secrets-issuance-deploy
|
||||
SECRET=$SECRET_DIR/secret
|
||||
UNIT=secrets-issuance-deploy.service
|
||||
|
||||
install -d -m 700 "$SECRET_DIR"
|
||||
if [ ! -s "$SECRET" ]; then
|
||||
head -c 32 /dev/urandom | base64 > "$SECRET"
|
||||
chmod 600 "$SECRET"
|
||||
echo "[install] generated webhook secret at $SECRET"
|
||||
fi
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now "$UNIT"
|
||||
systemctl status "$UNIT" --no-pager | head -10
|
||||
|
||||
cat <<EOF
|
||||
|
||||
[install] webhook listening on :9821/deploy.
|
||||
Configure Gitea (dtoro/Homelab-Docs → Settings → Webhooks → Add Webhook → Gitea):
|
||||
Target URL: http://<lxc-105-mesh-ip>:9821/deploy
|
||||
HTTP Method: POST
|
||||
Content-Type: application/json
|
||||
Secret: $(cat $SECRET)
|
||||
Trigger: Push events
|
||||
EOF
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Gitea deploy webhook for dtoro/Homelab-Docs → secrets-issuance
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /opt/secrets-issuance/secrets-issuance/deploy/webhook/webhook.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
91
archive/secrets-issuance/deploy/webhook/webhook.py
Executable file
91
archive/secrets-issuance/deploy/webhook/webhook.py
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deploy webhook for dtoro/Homelab-Docs → secrets-issuance on LXC 105.
|
||||
|
||||
Listens on 0.0.0.0:9821/deploy. Validates Gitea HMAC, runs deploy.sh.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
BIND_HOST = "0.0.0.0"
|
||||
BIND_PORT = 9821
|
||||
SECRET_PATH = "/etc/secrets-issuance-deploy/secret"
|
||||
DEPLOY_CMD = ["/opt/secrets-issuance/secrets-issuance/deploy/deploy.sh"]
|
||||
TARGET_REF = "refs/heads/main"
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("secrets-issuance-deploy")
|
||||
_deploy_lock = threading.Lock()
|
||||
|
||||
|
||||
def load_secret() -> bytes:
|
||||
with open(SECRET_PATH, "rb") as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def log_message(self, fmt, *args):
|
||||
log.info("%s - %s", self.address_string(), fmt % args)
|
||||
|
||||
def _reply(self, status: int, msg: str = "") -> None:
|
||||
self.send_response(status)
|
||||
self.send_header("Content-Type", "text/plain")
|
||||
self.end_headers()
|
||||
if msg:
|
||||
self.wfile.write(msg.encode())
|
||||
|
||||
def do_POST(self) -> None:
|
||||
if self.path != "/deploy":
|
||||
self._reply(404, "not found")
|
||||
return
|
||||
length = int(self.headers.get("Content-Length", "0"))
|
||||
body = self.rfile.read(length) if length else b""
|
||||
sig = self.headers.get("X-Gitea-Signature", "")
|
||||
secret = load_secret()
|
||||
expected = hmac.new(secret, body, hashlib.sha256).hexdigest()
|
||||
if not hmac.compare_digest(expected, sig):
|
||||
log.warning("signature mismatch")
|
||||
self._reply(403, "bad signature")
|
||||
return
|
||||
try:
|
||||
payload = json.loads(body)
|
||||
except json.JSONDecodeError:
|
||||
self._reply(400, "bad json")
|
||||
return
|
||||
if payload.get("ref", "") != TARGET_REF:
|
||||
self._reply(204)
|
||||
return
|
||||
if not _deploy_lock.acquire(blocking=False):
|
||||
self._reply(202, "already running")
|
||||
return
|
||||
try:
|
||||
result = subprocess.run(DEPLOY_CMD, capture_output=True, text=True, timeout=180)
|
||||
if result.returncode != 0:
|
||||
log.error("deploy failed: %s\n%s", result.stdout, result.stderr)
|
||||
self._reply(500, "deploy failed")
|
||||
return
|
||||
self._reply(204)
|
||||
finally:
|
||||
_deploy_lock.release()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not os.path.exists(SECRET_PATH):
|
||||
log.error("secret file missing: %s", SECRET_PATH)
|
||||
return 1
|
||||
server = HTTPServer((BIND_HOST, BIND_PORT), Handler)
|
||||
log.info("listening on %s:%d", BIND_HOST, BIND_PORT)
|
||||
server.serve_forever()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
266
archive/secrets-issuance/server.py
Executable file
266
archive/secrets-issuance/server.py
Executable file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Secrets-issuance HTTP service.
|
||||
|
||||
Lives on LXC 105 alongside the MCP server. Mesh-bound (nftables restricts to
|
||||
Netbird + Tailscale subnets). Identifies callers by source mesh IP and returns
|
||||
the per-client age private key.
|
||||
|
||||
State:
|
||||
/var/lib/secrets-issuance/keys/<hostname>.key (private, 0600 root)
|
||||
/var/lib/secrets-issuance/keys/<hostname>.pub (public)
|
||||
/var/lib/secrets-issuance/denylist.txt (one hostname per line)
|
||||
|
||||
Inventory lookup:
|
||||
/opt/homelab-context/inventory.yaml — the issuance server is itself a
|
||||
homelab-context client, so it sees inventory updates within 5 min.
|
||||
|
||||
Endpoints:
|
||||
POST /issue body: {"hostname": "..."} -> raw age private key
|
||||
POST /revoke body: {"hostname": "..."} -> shred key + add to denylist
|
||||
(called by 'homelab client remove')
|
||||
GET /health
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import secrets as sysrandom
|
||||
import subprocess
|
||||
import sys
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
BIND_HOST = "0.0.0.0"
|
||||
BIND_PORT = 9820
|
||||
CONTEXT_DIR = Path(os.environ.get("HOMELAB_CONTEXT_DIR", "/opt/homelab-context"))
|
||||
STATE_DIR = Path(os.environ.get("SECRETS_ISSUANCE_STATE", "/var/lib/secrets-issuance"))
|
||||
KEYS_DIR = STATE_DIR / "keys"
|
||||
DENYLIST = STATE_DIR / "denylist.txt"
|
||||
ADMIN_TOKEN_PATH = Path(os.environ.get("SECRETS_ISSUANCE_ADMIN_TOKEN",
|
||||
"/etc/secrets-issuance/admin-token"))
|
||||
MESH_SUBNETS = [
|
||||
ipaddress.ip_network(s.strip())
|
||||
for s in os.environ.get("MESH_SUBNETS", "100.64.0.0/10").split(",")
|
||||
if s.strip()
|
||||
]
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("secrets-issuance")
|
||||
|
||||
|
||||
def load_inventory() -> dict:
|
||||
return yaml.safe_load((CONTEXT_DIR / "inventory.yaml").read_text())
|
||||
|
||||
|
||||
def known_hosts() -> dict[str, dict]:
|
||||
return load_inventory().get("hosts", {})
|
||||
|
||||
|
||||
def host_by_mesh_ip(ip: str) -> str | None:
|
||||
"""Resolve a mesh source IP to a hostname per inventory."""
|
||||
for name, entry in known_hosts().items():
|
||||
mesh = entry.get("mesh", {})
|
||||
for proto in ("netbird", "tailscale"):
|
||||
p = mesh.get(proto, {})
|
||||
if not isinstance(p, dict):
|
||||
continue
|
||||
if p.get("ip") == ip:
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
def in_mesh(ip: str) -> bool:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
return any(addr in subnet for subnet in MESH_SUBNETS)
|
||||
|
||||
|
||||
def is_denied(hostname: str) -> bool:
|
||||
if not DENYLIST.exists():
|
||||
return False
|
||||
return hostname in {line.strip() for line in DENYLIST.read_text().splitlines() if line.strip()}
|
||||
|
||||
|
||||
def deny(hostname: str) -> None:
|
||||
DENYLIST.parent.mkdir(parents=True, exist_ok=True)
|
||||
existing = set()
|
||||
if DENYLIST.exists():
|
||||
existing = {line.strip() for line in DENYLIST.read_text().splitlines() if line.strip()}
|
||||
existing.add(hostname)
|
||||
DENYLIST.write_text("\n".join(sorted(existing)) + "\n")
|
||||
|
||||
|
||||
def generate_key(hostname: str) -> tuple[Path, str]:
|
||||
"""Generate an age keypair for hostname; return (priv_path, pubkey)."""
|
||||
KEYS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
priv = KEYS_DIR / f"{hostname}.key"
|
||||
pub_path = KEYS_DIR / f"{hostname}.pub"
|
||||
proc = subprocess.run(["age-keygen"], capture_output=True, text=True, check=True)
|
||||
priv.write_text(proc.stdout)
|
||||
priv.chmod(0o600)
|
||||
# Extract the public key. age-keygen writes "# public key: ageXXX" line.
|
||||
pubkey = ""
|
||||
for line in proc.stdout.splitlines():
|
||||
if line.startswith("# public key:"):
|
||||
pubkey = line.split(":", 1)[1].strip()
|
||||
if not pubkey:
|
||||
priv.unlink(missing_ok=True)
|
||||
raise RuntimeError("age-keygen did not emit a public key")
|
||||
pub_path.write_text(pubkey + "\n")
|
||||
log.info("generated new age key for %s (pubkey: %s)", hostname, pubkey)
|
||||
return priv, pubkey
|
||||
|
||||
|
||||
def existing_pubkey(hostname: str) -> str | None:
|
||||
pub_path = KEYS_DIR / f"{hostname}.pub"
|
||||
if pub_path.exists():
|
||||
return pub_path.read_text().strip()
|
||||
return None
|
||||
|
||||
|
||||
def read_admin_token() -> str | None:
|
||||
if ADMIN_TOKEN_PATH.exists():
|
||||
return ADMIN_TOKEN_PATH.read_text().strip()
|
||||
return None
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def log_message(self, fmt, *args):
|
||||
log.info("%s - %s", self.address_string(), fmt % args)
|
||||
|
||||
def _reply(self, status: int, body: bytes | str = b"",
|
||||
content_type: str = "text/plain") -> None:
|
||||
self.send_response(status)
|
||||
self.send_header("Content-Type", content_type)
|
||||
if isinstance(body, str):
|
||||
body = body.encode()
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
if body:
|
||||
self.wfile.write(body)
|
||||
|
||||
def _read_json(self) -> dict | None:
|
||||
length = int(self.headers.get("Content-Length", "0"))
|
||||
if not length:
|
||||
return {}
|
||||
body = self.rfile.read(length)
|
||||
try:
|
||||
return json.loads(body)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
def do_GET(self) -> None:
|
||||
if self.path == "/health":
|
||||
self._reply(200, "ok")
|
||||
return
|
||||
self._reply(404, "not found")
|
||||
|
||||
def do_POST(self) -> None:
|
||||
client_ip = self.client_address[0]
|
||||
if not in_mesh(client_ip):
|
||||
log.warning("rejecting non-mesh source %s", client_ip)
|
||||
self._reply(403, "non-mesh source")
|
||||
return
|
||||
|
||||
if self.path == "/issue":
|
||||
self._handle_issue(client_ip)
|
||||
elif self.path == "/revoke":
|
||||
self._handle_revoke()
|
||||
else:
|
||||
self._reply(404, "not found")
|
||||
|
||||
def _handle_issue(self, client_ip: str) -> None:
|
||||
payload = self._read_json()
|
||||
if payload is None:
|
||||
self._reply(400, "bad json")
|
||||
return
|
||||
hostname = (payload.get("hostname") or "").strip()
|
||||
if not hostname:
|
||||
self._reply(400, "hostname required")
|
||||
return
|
||||
|
||||
# Cross-check: does inventory list this hostname, and does its mesh IP
|
||||
# match the caller (if known)?
|
||||
hosts = known_hosts()
|
||||
if hostname not in hosts:
|
||||
log.warning("unknown hostname in /issue: %s (from %s)", hostname, client_ip)
|
||||
self._reply(403, f"unknown hostname: {hostname} (run 'homelab client add {hostname}' first)")
|
||||
return
|
||||
|
||||
if is_denied(hostname):
|
||||
log.warning("denied hostname %s tried to issue (from %s)", hostname, client_ip)
|
||||
self._reply(403, "hostname on denylist (was removed); operator must clear before re-enrolling")
|
||||
return
|
||||
|
||||
inv_ip = host_by_mesh_ip(client_ip)
|
||||
if inv_ip is not None and inv_ip != hostname:
|
||||
log.warning("source %s maps to inventory host %s but body claims %s",
|
||||
client_ip, inv_ip, hostname)
|
||||
self._reply(403, "source IP / hostname mismatch")
|
||||
return
|
||||
if inv_ip is None:
|
||||
# Inventory has no IP for this hostname yet (first-bootstrap state).
|
||||
# Accept the call but log it loudly so the operator backfills.
|
||||
log.info("issuing for %s (source %s) — inventory has no mesh IP yet",
|
||||
hostname, client_ip)
|
||||
|
||||
priv = KEYS_DIR / f"{hostname}.key"
|
||||
existing_pub = existing_pubkey(hostname)
|
||||
if priv.exists() and existing_pub:
|
||||
# Re-issue: client lost its key but is still authorized. Return
|
||||
# the existing key.
|
||||
log.info("returning existing key for %s", hostname)
|
||||
self._reply(200, priv.read_text())
|
||||
return
|
||||
|
||||
# Fresh provisioning.
|
||||
priv, pubkey = generate_key(hostname)
|
||||
# Tell the operator (via response body comment) which pubkey to commit.
|
||||
body = priv.read_text()
|
||||
body += f"\n# operator: add this pubkey to inventory.yaml under hosts.{hostname}.age_pubkey:\n"
|
||||
body += f"# {pubkey}\n"
|
||||
self._reply(200, body)
|
||||
|
||||
def _handle_revoke(self) -> None:
|
||||
token = self.headers.get("X-Admin-Token", "")
|
||||
expected = read_admin_token()
|
||||
if not expected or token != expected:
|
||||
self._reply(403, "admin token required")
|
||||
return
|
||||
payload = self._read_json()
|
||||
if payload is None:
|
||||
self._reply(400, "bad json")
|
||||
return
|
||||
hostname = (payload.get("hostname") or "").strip()
|
||||
if not hostname:
|
||||
self._reply(400, "hostname required")
|
||||
return
|
||||
priv = KEYS_DIR / f"{hostname}.key"
|
||||
pub = KEYS_DIR / f"{hostname}.pub"
|
||||
for p in (priv, pub):
|
||||
if p.exists():
|
||||
# shred-then-unlink
|
||||
subprocess.run(["shred", "-u", str(p)], check=False)
|
||||
if p.exists():
|
||||
p.unlink(missing_ok=True)
|
||||
deny(hostname)
|
||||
log.warning("revoked %s (shredded keys, added to denylist)", hostname)
|
||||
self._reply(200, f"revoked {hostname}\n")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
KEYS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
server = HTTPServer((BIND_HOST, BIND_PORT), Handler)
|
||||
log.info("listening on %s:%d (context=%s, state=%s)",
|
||||
BIND_HOST, BIND_PORT, CONTEXT_DIR, STATE_DIR)
|
||||
server.serve_forever()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
23
archive/secrets-issuance/server.service
Normal file
23
archive/secrets-issuance/server.service
Normal file
@@ -0,0 +1,23 @@
|
||||
[Unit]
|
||||
Description=Homelab secrets-issuance (per-client age key provisioning)
|
||||
After=network-online.target homelab-context-sync.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/secrets-issuance
|
||||
Environment=HOMELAB_CONTEXT_DIR=/opt/homelab-context
|
||||
Environment=SECRETS_ISSUANCE_STATE=/var/lib/secrets-issuance
|
||||
Environment=MESH_SUBNETS=100.122.0.0/16,100.64.0.0/10,192.168.8.0/24
|
||||
ExecStart=/opt/secrets-issuance/.venv/bin/python /opt/secrets-issuance/secrets-issuance/server.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
NoNewPrivileges=true
|
||||
ReadOnlyPaths=/opt/homelab-context /opt/secrets-issuance
|
||||
ReadWritePaths=/var/lib/secrets-issuance
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
63
archive/secrets-sops-backup/README.md
Normal file
63
archive/secrets-sops-backup/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# secrets/
|
||||
|
||||
SOPS-encrypted YAML files. The plaintext lives only in transit and in the
|
||||
operator's head — committed files are always ciphertext.
|
||||
|
||||
## Conventions
|
||||
|
||||
- One file per logical grouping (e.g. `gitea-tokens.yaml`, `webhook-hmacs.yaml`,
|
||||
`api-keys.yaml`).
|
||||
- Recipients are declared in `../.sops.yaml` by path-regex, not per-file.
|
||||
- The plaintext schema inside each file is free-form YAML; the consumer code
|
||||
decides what it expects (e.g. `gitea-tokens.yaml` contains
|
||||
`{"<host>": "ghp_xxx"}`).
|
||||
|
||||
## How to add a secret
|
||||
|
||||
```bash
|
||||
# 1. Decide which clients should be able to decrypt it; edit ../.sops.yaml to
|
||||
# list their age public keys for the new path_regex.
|
||||
# 2. Create the plaintext, encrypt in place:
|
||||
sops -e --in-place secrets/my-thing.yaml
|
||||
# 3. Commit + push. The 5-min sync propagates to every recipient.
|
||||
```
|
||||
|
||||
## How to consume a secret
|
||||
|
||||
```bash
|
||||
# On any client that's a recipient:
|
||||
homelab secret my-thing # prints plaintext
|
||||
# Or programmatically:
|
||||
sops -d /opt/homelab-context/secrets/my-thing.yaml
|
||||
```
|
||||
|
||||
The `mcp` tool `list_my_secrets(caller_pubkey)` returns the names of secrets
|
||||
the caller can decrypt. The MCP server never reads plaintext — decryption
|
||||
stays client-side.
|
||||
|
||||
## Granting / revoking access
|
||||
|
||||
To grant a new recipient: edit `../.sops.yaml` to add their age pubkey, then
|
||||
re-key every affected file:
|
||||
|
||||
```bash
|
||||
sops updatekeys -y secrets/my-thing.yaml
|
||||
```
|
||||
|
||||
To revoke: remove the recipient from `../.sops.yaml` and `sops updatekeys` —
|
||||
but remember this only protects future ciphertext. Past plaintext the client
|
||||
already decrypted is gone from your control. Rotate the underlying credential
|
||||
if compromise is suspected.
|
||||
|
||||
`homelab client remove <name>` does the recipient removal + `updatekeys` for
|
||||
you, and prints the rotation checklist as a follow-up.
|
||||
|
||||
## hello.yaml — bootstrap decrypt test
|
||||
|
||||
`secrets/hello.yaml` is encrypted to every enrolled client. Used by Phase 3a
|
||||
verification to confirm the end-to-end decrypt path works on a freshly-
|
||||
bootstrapped machine. Content is intentionally trivial:
|
||||
|
||||
```yaml
|
||||
greeting: hello from the homelab
|
||||
```
|
||||
80
archive/secrets-sops-backup/gitea-pat.yaml
Normal file
80
archive/secrets-sops-backup/gitea-pat.yaml
Normal file
@@ -0,0 +1,80 @@
|
||||
user: ENC[AES256_GCM,data:e6Dj7CE=,iv:Bf4tvdWQr0vdF3PShG2cRTryxqRC5rN6UdGd1qwFg7g=,tag:22rrPMAF97C2GUhRGgutOw==,type:str]
|
||||
token: ENC[AES256_GCM,data:98bMZO2t0qgOO/rTYmyDBZdRTHu8iPgUsctS4dc0B2BCML6tBxBNhA==,iv:oXehq2FJy1pQxzK8M3LqDtjpoFbdfAPvYcKhygwf51A=,tag:RMYdimBwxw247OARznBu3g==,type:str]
|
||||
scopes:
|
||||
- ENC[AES256_GCM,data:S3nz8buLTp1PCHpf8F3Y,iv:IkHU+QwIlsxrNrGEu3tRS0zlnuCyp9Fs8ILxUaZrabw=,tag:Ufd+MVYXAAtbSbfvRi3jCg==,type:str]
|
||||
- ENC[AES256_GCM,data:/UG/8LQD21l/Ca0wfEodKw==,iv:f3tW1ZNLLkzE5Oy8acQ6GfyDvzEH3++kp8duBOI8+Is=,tag:IgfhvEaX9EGyPHhr6FSAjQ==,type:str]
|
||||
note: ENC[AES256_GCM,data:vRDjHXh5fYlhwJ4LyS/hcqMGdS+Z/D1W2oH6NaJak/XRUr+nFVQqcXSH/zUgqEBWNrMv6j8n2Ouxex/q2mr+p3Ds1jfThJTobX/MFqMkxvxk0TmB7ucui7CDa4mcVgSo3wyhcaba2o3erObByZ3o2uay3mmqzAUdo/wy2bCgQTkTG9/xqVSf3EYuR1j/A+SAzSvpsOORMNUjC5NRP1DUL+3hZkPXsfkXuy7sV6T2vF+oOrbEI0WxR8o4/JcF1B/4ezFAJ+f+nukCMz6eSCwur/wHffbcYa8mtD6BIRuV1pTd/Zob8arhe2ir19MB0fR27E3UkcCwyCkQ+QWuGzisvciCPtwR4JidH7xOpPW+4AK3vqEjmpEeMSWlyxbZadQFuSi8f3bPH+8eB2h/UFvT0BU6xw==,iv:r2dUnKyUWhyafIeDIjHOdqh+PNFzGtVZfcHc2TAhvvI=,tag:Cmg6esU4mQ8/9UbIQQL/gw==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIa05CSXpkcUsvbFYxcnNM
|
||||
b2o1ejFFRVdUaVhzYURPRm5oTnBxUXgwYWtVCjY4L3hvcy9NMHpDM1Q0WGJMZ3Mv
|
||||
aXhVek5uWkIrdEI3cFl2N2NUUjc2ckkKLS0tIFpSRDZqeE5VM3ZsNGdYTHRWcUQw
|
||||
QkhSSVZkVm05TEp1RmFNdXFydVhpQXMKgpF5MlP8wkOLkbCxozmtG+axD32rs/SA
|
||||
Oc1Eo/ggRT54EGCFoMPITnzsC0ELqZOnOjpZi2FU/Vh1pEhBXCOwJA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxeXFyQW8zb09QRm94M1da
|
||||
ejBkbCtzMkQyaVF3RkxscWk1cDBiQW5SdmxrCjRCa1UvZ2VhQ3Rqb3pXcjN2d2dS
|
||||
VlROVHh0ZnRWbGVhdDYvY0ZDWUUxNE0KLS0tIE9zT2VvZ2ZjWnBNYWxTaWtFVjkv
|
||||
S3VIdUgydit1Wk9kMnd0ZDB5NE1iaVEKLvVToIX8zc6lXi0Ro59copkeCl19Nv3H
|
||||
obbUoW6v7tVnM/Khiw0DhHzzhLbgLFfSm/e6h/8o4Wok8CSHvp35iQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlMy9nek80M3k2cTNPQlRG
|
||||
WE42aGtsWllkaHZjOFg2b3ROdmZuWUNLQXhBCmNZZC9mSXNkU2xLUnp3MDNVZ1hn
|
||||
YTA5TzRHNHJMN0xqMDhhZGxxSytpZDQKLS0tIHVwdHNRczZaLzRrK3QyLy9qU0E2
|
||||
anExUUFlRzdiY3dFbU5VMW83K2I0a1kKFaQVQH6mjh7I4r9UNk1Uvw2hyYnwLGSJ
|
||||
Z9PxVOucYfODHoWMq1jWcW2vQQO5G8dWtE3S97yoqNVteAf8nmH4xw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDaHZmTzkrTjhETkttOWho
|
||||
azcxb0lTNVd3aW5iZzBxUkNZQjE4T1EvblYwCnBSdFN3WFhBYWRFeTlSQk1MbkQx
|
||||
bm9ORkJRbHhJLzJva1pvSDF0SU5pZlUKLS0tIEZLSmxBdmE0RHQ1Qk05MnBnUVlL
|
||||
aWtDcTZHZ1BWWmtLNHEveEE2K3F0eVUKO5Yi/DEmFL5wJDL3XVFlRfWTjPTNVFKz
|
||||
G6NPwO5qeC0kFCOZWtKU8+6f0m3tw2ZsNylntnCZbRScbshR9QhVEg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvSkEvNm1NZFgxRlJaeVZu
|
||||
TCs0L3c0UEpTWmlDRnV3QnkxUWYxTnFIZjJnCnlOMm5lOS9MbmJwekNoczZsZFFa
|
||||
R3JKYWZoNjNLTnhscXp2UGVrOHBsUkUKLS0tIEx1K1lYODBuczllRkl5U0JTTGpC
|
||||
RC9Mc2NyTUhPekJnY0VWSDVpZzlhVjgK1bFDQaqJf0Nz+IjTPPMgi5DxbW/ln5Po
|
||||
SABlg3INzGh2pyAgOroBEKr0Dl2dU23/hX4uixP2YxkJRkbamU3+6w==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHQUE3RHhGS01SYzMrZ0lK
|
||||
elR4UEQvT0w1anVjeHNKN1JERGxwcUoxc0c0CmNLVEFVMWFDdGNlM3VLcDFueDNa
|
||||
SW10cWhlWk5KMWtYMnFXTmJYbFQ1RU0KLS0tIGNHVWloaEtIeEVoREMvT3lqT3dS
|
||||
ejZwcVhWS1NmNVByRGY0ekM4V2lKQUEKOWxj1HmtSPoungbzek2Z0okRS59A2QwY
|
||||
/zXoEXgIskP9Rjr4zf29cE4RYSYW2UVU4aeE9fVA3Y+NrylGi5I3ng==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzYWQ4RHpNWEpKNWpMRGll
|
||||
blpTUWpmV1lOZ3lKdUwwL2o4TzNMY3lnV0NjCmRUNEQ2OU93QXdhRURveXY1bE5S
|
||||
alAwNzVGRVZtaXg0TU9xclJZMGNvSlUKLS0tIEM1SmY3TUhkN0FRb1JUMDFKVndN
|
||||
a0dIZlZ5ODZBMGtEZmxiTFRNUEdKQ1EKXYAaezVE0H1viKEo49D60i/zqHv8xZsg
|
||||
8c4oVRGVW3wLzb4Dd4O2MvLpNpy1yRgd1pN+qUB/Pft2G3IwWWw3BA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-20T16:24:18Z"
|
||||
mac: ENC[AES256_GCM,data:d3qcjEqpPIFeut634ImvISJLdit0MQWcdFYomrsqriqJ0NUKdyq3XCk85+vvPvDWikB4WEApk6HMXssSY8mhDci95q5Ssmr+JRAhLebMZjs9yXUf9A7vXpNFswmdldu3CKBiDrhm2GE08qUfsCkcf3jjihEqsfHhxMuWSd5fZpU=,iv:VZgvZ3bBArRChgRX38U6/43XoX5aTnts7Kd4S9spBPk=,tag:/5oOw5yvulvFT6KXv2zj0g==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
76
archive/secrets-sops-backup/hello.yaml
Normal file
76
archive/secrets-sops-backup/hello.yaml
Normal file
@@ -0,0 +1,76 @@
|
||||
greeting: ENC[AES256_GCM,data:eNMtZcpv04PHl/lGBZGBp7xAcO/5Sw==,iv:A4jtwePX0p/eDTP5+ztfV6gege9RwdPfLBY2N+DPfx0=,tag:MU8HpvLt/PUrNMw7BNUVIg==,type:str]
|
||||
phase: ENC[AES256_GCM,data:oIeo+kmkwaMDQrf10iY2PqOXJnq+hCIwOROQ86xgpkU7EJEeJKFssrNgbvQPHhIRZUZblQg9ZPamiZixQbYSTOXQSTWWu2FOnu6XFb24wCay,iv:TYKqoHcN+V324/roNopYhkwRpmv3cPrTFcmni5TajYQ=,tag:g8DXqEyORbQZ9B7PaFWgOQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzUFNydDVhMEpvMVNyMTk5
|
||||
N3ZiTGZibmNhWDBMajlOQW5BVGllQ0lWcnlVCmJsQVZ1Z0c0WTNIYWFYbFk2R2lZ
|
||||
RkUyR0M1Tjg0SUxhY0RrU1FnYlNrbFUKLS0tIFdSWlllREpVUnVOVTh6dTRxenhk
|
||||
WndBSEdid1hlRUZmMTl0a3d5N2lRbncKIkcQC2JTSxGES+DOzLCT3G8KrrIO2nUK
|
||||
jjovwCLP1XxoTwXfv85nMPNwsJkCddg5sh/FXGTDglSBHfZ94RFeYQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJZEptakpiYnRoaDNhek9E
|
||||
c1NKSVNsZDBlbWhabTJ4NkM4RmNNTXVTS1ZNCmJ1RHUzNlZ3aGdsUHZCaXNEeTBM
|
||||
TTIzNHY0Nk1sOGRCQkVyRlc5Y0s1VHcKLS0tIGx3NDJDRmZiYzdTTUc1clRkamtm
|
||||
b2gxTGpZbEdVUS9oMVUxODkrVERqUncKTsngnt9pySyKqxhBOlDFbtqaO72Bq3Z4
|
||||
CNVVigYh73xWK8CHVU+nj0QG+VFnRr8FJhRxpxJw/ZfFpHvpSKSxtg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5eDY2V2U1YkJhc1ZwSVRM
|
||||
d1NnQ1ZvUHpVbG5tWkoxT2N2M3ZRYml4NlJJCnJDVmV5bzdvTS9FZGlxY3pheDBu
|
||||
VFRWZ3pCdUFFV1R1dUtTSmMzcHdrUlkKLS0tIGRHcThteTlOUUMxOXZSS2tlVVpt
|
||||
ZGl0VkJpSlVxQ2pzaEdmQnpmY254L3cKQ51TCwCzDpN8T5qsozW2ScSiBvvERLg6
|
||||
p2128rIityffiCCUoOCUt8gHdqDBBivruVnofINlp7fN7m3xss6NcQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNbHBLU1pJRXlIWFN0MzAr
|
||||
bnZUd3NoYXVsMmxKNkR0ODBlcFlDRkdTUm1zCks4Q042Y2dCTVV5OEJTS2REQm41
|
||||
b3M4RVM5Vk8zSEIyYTF4aDdJWDNKbHMKLS0tIGE1Tzcrb1VTUExieFRxNDJPSFFa
|
||||
SkI2NTMxdUhraWduSHpLMmh3SDdZaGsKzjZvj1Y9UlEoGZp6FJoOxW6r7sX7g7Jj
|
||||
FpK4QiUhiKztNeUIdx7abte2rKSFqyjLdq45SKdqi4Bg1iiuqh6Umw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtNEhuY2Rrb2hzWTVFQlZM
|
||||
VHJURUJxQSt2dVhqbTVZNVBBWVg3ekpBeFJ3Cnd6bmNEL0RTNjc3UXEvOFA2ZVhF
|
||||
dThNNkd5R3c5L2JKRTA1NDhNRXYxU2MKLS0tIFFnVU9TK1JHQnhRRy9CNWpzZjdy
|
||||
OFZYb3E4YlB5VlcvRVA0TFM0bFFLeTgKSFvNi+X2CI2zvWBdcctv26ocG07Xvtw1
|
||||
mstp3VQUhwMYe63YVl/liU/JnBSC9EN0Q0/8WdsVUycm2HEIRBnIUg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1Z3FRVzZWUU1UN0JUMGdw
|
||||
T05yaU05Q2wzc0xpTGNkSThOY2U2YkNSQnl3CllYbUlwN3FxSHpaM2Ewa04vR09j
|
||||
RlF5a25NSW8wc1ZteEpyR2NJdWl0ZWMKLS0tIFBQQmJEYWpNaTlTMXdqdFh0OCt1
|
||||
eDF0U3oxdXdQdFpNMjBBRGNFZnY5ZWcKvYjG2p6/MEd7VV5/K7tnNTqZC3R1BsVd
|
||||
oAHqKo4+mcqyRaS0UYoSfiDo6XyTUXNk8n3zRGMlKuSaTqGVpv/PMA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPU3BuZ2ZERWJDZU0vdHVi
|
||||
cmxwTFc2WXE1SjhBdmhnSUJIZVRrb1FHRnpzCkdiOHdFZ3NCVjM0SVN4cVZ3MkJn
|
||||
NzFiV0tFMzJPUDB1Y21mSDZGOGVzOUkKLS0tIDFUVEpUd0NETy94KzlCSU5UTG5G
|
||||
K1Blb2EySlhnQnFxUm1zNXNOMDRES28KXeFuHSpm3VrdVdzK1cMiFQQEzvjko/Xq
|
||||
Bsfu3mZ7fNlHW5szZtAFsyxPyvaELCPGprKmQFzUn68GFtphJxmXww==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-20T14:39:14Z"
|
||||
mac: ENC[AES256_GCM,data:CAoIW1sJkIbNoXMaVlXwfHdREzATNqDKpSN5ecaSiRARThB0/tcLNbkazXKyV1Rx5b+b3s+yCpBzwmNnH6ytaCzG5pKPcnfd6YJq7C5rm/EQHyaVGrPdBsSfqiz4yBXKClej//v3+qkD5Ls2PMppv5KmqigOKNQC/ot3ht2c7lM=,iv:eOmDDclksr9f9CDu6dJprSFwlWuoggzVFjyfqWf5Uhg=,tag:IU3sMlIi92ohM86zOIJdAg==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
50
archive/secrets-sops-backup/hermes-house-users.yaml
Normal file
50
archive/secrets-sops-backup/hermes-house-users.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
members:
|
||||
"+491726924525": ENC[AES256_GCM,data:RA==,iv:FLOi3HmBPFtS8BAqPR9Epihs868OwAWs1t2LGjg9kGU=,tag:bf8PGNipOeepfnzHUE5KMA==,type:str]
|
||||
"+4917622791635": ENC[AES256_GCM,data:Mw==,iv:onlvwM9nlAlXnL6f+BTRZlOi4+C6pryyiay421GY9WA=,tag:KiYrTcRlBNbB5mYYlb7GHg==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmYW9VS2FLb1BTVTk2OFZZ
|
||||
Wnd1U2tnanR5N2d6SmNqdFYrekpzZTRMRGpZCjZhREcrY29qc2pFZUhnQnc3U1dE
|
||||
aU5Kd1Robkk4OVd2M0ZkalBWMlQzR0EKLS0tIDJOTjkwR2htaDhDR0hqN0R4cCtB
|
||||
dldLSHBOVjZxNVFZVUtkZm1INkdvM1kK2iaqr75MZ+4QlWaNOjH1X11zHbi4Ahy0
|
||||
ely7Fakx6wSoQtl803q6UyNJyIqCOavJKoLQw48FhAl2yGmv9KsvrQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4eXFvMjZXVWkvZnNLSGhS
|
||||
eWJ3Q0M0QzlnYnJ2dTVhZGRtZ3Y4Ylh5NHc0CkVTRnNLa0NzK2czcjFSSmgxQ081
|
||||
ZVo2MzR4S2ZlY1AxZC8rWTJ4d25vekUKLS0tIEFnK2gvTVBUWk5jOW1NaUtXTmRL
|
||||
U1FCaHZjaTUwZ1RGVlZkZ0JzT1NOek0KceYGFDlpcpAQte0yqebyuQBxr00/Rurh
|
||||
McQ607wCQWbGKkwkHCDi1VGqqokvbU7MRT9iOBLLBlxEF+KS9UiYuA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6aXRkd3d4ZTV0RmltR1JN
|
||||
bFg4VU0vMDVuR0cxc2I0b0NRRUQyQlE0RHk4CnpPaUpGMEh0Unk4d3BDeE84Q0px
|
||||
RnZnUHRQa3BJQUpGWUFZd2dLczhmc3MKLS0tIE9NOUNacDM5dktuWWZ2WWRxajZu
|
||||
Uk5Gb216dmdvUkJzYkoyWE5yQ044eWMKSGR5dDya0gyBWDMB+NSudXK38VQYP88y
|
||||
M6lyeZUAJ3cFydjyE1PjyllBehX9tYh/rh9RS67y/zRSmkXH5e7eAg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkM2I2MkV6NlhydURwYk8w
|
||||
ajB1cEtiQjlLYkRGbDZXTHkzTWxuNW53NTJVCmJuMzJZSlNtU1BxVFNpOWpSVWp3
|
||||
TmxOeTRhRmZlanp4QmVmRjRSOElHL3cKLS0tIHp1MzZQUlUxNXFlS1B6TWhQdzhE
|
||||
ekFibGF3ZE95K2FjZmZmVGkzek54R1UK7Au0Op4P+JZjHIiJJ5effjxjeAJItXw1
|
||||
Wrq8Z6D9NZTAZ2TBUGpdqIZcZhBiOuqxdNV88ZHOu8sjT1SLwm17GQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-28T14:50:45Z"
|
||||
mac: ENC[AES256_GCM,data:7gWwrXY9q7zpiDZgFidEMa4VbxJ0uM7HdG6Ea8P4fbISgRnOx+aoAwznEQ+K/fIBsa1qtiR82lIxsNr6b44W4jmFF3+rPAm9DMs6ImX1KIVCW3GVptM9fMqOxnCI7RGMKKwWYxkvfE2lwl5EsBljqqlsuxDfX26etfqukIhbOIY=,iv:6iI7Q8wFKguICPtFvZWshok7BkEUUITp5XcSYt9hwFM=,tag:jIOzsy4jiSvuHh5GNQsjpQ==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
66
archive/secrets-sops-backup/netbird-authentik-oidc.yaml
Normal file
66
archive/secrets-sops-backup/netbird-authentik-oidc.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
client_secret: ENC[AES256_GCM,data:p8csBIMAuDb1BYLLL9aYIifShpw9uIWm1Z0c59SlzBDP3SJD4I8Z6ipYBETisgxcECr+78zs2wtuebtcHZ3DBqF5PZaG87TJEGotTkjOVSjDLKgelMq0TDfnpWyR1yedsi6/SiRhm4ggTz0Vwwo+TLDNkw3Gv7EP7fZQphYdMyA=,iv:MZcvhiBwDc8FWcNqQvCiTL+9OVEfHpZrzt1kmcKcxdA=,tag:FmEIrw7u2wYiF3ksymC5lA==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhYmFWOGllS0ZTeU5yUkdO
|
||||
QUpHMHdXUDZsY3VFaFJjbkQ5aDJVUk5RSG5vCmJ5RWNhZXZhdkJwdUk3anNjcWlt
|
||||
WG5WSjA2UzN5TmloUm81VVo4eGdCdDAKLS0tIGtpclRlcjhlSDJnSFBmUkluM3Rz
|
||||
bm9NK3Q1NEhMSStjM0hSSXlBVUlab0EKeR1I/tAHV0ivIeaJkr0OC8W54Wvwpyuy
|
||||
qc5QCjXeD9tpMltakMFqk+tDNtItRebWO33RD9TpSkryPhiYxywF2g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZelpTaWIwbFN6bUJRemZu
|
||||
blF6VnlqZDdKNFpaY2ZXTTZCVVZETmtxUGlZCjlEaVIzc3cyVmxXQmNDRm5JdVpJ
|
||||
bm9hbjNZNGVLSldnak8wWDhqZUpqRGMKLS0tIDhoUTJjU3FDRjdYcFNmekJ3dXAv
|
||||
QW1tWElyWGJPSldhdWF0ZkFTekVoVDQKlBub868JsuRRrPrHkopMyD+GkeQo7lSn
|
||||
s7WoYd2q0oKeF4JDgO9yNZbH1zGg+5Qa76wnOqk7z2zphGIpADmyaA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1Zzc5NUJZbjNVQVAxUDdi
|
||||
WVIvUXZnK21uWmlWVUVLWDBrdmlnZFJlbnlvCjhsRG41NTdRU1NrOTRlMStzZHBl
|
||||
M3ZZUkpPNFBaRERLUGQ1cGZKTVAxdGMKLS0tIHYzdytHdmJWWHc1eDdHUUxNWnBl
|
||||
TzJBTFhxZy8vWkFGNnNTNVZjK0ZIaUkKpRokUUB7W1Xt9jP7eVQw9Hch7Fn2oEQM
|
||||
cuWcayD3w/DliXsEuS3ZGwn7bjzbJKLpDZmcxCNTwd600UdokAc/rQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYRUdQZ2k2T1AydGlBRWg2
|
||||
YUtVS29BV2VDWGVzV0xuYkF3U2NvbzdtYzFRCkRkK08vaDlOdEFsVHhCaEJ5cXZz
|
||||
bEZ4cUxjem5oM3RlZm1JZXdUcStGZk0KLS0tIHM1a1VYS1krUlg2OGVNN1pCdTdT
|
||||
TVVLK2FBdlRWTjdqWEc0WXlibytJczQKUoAigx2KB4JYy+LxbpAdUcuTW8afL7hz
|
||||
9eTizEzZRphfoEioIvIes+sLAraTj1hQH/jSFYaM9iS3sE2Zz8u35Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1cXRIWk54U2VyNVNuRGl1
|
||||
WkxlbFBMa24xdStmcW0rTUJFcUdDTUNqdmhnCms0cmdIUWtUYXNpTHBOVHB3czZn
|
||||
Qmh1UFh5RkxXdlpPa2sxK2c1M3RmRFUKLS0tIHFYQU9jUzdqQlVRZi9RNmVzM1ZN
|
||||
OStmN2VQWHhXMjczRjdtYU9wUUg1WUUKrkjJBSPWowCpNFtPm4kvQKTiKBAquilo
|
||||
fwV5/ds2gvsidoTeC6aSNIP279TZu+iT6n/K6vdt7h+Gz0Xj5YbyNQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArbFZkV1VmWXFvNE5pMXRq
|
||||
a0ZDZUVsRXJvVUVNTENDbG15MmV1ZDJ4eWdJCnJWY1dhTlRkUUd5anl2c1hYajhJ
|
||||
NmNzai9GSFVralRnaHg1NElIeFQxQVkKLS0tIGROK2pGMS9mVTJpanFxQURPNDYr
|
||||
ZzUwdDBEdS9kNSs5bDFYV0FUT2xuaDgKOwWcRF2vk82imiho3y8RliyBYzhG7AP8
|
||||
NIham/3UzYOLViPTHmMhNY3fEwUf7pv07Z1tsAglnPm3iaxbBl0Gog==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-21T20:13:20Z"
|
||||
mac: ENC[AES256_GCM,data:bi4o17skIOtZoxJGzLFJ7IAv+X265qIhgnQz6wr4bch2xofwPZZfzECck0I0xDhcwxcDtx3VZ5E2VBdvHWk+iSan+clRq7735k9+DIpjdWaGxIHuylmelZl6aE5kE36UO7fQdnwmiqCPHxiVq/sKuqknFXkl1+FvO1ryrtSHQsU=,iv:xxm+5rK7o2c43iGS1j2Q274QCRsbDn+k+wskpp5tzeU=,tag:sZHk9eth/TwqM8tIWSrv3w==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
36
archive/secrets-sops-backup/netbird-pat.yaml
Normal file
36
archive/secrets-sops-backup/netbird-pat.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
#ENC[AES256_GCM,data:yB1EyrABkGl4G+Mq67djNXCuySBS1TDZjKwtko8hfZi15br6+L9dlwhVTHrzvHAdGYOvkZu/dCssii0kKXC+3QeJws+SfwUV,iv:r7bb8QjkyfeaATPKN3VzO4H+ykQTyUKRMxoE9bmGo+E=,tag:TA0actRKQmYsgCMAcvQkjA==,type:comment]
|
||||
#ENC[AES256_GCM,data:oVH2uanu0pyv7B0SKiaSKfAXWmKAimXKR2mHKi/+hvFIQGzf3mYTZ5zGMQdBCLoy,iv:pgQrEqMZmynEHfIIHHi52xSNwn0/uQ2ccnK0T9T/Z7U=,tag:8hdflKb6l7HlqHTto8eEyQ==,type:comment]
|
||||
netbird_pat: ENC[AES256_GCM,data:cz9pdu7aLR+vKV9QbQ2PtKWf5j5PWxcHEWZ8IEseHS/qPdR4M8282g==,iv:3MCOU0q/7nqQCD6+7qWq++XR9uHi5QHZoenCIT2aYIk=,tag:m4ZE4pDycjwGxoLi1Li1bg==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpYVlocHFacWxtTnhPeTV1
|
||||
SnZqVUUwbXVId0dKNTdmaVQzSUcrT3gxYTA0CloyYm1oQlU3QXVUNVIxTXpGQkN6
|
||||
cTBxTHViZ0E5bEF0dmx4S3l5WVJZOW8KLS0tIEQ5UmJGZG5aVDhGblFDREc5M1dR
|
||||
ZlJ1UEVLeWFsQm0zQ0Q1Z2pTbEpFenMKsw+gfYZ+sby1crIyfoy+yGIaQ2WZ2KYU
|
||||
YgsXY4Vt5b5DC0u/sHKaqXH3bw8ooBQ7tR4WX3JvU6emIAJU0rAuwg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaM3NXTExiRzZ0VWdRR1RH
|
||||
RUlBVVBTWFpUQWtCTnc2T0VqMDJITVFiOFNnClZRbElxRFlGb1orbThmdGI3dFRM
|
||||
V1Jqck9pRzVjWGhVQlIvVUFLMXkyT1EKLS0tIGY5K3g3VDJjUHpjSTNrOTRBUEpz
|
||||
dHBmQ0FpeHRSc1ZyMjJHdU1pM1FzbFEKqIJ0qC0SY0aSceh0ZhGVDMb/WCkh1JZZ
|
||||
E3cP7r4Sa6rMZXRR4u23wTHnrckdHgwxSSXYYymy4ilXhgiFQ3ZTBA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhSElYV3dkMU5hR1Vnb0tl
|
||||
L1R0SG0zeFRuZUd2aTVVaU1leU14bHJjVWl3CjhibVp4NWFsazRYWkl4R1NWTUdk
|
||||
TFdrOXc2OWY0aWxUb3NqM3R1T2xVVEkKLS0tIHlUZGUrRmM5VVVTOEZNYTBSZ2s0
|
||||
VWVTdXlZcmJqeU4xc2NLSDY3MWNUUVkKix4IZEbRI7icZciI7bYzIvDiW4M3Nb7O
|
||||
uopkLc9bC/E+qK9H7jfySkk/zIeW1s/s7fBEKB5utUVpvmKzUB24pA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
lastmodified: "2026-06-03T19:57:46Z"
|
||||
mac: ENC[AES256_GCM,data:zez2CaP1NPJJnUbEWT58If6jKxFiAudRRPKVJCzbjtp0loFohvOHt1nFY3g0XldqkNngsSZ83GOeJ9g+CvdD7/plkmjz0C04uHWahnNhjAwNRiN32xt31XFqEejScOHW4dbOunX8wXuQ0Q4SrSLTl5G9O/qbLwlwEiqp8KxwRWA=,iv:ctqQ9TDUM65POxn4v1iILubygU2lloCLz9u0bAP6gsY=,tag:05FpkqIhPRMX5hYopFNK2Q==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.1
|
||||
25
archive/secrets-sops-backup/oikos-approval-hmac.yaml
Normal file
25
archive/secrets-sops-backup/oikos-approval-hmac.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
hmac_key: ENC[AES256_GCM,data:KEvRp+N/NYHlTJZoehf8cehYMpShnP43s+KWX3OR6p9udA4d1B2lMqXFhrXHxEuWDzLdfOP97AG19DDYgjemPw==,iv:SH8cyI9HP974Af54v0ecwN3ICnX+eBudP6XwB/9nwMU=,tag:VzyLoHy5LRa/5hjL43O2eQ==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyQ2s4QVJQY1VSZG5JN1NL
|
||||
Vll1MGZNSGIxQ0NJZEFpYUJ4OWxweXFOUmtRCitsODBCTVhMempzYVFpNHpmNWds
|
||||
Qk5SL2pOUGVkbzBtanRmM3BVT1JwYmcKLS0tIGpTV3k5OVF2aXZFRk1JZDVNNk1C
|
||||
VkY1SXpQS2YyM2g3TGpDWG9HdEZqTDAKYhb7yKywJ+jj7ChWCDRmhHsYCASMxBgd
|
||||
hLZV9pmIYCC4jse8q4wJK+tbT+Za5mklXt+j8nZoqm+sA0+MNlnV6g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHV0F6RzFiVHF0bS9pNER3
|
||||
elQ1YUorYUsvVC9zWXVBeXYzcThOU3JBbEVZCkxMQlVnMVNtNElibGhUaHhJM0hO
|
||||
cXhFUU5KYzAzQUREMVNJTjhmYlpXekkKLS0tIGhxb3Q3blQ2a29YM3VLdWY2aFgw
|
||||
Qm9HUEl4ZEd0WDVOZG96WXdORjdGVmMKNDNwkB4IF1xcFqG79dnUpHamOzlwQZJt
|
||||
3qfpdXquDeOOomnk7FLlnF/IqyHnhXNGqg+W1XBbZpwvqcAGtTFxQA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
lastmodified: "2026-07-05T21:11:20Z"
|
||||
mac: ENC[AES256_GCM,data:yz52Q1+49ntOFMESD3QHb5yGE7LB9SfSnHIWeMmYllaYTqR/rcNSXAb/b04ZWvLHqESGLLfZif7gNQYx3Fes0AFnAccowec7IWk/wNY0QoHLA8tZOw2Y1J1Hm32uVOWLl+G6MQybHGt9vHPlz33CLDeB/V07j7WjORqtYjVl8A4=,iv:zvx/bV3WX48OzN3Hi+moL+cELOHaw1SzW/eSCjJ25bg=,tag:DiIZfLyljK+xB3NjZsrwlA==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.1
|
||||
16
archive/secrets-sops-backup/oikos-console-deploy-secret.yaml
Normal file
16
archive/secrets-sops-backup/oikos-console-deploy-secret.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
webhook_secret: ENC[AES256_GCM,data:+/+IOmg+PC1IvItSOEl9PJZ53HhTBC1Qg6NGTQEW5K1mC/7YE1CQEpAr0w==,iv:rv9+eE1OhkudGtBB6VELcHWeDIQz6IvH3nl6H1y8nks=,tag:vzkMN8lfL3Sw1a/OA28i3w==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNnhBVnpJaUFNcWZYZW5C
|
||||
Sml0SkI1SVFVQ0hsSDkzMXBWNlZmQmRmRjNBCjZqNWRUQk45elp3VFRjUEpqVHFs
|
||||
cDNBVmM0aEtJZGplT0EwU01RNGNxZkUKLS0tIFFJMXh6N2Q3Ymg4UXdoTExFalVp
|
||||
dmpPWXlKT0tuUnhkc0pEUWFuS3l3OXcKCaSTvIPguiHnxPquk9AiT/zKBRNWvgxK
|
||||
L4Iawa12jTsRSIM6Z+T8ow2eCz0P+dtrUmxc40WXmKR9OI9mPpZQOg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
lastmodified: "2026-07-06T12:00:57Z"
|
||||
mac: ENC[AES256_GCM,data:wGYgH6R6jReR2DUSyBQbs3xxF6tvD/conjODOri85ups9hMqxZUXGsxBBbshjwIZrs1LDOoDqq4tYtWcnLPa6awbx0gZdTe6AJji2ApFDf9Ah1MkysvHio7Qr7GdyxrJbdt80bfP/tF740m9iovv2eEUNJosQOnCz6NQmwd2nZQ=,iv:caYOD2gm596LrJjnFVG8UpD8x9TQaa2ixXreoYKUc3U=,tag:GYx6NpG/lWcuANhtlY3uLA==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.1
|
||||
48
archive/secrets-sops-backup/openrouter-api-key.yaml
Normal file
48
archive/secrets-sops-backup/openrouter-api-key.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
api_key: ENC[AES256_GCM,data:TbPkuLCidS7cg52DYb0MOCui3TsaiwkOWyYLhp6gcvXI8TqvXej1tNsCF0TNAIr9vvnFaYno/UTEPiOKe/EQqR8uQMyWuvsmMg==,iv:bahy9ae4Qxvkv1OAjx6LwzK5ggqDh2GVHDj95PVP1Mc=,tag:J7PajLaeqFOdz3FESYZmSQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4TFVsb3VYWnVFcnhRL2Vz
|
||||
SVp6cG9CYzdqSFNkTGhHSmtlRDJjVWwyK0RNCkdEcEJtNjB0aHBZQVZBbTlHMTla
|
||||
cHduT1lyTWlESmQrVHB6NU9LTmU2MTQKLS0tIHkwQXM5TzZCRXFsOHdjYThZQ0Z2
|
||||
bS9wOTBDenRMb2hGcmgrcTl5R3dnTG8KZfSI1pnfpcqtD0Z5N6hONSeuAaggCkZI
|
||||
VJgh6TF7soOHbvddcNc5RINHQI6XLOZAeSOzvKm3zT8y5xe48Ff+3g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkb3ZmZmpPZDAzZzB5SmNx
|
||||
SHFKT3JENnVwZUttcFNYd3RoTHI2MCttQUNFCllZc2Z5Y1dSSnV0bW1LeGJvaURh
|
||||
TlZGWjhseUdjU1FRWE41aTlMczI0U28KLS0tIHpzajRWWmlUU3FCaEFWMlBsVjBT
|
||||
bGVRMGF2YjVwcVNCYkNEZVM1TmsyUncKYLscCS6BD3fwVmK3JnkISsEhXvhuRBnr
|
||||
tJ+v7mcEXj4pz8FjIyY6MG3T+EXx6+HwuWtPyxbmEfqnxu2Ocfg5Dg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUSCtGaUdaR2pVekZCUUkz
|
||||
am1pRTJSdzgvUjJsTlpXVE0rVlczOFZ3aGpzClJNVXRJV2JqbHB5ZEUvUFJXR2cy
|
||||
MUpad0ZMYUI2MmtoUm8zcG1RS01WcmMKLS0tIGsvVGhITGRsQkJXcUdmQllOVEVr
|
||||
QnN5QVZXcFR0cDVab09hcEFiWjRoMmsKYo5ThhNgdp9GUXZkyF6JGGdWnmHmZK5c
|
||||
QIqk5L5fYq+Wif8bKUJBEv2fGyJJZD74AOCFjQUIDgBnLx2tK3Kf4Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiVHRnMTZXK1FNWmFJZlF5
|
||||
MWk5cjRHQ2NuRWJrMlhQVXhPL094elF1MW5VCkx5TklkR1VQSXc1OTljaXBFR2FQ
|
||||
WEtudU5DNUxTWGNpbUFESjBoSkh6bVUKLS0tIFFiV2NkcktOdnF1djY2UC93OVdF
|
||||
Unhpa0h5RU9xUHNic0hXVGhqSUthN3MKMAOF3gxkJVFU9aKqeUaViDs+Ka8NI4YQ
|
||||
Bb8wPf9BcQe0NPjVMrxxP7L9c5oACiiNuA/46YxMYu7K1dOj0KapGw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-01T21:23:34Z"
|
||||
mac: ENC[AES256_GCM,data:DXvUZUOMKQA0aDpN1foKy+aHhBh6daGSRJM1FagTh76qutk4XF9Een9iyy9sQ7olabCjf3oUezvU7XWB83bSDD3e23CFuweOl0RreRFqIsbXBi55Dbcxw8+9fvMyOK4PXu6mkT/PRao/XR5sSMqiHt3FoJbjazSaJlI5teeVXbM=,iv:IICO5ay+KqkTikfxW4kqB2qcSRpbsOP1NjGWtCB9uqI=,tag:MX2TqUxre+ff3RszCfXeFg==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
100
archive/secrets-sops-backup/rotation.md
Normal file
100
archive/secrets-sops-backup/rotation.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Secret rotation runbook (Phase 5)
|
||||
|
||||
Rotation cadences per secret type. All rotation is automated via Infisical;
|
||||
this runbook covers the manual verification and DR procedures.
|
||||
|
||||
## Rotation schedule
|
||||
|
||||
| Secret | Cadence | Method |
|
||||
|--------|---------|--------|
|
||||
| OpenRouter API key | 90 days | Infisical rotation policy → update `OPENROUTER_API_KEY` env |
|
||||
| MCP bearer token | 30 days | Infisical random password generation |
|
||||
| Approval HMAC secret | 90 days | Infisical random password generation |
|
||||
| Matrix access token | 90 days | Manual (Matrix does not support automated rotation) |
|
||||
| Age DR key (SOPS fallback) | Never | Static — stored offline for DR only |
|
||||
|
||||
## How to rotate a secret
|
||||
|
||||
### Automated (Infisical)
|
||||
```bash
|
||||
# Secrets managed by Infisical rotate automatically per the policy above.
|
||||
# To force an immediate rotation:
|
||||
infisical secrets rotate --project-id $INFISICAL_PROJECT_ID \
|
||||
--secret-name <secret-key> --env dev
|
||||
|
||||
# Verify the new value is available:
|
||||
oikos secret list
|
||||
```
|
||||
|
||||
### Manual (SOPS fallback)
|
||||
```bash
|
||||
# If Infisical is unavailable, use the SOPS DR fallback:
|
||||
sops -d secrets/<name>.yaml
|
||||
|
||||
# To rotate a SOPS secret:
|
||||
sops -e --in-place secrets/<name>.yaml # edit in place
|
||||
```
|
||||
|
||||
## Rotation verification
|
||||
|
||||
After any rotation, verify the consuming services still work:
|
||||
|
||||
```bash
|
||||
# 1. OpenRouter key: test Hermes query
|
||||
curl -s -X POST http://localhost:8092/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"fleet health"}'
|
||||
|
||||
# 2. MCP bearer token: test MCP connection
|
||||
curl -s -X POST http://localhost:8092/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"tool":"list_entities","args":{"limit":1}}'
|
||||
|
||||
# 3. Approval HMAC: create a test execution
|
||||
curl -s -X POST http://localhost:8092/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query":"restart caddy"}'
|
||||
```
|
||||
|
||||
## Disaster recovery
|
||||
|
||||
If Infisical is completely unavailable:
|
||||
|
||||
```bash
|
||||
# 1. Export SOPS DR fallback
|
||||
oikos secret export-sops > /tmp/sops-dr-backup.txt
|
||||
|
||||
# 2. Configure services to use SOPS fallback
|
||||
# Set OIKOS_SECRETS_DIR=/opt/homelab-context/secrets
|
||||
# This switches the secrets manager to SOPS-only mode.
|
||||
|
||||
# 3. Restart services
|
||||
docker compose restart api hermes notifier scheduler
|
||||
```
|
||||
|
||||
## Restore drill
|
||||
|
||||
Run monthly:
|
||||
|
||||
```bash
|
||||
# 1. Export all secrets from Infisical
|
||||
oikos secret list
|
||||
|
||||
# 2. Simulate Infisical outage: stop the container
|
||||
docker compose stop infisical
|
||||
|
||||
# 3. Verify SOPS fallback works
|
||||
OIKOS_SECRETS_DIR=./secrets oikos secret list
|
||||
|
||||
# 4. Restore Infisical
|
||||
docker compose start infisical
|
||||
sleep 5
|
||||
|
||||
# 5. Verify Infisical primary works again
|
||||
oikos secret list
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-07-07 — initial rotation runbook
|
||||
Phase 5 rotation cadences, verification steps, and DR restore drill.
|
||||
66
archive/secrets-sops-backup/turn-shared-secret.yaml
Normal file
66
archive/secrets-sops-backup/turn-shared-secret.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
password: ENC[AES256_GCM,data:C9uA7LHAc/SmWJsICGMNLu2OOYyowIhLq/sTfuNo/w==,iv:211oFm7MdphnXwYszRxWrHLeKw7qeh0BNzP0yomTUTk=,tag:zfhfkzLm0AivKwD7TZTEqw==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3S0pWdGZGSFcyTG1pMnMr
|
||||
ZWpOQ1RpR1R4T2tDYTNMdmNtcnFwVzlCRHhFCkpJQm8zaDNnWS80OHA4dksyTE1P
|
||||
c1BnenJ3ZWRESlRNajltWXgwUSszYzQKLS0tIDcvdk5CaHgwWlBQanIzYmJ4V1Z2
|
||||
V2pEb0M1WmxGZFF3TmZacXFBakNpZjgKyv7Z3XvyLYTGoyS2e8ptPgIALQWKi18t
|
||||
b3qO9TTY98HGUwTLBU7Tf3IcHy8aO+u67sCVMbDhfFTd4g0bfdrNOw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByZGI0ZmhWMVlUT0x2MkpM
|
||||
dUk4cFBZSXFYcHd4dWt1d3hCYlFzUkJEdEJnCkdIaFBDcHJyS0p0aUZXb2xKckIw
|
||||
Ky9CT2lLWXp4dkUwNEpoRmd3YWNDaGsKLS0tIHdROUx1Uzc4NEVrZnBxMHVmUi95
|
||||
SXdZaUZGZklIalYzd2xZdjZtMFQ0MnMKxEKbyLvlwdrDx4G1CmUs2qs1nJbdIlSu
|
||||
coXVNnkbt6FIzNO08DxTGBP7Q2XQWQKMvRpBFDYYUssOGBHL3JPVgQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWU243SmlKVHRwKzMvaFk1
|
||||
U2ZpcnJxK0RwZXhzWFpQOUNyS0JOZlljbFgwCitQU1hTbzMwNlltcUZaSUxXK01T
|
||||
VGFxLzZCZGZsUEtXWjY5b2dBZmxDcGcKLS0tIDM4S0Z2MW1rVmlyZjJ6N0NZQytq
|
||||
d0xLUlNHdE1mZVhOZjVManA1OXhBV2cKgoo6S6kVypzCljJSqSRQP2fSYHizdZQF
|
||||
H6GNSOCj5fYbkZQwa3WUyun8YRl0BMgdkg1Uym++b2EGGCOFoHSuyg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrWU5PKzk2R1h0VVIwMFFl
|
||||
MzBCN1BXUGdvS3FobkNiTGZWRnVkckVWUGs0ClVSaVEwclBDdnkwalllUFF3T3ln
|
||||
V1dHL2Q0MnR0Nm9BUTdIbTRVUlJQZk0KLS0tIDRjd1hjT1lja3hPS3FyQndnNXJi
|
||||
SzczZU1pVG5xM05TMUdtcHNNbHE3WjAKITW1sBP9TqZ6+cKNMnvQPzuNAdawaX8m
|
||||
H4aAjlaR+BCrJPNJmv3J+VT3NZxw8rFIKQBu+c5h9RaU3RX1o/A2PA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3d0p3Z3J6VXBvVjZpMXVa
|
||||
TDZ6V1BEdTQzYzErMitDeHpRT3NFQ3FVRURzCnlWaVkzYjhXbStVSmp5eThBcXZY
|
||||
RG5MQXpWYXB1aVhISmNJZ0p3K0k4akUKLS0tIFJWa3NkQ0tEc1NscHRjR2FmalpL
|
||||
SVBrem8yT1FqRjI2dlpQTzB1N3JSWXMK08Gj8cRPmImMVPbS2/0W2qHqJCZ9Gy9S
|
||||
VgvSaI1PX1ieOkpqvl6nNUfWLzykZkdcAu9DhHC9MSwDDYy8vOBHPw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSdVBpMmFEcUZkYjdKY1Qz
|
||||
aCtWOXFoc3FGd0dZUHNwNExuRFF3cDhXNEhBCnQxZWNkbmxQMnNSaGhjS0E3TS82
|
||||
a25OVytyK2lnUkpwYmRIUEJmZG1yLzQKLS0tIFA2U1VVUzdNb3B1SUFnZmF3VndQ
|
||||
R0NuY0NWRjU0MmVEWGhyYWN3dk9nbmMKbN0hg8Sd8/TJxTSvhMYicpwLnR7LPD7P
|
||||
HeH+hwrqzT/3CRDpL7DRpILommAEQ+3j1dbaLTdbE1xhBp7jegfD3w==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-21T20:13:20Z"
|
||||
mac: ENC[AES256_GCM,data:PXXEpDbJIoIwWuNwMetpALtxrcB7yJhDYqp6LSKV8WFQI2TJSDgDHiNQ2cngfB9PXKmUNNhlvhmB52Hi6WQSWQdXjiy79T1fOjMhbUAKmykTaZDrwvlrDGkPgiZ1cFSb8+hx/5aZw7YhdQO9+7LAmymeIldZIFEpEO2pvuPZ6MM=,iv:mJb3K8TnsnY183OX7o9pF7oVMDcwVU/rOgX74KTRpr4=,tag:f+M+FT0Vjozm7a8LFDI8lA==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
48
archive/secrets-sops-backup/yuvomi-api-token.yaml
Normal file
48
archive/secrets-sops-backup/yuvomi-api-token.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
token: ENC[AES256_GCM,data:LgiLFEsli412rVxSo9YU69wx9Lh7eDGBw6nmROh6Qbw7bYN8lylNjSDWkjNJ/UJvDg==,iv:vNmiV/D+SQaMtgwTK/mFDQIwSQ/L0JRatmoj5AWfBiw=,tag:W5P98TlmevQ0j051044LPQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQYVBwS250T0ZFa2VGbGow
|
||||
R0MwMzNsY0t2N1ZBVnlnbkF6UEIyUStaUEhJCnBuNWNZT1VxRFlKNFBjOTZHNEd0
|
||||
cCtxN1p3TjkzWUZNZkdZeXRJVEhaQzgKLS0tIFVSWUpVQ2k3VE83eHU4VmR4SnVH
|
||||
RVgxdDNnKzMwM2c2bm1YS1ZSRDdsMVkK8PVncSpesaxIEz7a39rVBzB0V3RphmAA
|
||||
ANV8fY4W4tLuxo+EhB90ciy2dnAHaKEbxHBWMMjN4u22yZRT4lPWQw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Sk1hdENFQWIvZXhIK0U0
|
||||
TmhCWkw5TWhSVXdIbDN5SmVNdHkwYm1aOGxvCmlnQmZYWitxUmdURzNYRDBFUUVM
|
||||
TjdIN2FEd2ptWG5UVlFCb1IyVzFjMmsKLS0tIDJMZHlXVHFNUlY2bnhlS3hFT3FY
|
||||
U2szdVB4MGxxUnpJN1BBaUhKVE5WQkEKz7BkFIfs2RSyAww73dH5+PyDNjo4Ocor
|
||||
mX5pLhSRGGf13bfA5lMQiGvqtd3jw1c4zKVZD9BcthIk5H/oqallNQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmVWI2L05GbjdLR1k3a280
|
||||
N2RRWmZsZWZPeWdmd3I3aThrV1ppOEw4VTJRCit6aXgwTVIwQldkSmpRNkM4UTh4
|
||||
UEcyQjkrMFk2UGVBL1JZdGg2WDI3c0EKLS0tIGV5VkRWYWpXZXZGN1FXNmhTSmR5
|
||||
cjQ0TWg0TkFmMnIyMDNBNVZXa1l0REUKVHdvbXFwLgMECi2JLg8aoYIkOWHwYyZh
|
||||
v3mVVaaki5KceuyUhcGsdthNadrM0RDi89uUw8O2cfADdDgLlha/iQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnemkvTUxHZDNxYmJ4eGRK
|
||||
Y09laWNnN0pXN1VHQzUzSWlJZGFqcmkwTWdJCjkxZG9pVVVzWXBTU0lpL0JFR2w3
|
||||
TWhPTDNFSXhPTkVMVUpOY0xHMlN6RkkKLS0tIDB3d0ZyRVpGT3YzSXIrM1pPQ1Uz
|
||||
QkpQYXRWaGtabnU5c1RpeE9vb0MwMWcKVRnpepDJ8a2ECD5uaK6O3fMKwWcLJP8a
|
||||
uFWds5rvwDfrM1aNLXYbPIfXtqLx4fbxkoRZvHcuZ2/lYLDBFBrFlQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-28T14:02:13Z"
|
||||
mac: ENC[AES256_GCM,data:NDjBNtSzGdm0+SqAvaUlltljXcyXzLQx3b9RUWZIlS/D3ox1BTO6t3t4WT8szbSnKcT51mJGJ8EIqppNWkmOpEaMNGdMw70DmGnf/IJAzScTpRyvhfsGlohCVaekMBjF+wg1uoB91vSKcTFq/wfUW79KICmxMF6woavRQ94yfQs=,iv:1f9sGKyTITSslqDXB3Khk/OGiPaJM2BreSEG6aTbZDs=,tag:UulDl/JQamg/p/q4esvtIA==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
Reference in New Issue
Block a user