recovery: wire Vault passwords to existing Ananke host actions
This commit is contained in:
parent
36c7e76e56
commit
886dbeee10
89
scripts/configure_ananke_node_access.py
Executable file
89
scripts/configure_ananke_node_access.py
Executable file
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Enable Ananke's existing Vault-synchronized sudo path without replacing config."""
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def updated_config(source):
|
||||
"""Preserve unrelated text/settings; change only credential lookup and Titan-24 user."""
|
||||
config = yaml.safe_load(source)
|
||||
desired = {
|
||||
"host_sudo_secret_namespace": "maintenance",
|
||||
"host_sudo_secret_name_template": "ananke-sudo-{node}",
|
||||
"host_sudo_secret_password_key": "password",
|
||||
}
|
||||
for key in desired:
|
||||
source = re.sub(r"^ " + key + r":.*\n", "", source, flags=re.M)
|
||||
stanza = "".join(" " + key + ": '" + value + "'\n" for key, value in desired.items())
|
||||
if source.count("\nstartup:\n") != 1 or source.count("\nssh_node_users:\n") != 1:
|
||||
raise ValueError("unexpected_config_structure")
|
||||
source = source.replace("\nstartup:\n", "\nstartup:\n" + stanza, 1)
|
||||
start = source.index("\nssh_node_users:\n")
|
||||
end_match = re.search(r"\n[^ #\n][^\n]*:", source[start + 1:])
|
||||
if end_match is None:
|
||||
raise ValueError("missing_inventory_boundary")
|
||||
end = start + 1 + end_match.start()
|
||||
users = source[start:end]
|
||||
if not re.search(r"^ titan-24:", users, flags=re.M):
|
||||
users += "\n titan-24: tethys"
|
||||
else:
|
||||
users = re.sub(r"^ titan-24:.*", " titan-24: tethys", users, flags=re.M)
|
||||
source = source[:start] + users + source[end:]
|
||||
expected = config.copy()
|
||||
expected["startup"] = dict(config.get("startup", {}), **desired)
|
||||
expected["ssh_node_users"] = dict(config.get("ssh_node_users", {}), **{"titan-24": "tethys"})
|
||||
if yaml.safe_load(source) != expected:
|
||||
raise ValueError("unrelated_configuration_change")
|
||||
return source
|
||||
|
||||
|
||||
def main():
|
||||
"""Validate staged configuration with Ananke, then retain a root-only rollback."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--config", type=Path, default=Path("/etc/ananke/ananke.yaml"))
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if os.geteuid() != 0:
|
||||
raise SystemExit("Run as root")
|
||||
source = args.config.read_text()
|
||||
original_stat = args.config.stat()
|
||||
revised = updated_config(source)
|
||||
if revised == source:
|
||||
print("Node-access configuration is current")
|
||||
return
|
||||
if not args.apply:
|
||||
print("Node-access configuration needs updating; use --apply")
|
||||
return
|
||||
fd, name = tempfile.mkstemp(prefix=".node-access-", dir=args.config.parent)
|
||||
staged = Path(name)
|
||||
try:
|
||||
with os.fdopen(fd, "w") as stream:
|
||||
stream.write(revised)
|
||||
checked = subprocess.run(["/usr/local/bin/ananke", "status", "--config", name],
|
||||
capture_output=True, timeout=45)
|
||||
if checked.returncode:
|
||||
raise SystemExit("Ananke rejected staged configuration; original retained")
|
||||
backup = Path("/var/lib/atlas-maintenance/ananke-access-before-20261004")
|
||||
backup.mkdir(parents=True, exist_ok=True, mode=0o700)
|
||||
target = backup / "ananke.yaml"
|
||||
if not target.exists():
|
||||
shutil.copy2(args.config, target)
|
||||
target.chmod(0o600)
|
||||
os.chown(staged, original_stat.st_uid, original_stat.st_gid)
|
||||
staged.chmod(stat.S_IMODE(original_stat.st_mode))
|
||||
staged.replace(args.config)
|
||||
print("Updated node access; restart Ananke after verifying synchronized secrets")
|
||||
finally:
|
||||
staged.unlink(missing_ok=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -25,6 +25,58 @@ spec:
|
||||
- objectName: "soteria-restic__AWS_ENDPOINTS"
|
||||
secretPath: "kv/data/atlas/shared/soteria-restic"
|
||||
secretKey: "AWS_ENDPOINTS"
|
||||
- objectName: "ananke-sudo-titan-04"
|
||||
secretPath: "kv/data/atlas/nodes/titan-04"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-07"
|
||||
secretPath: "kv/data/atlas/nodes/titan-07"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-08"
|
||||
secretPath: "kv/data/atlas/nodes/titan-08"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-11"
|
||||
secretPath: "kv/data/atlas/nodes/titan-11"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-12"
|
||||
secretPath: "kv/data/atlas/nodes/titan-12"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-13"
|
||||
secretPath: "kv/data/atlas/nodes/titan-13"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-14"
|
||||
secretPath: "kv/data/atlas/nodes/titan-14"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-15"
|
||||
secretPath: "kv/data/atlas/nodes/titan-15"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-17"
|
||||
secretPath: "kv/data/atlas/nodes/titan-17"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-18"
|
||||
secretPath: "kv/data/atlas/nodes/titan-18"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-19"
|
||||
secretPath: "kv/data/atlas/nodes/titan-19"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-20"
|
||||
secretPath: "kv/data/atlas/nodes/titan-20"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
- objectName: "ananke-sudo-titan-21"
|
||||
secretPath: "kv/data/atlas/nodes/titan-21"
|
||||
secretKey: "atlas_password"
|
||||
filePermission: 256
|
||||
secretObjects:
|
||||
- secretName: harbor-regcred
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
@ -42,3 +94,68 @@ spec:
|
||||
key: RESTIC_PASSWORD
|
||||
- objectName: soteria-restic__AWS_ENDPOINTS
|
||||
key: AWS_ENDPOINTS
|
||||
- secretName: ananke-sudo-titan-04
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-04
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-07
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-07
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-08
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-08
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-11
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-11
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-12
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-12
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-13
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-13
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-14
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-14
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-15
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-15
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-17
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-17
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-18
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-18
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-19
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-19
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-20
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-20
|
||||
key: password
|
||||
- secretName: ananke-sudo-titan-21
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: ananke-sudo-titan-21
|
||||
key: password
|
||||
|
||||
@ -14,7 +14,7 @@ spec:
|
||||
labels:
|
||||
app: maintenance-vault-sync
|
||||
annotations:
|
||||
maintenance.bstein.dev/restart-at: "2026-04-13T05:57:00Z"
|
||||
maintenance.bstein.dev/credential-schema: "node-sudo-v1"
|
||||
spec:
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
@ -33,6 +33,12 @@ spec:
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- "sleep infinity"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 16Mi
|
||||
limits:
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- name: vault-secrets
|
||||
mountPath: /vault/secrets
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user