diff --git a/scripts/node_admin_access.py b/scripts/node_admin_access.py old mode 100644 new mode 100755 index 71d619b9..97fd0aa4 --- a/scripts/node_admin_access.py +++ b/scripts/node_admin_access.py @@ -13,6 +13,7 @@ import hmac import json import os import pwd +from pathlib import Path import socket import subprocess import sys @@ -70,16 +71,44 @@ def run(payload, apply=False): "before": before, "after": after} +def retire_legacy_sudo(result): + """Remove only the known Metis grant after password access is established.""" + if not result["after"].get("atlas", {}).get("vault_password_matches"): + raise ValueError("atlas_password_not_verified") + expected = ("atlas ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/sbin/poweroff, " + "/sbin/poweroff, /usr/local/bin/hecate, /usr/local/bin/k3s, /usr/bin/k3s") + paths = [Path("/etc/sudoers.d/90-hecate-atlas"), Path("/etc/metis/sudoers-hecate")] + for path in paths: + if path.exists() and path.read_text().strip() != expected: + raise ValueError("legacy_grant_modified_requires_review") + if subprocess.run(["/usr/sbin/visudo", "-c"], capture_output=True).returncode: + raise ValueError("sudo_configuration_invalid") + backup = Path("/var/lib/atlas-maintenance/legacy-sudo-20261004") + backup.mkdir(parents=True, exist_ok=True, mode=0o700) + changed = [] + for path in paths: + if path.exists(): + destination = backup / path.name + if destination.exists(): + raise ValueError("legacy_backup_already_exists") + path.rename(destination) + changed.append(path.name) + result["retired_legacy_sudo"] = changed + + def main(): """Read one bounded payload and emit only safe operational metadata.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--apply", action="store_true") + parser.add_argument("--retire-legacy-sudo", action="store_true") args = parser.parse_args() try: content = sys.stdin.read(65537) if len(content) > 65536: raise ValueError("payload_too_large") result = run(json.loads(content), args.apply) + if args.retire_legacy_sudo: + retire_legacy_sudo(result) except Exception as error: # Do not echo exception messages: malformed input can contain credentials. print(json.dumps({"error": "node_access_operation_failed", "type": type(error).__name__})) diff --git a/scripts/node_disable_obsolete_ramlog.sh b/scripts/node_disable_obsolete_ramlog.sh new file mode 100755 index 00000000..d5c2479f --- /dev/null +++ b/scripts/node_disable_obsolete_ramlog.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Stop stale Armbian RAM-log copy hooks after logs have moved to external storage. +set -euo pipefail +[[ ${EUID} -eq 0 ]] || { echo "Run as root" >&2; exit 1; } +config=/etc/default/armbian-ramlog +[[ -f ${config} ]] || { echo "No Armbian RAM-log configuration" >&2; exit 1; } +[[ $(readlink -f /var/log) == /mnt/astraios/var/log ]] || { + echo "Refusing: /var/log does not use the managed external log path" >&2; exit 1; +} +[[ $(findmnt -n -o TARGET -T /var/log) == /mnt/astraios ]] || { + echo "Refusing: another filesystem is mounted over the external log directory" >&2; exit 1; +} +[[ $(findmnt -n -o FSTYPE -T /var/log) == ext4 ]] || exit 1 +if systemctl is-active --quiet armbian-ramlog.service; then + echo "Refusing: migrate an active RAM-log mount before changing this setting" >&2 + exit 1 +fi +backup=/var/lib/atlas-maintenance/ramlog-before-20261004 +install -d -m 700 "${backup}" +[[ -e ${backup}/armbian-ramlog ]] || cp -p "${config}" "${backup}/armbian-ramlog" +# The native Armbian hooks already honor this flag, including at boot. +sed -i 's/^ENABLED=.*/ENABLED=false/' "${config}" +grep -qx 'ENABLED=false' "${config}" +systemctl start logrotate.service +systemctl show logrotate.service -p Result -p ExecMainStatus diff --git a/services/maintenance/apps/ariadne-deployment.yaml b/services/maintenance/apps/ariadne-deployment.yaml index cb54df8a..043b9e2e 100644 --- a/services/maintenance/apps/ariadne-deployment.yaml +++ b/services/maintenance/apps/ariadne-deployment.yaml @@ -629,10 +629,10 @@ spec: resources: requests: cpu: 100m - memory: 128Mi + memory: 512Mi limits: cpu: 500m - memory: 512Mi + memory: 1Gi # timeoutSeconds defaults to 1, which this pod cannot honour. The # auto-triage tick runs every minute and spends most of it waiting on # Jenkins, OpenSearch, Gitea and Hermes; against a 500m CPU limit the