Files
mesh-configs/skills/autonomous-ai-agents/mesh-file-task-queue/references/ssh-scp-task-polling-2026-08-03.md
T

57 lines
4.0 KiB
Markdown

# SSH/scp Task-Polling statt NFS-Freigabe (Sicherheits-Umbau 2026-08-03)
## Entscheidung
NFS-Freigabe ist OBSOLET. Alle Sub-Agenten erhalten Tasks über **SSH/scp (Key-Auth, verschlüsselt)**.
Grund: NFS (sec=sys) überträgt plaintext → Man-in-the-Middle-fähig. SSH = Ende-zu-Ende verschlüsselt,
nur eigene Tasks sichtbar (gefiltert nach `*.C.D.*`), auditierbar.
`/srv/work/tasks/` bleibt als **zentrale Ablage auf dem Master** (offen/in_arbeit/fertig/bugfix/archiv),
nur die Zugriffsart änderte sich: kein Mount, sondern pull/push per scp.
## Warum NFS hier nicht überall geht
SwitchHermes = Ubuntu 24.04 auf Nintendo-Switch (Tegra-Kernel `4.9.140-l4t`):
- `modprobe nfs` → `FATAL: Module nfsv4 not found`
- NFS-Client + FUSE/sshfs fehlen (kein Kernelmodul, wenig Speicher → kein Kernel-Ausbau gewollt)
→ SSH/scp ist die einzige portable Methode für ARM-Switch-Geräte.
## Architecture des SSH-Poller-Daemons
- Jeder Agent hat **eigenen ed25519-Key** (auf Master: `/root/.ssh/<agent>_mesh`), Public-Key in `authorized_keys`.
- Poller-Script `mesh-agent-poller.sh <meine-IP> <meine-ID> [master] [keyfile]`:
1. `scp` pull eigene Tasks `offen/<JahrMonatTag>.<C>.<D>.*.json` nach `/tmp/mesh-tasks/`
2. Atomarer Lock **per SSH auf dem Master**: `mv offen/... in_arbeit/...` (absolute Pfade!), `&& echo OK || echo LOST`
3. Bearbeitung via `hermes chat -q "<PROMPT>" --quiet --max-turns 3`
4. Ergebnis als Base64 ins JSON-Feld `result` schreiben (SSH-python), dann `mv in_arbeit/ → fertig/`, chmod 0666
- **Daemon**: systemd-user-service (`~/.config/systemd/user/mesh-poller.service`, `Restart=always`, Loop alle 10s).
NICHT nohup/Cron-@reboot als Loop — der stirbt beim SSH-Session-Ende.
- Service-Start für User-Services: `export XDG_RUNTIME_DIR=/run/user/$(id -u); systemctl --user ...`
## Wichtigste Pitfalls (alle live getroffen)
1. **`SSH_CMD` MUSS den Host enthalten**: `SSH_CMD="ssh -i $KEY ... root@172.24.196.119"`.
Vergessen → `hostname contains invalid characters` + leerer Lock-Output = endlose SKIPs, obwohl der
manuelle `mv`-Test auf dem Master klappt.
2. **Relative Pfade im remote-mv vermeiden** — `../../../in_arbeit` geht 3 Ebenen hoch statt 1. Nutze absolute Pfade.
3. **Datei-Rechte (Hauptgrund, warum Polling anfangs nie lief):** vom Master (root) erstellte/durch `mv`
bewegte Task-Dateien behalten root-Owner → Sub-Agent (uid 1000) kann nicht reinschreiben, selbst bei
0777-Dir. Fix: **`chmod 0666`** nach Task-Erstellung (`mesh-task-create.sh`) UND nach jedem `mv` im Poller
(`in_arbeit` UND `fertig`). Sonst `PermissionError: [Errno 13]`.
4. **fail2ban bannt Mesh-IPs** bei scp-Fehlschlägen → `Connection refused` auf Port 22, obwohl Ping klappt
und sshd auf `0.0.0.0:22` lauscht. Fix: `ignoreip = <alle ZT-IPs>` in `[sshd]`-Sektion von
`/etc/fail2ban/jail.local`. Verifizieren: `fail2ban-client status sshd` → IP aus `Banned IP list` nehmen.
5. **Doppelte Daemon-Prozesse** (ein nohup-Rest + ein systemd-Rest) stören sich gegenseitig beim Lock.
Nach Umbau: `systemctl --user stop`, `pkill -9 -f mesh-poller-daemon`, PID-Datei löschen, sauber `start`.
Am Ende MUSS `pgrep -f mesh-poller-daemon` genau EINEN Prozess zeigen.
6. **"No such file or directory" beim remote-scp-Glob** kann heißen, dass der Task bereits in `fertig/`
liegt (ein früherer Lauf hat ihn gezogen). Erst `ls` auf Master prüfen, bevor man den Glob verdächtigt.
## NFS-Server deaktivieren (nach Migration)
- `systemctl stop/disable nfs-kernel-server rpcbind`
- `ufw delete allow from <ip> to any port 2049/111/20048` für jede Agenten-IP
- Offene Mesh-Ports danach NUR: 22 (SSH), 8644 (Webhook), 11434 (Ollama) — alle gerichtet auf die 4 bekannten ZT-IPs.
- `/etc/exports` bleibt als Datei liegen, aber inaktiv.
## NFS-Export-Härtung (falls man doch NFS nutzen muss)
Export auf die 4 bekannten IPs statt `/16`, zur
defense-in-depth: UFW-Regel (nur bekannte IPs) UND Export (bekannte IPs) konsistent:
`/srv/work/tasks ip1(rw,sync,no_subtree_check,no_root_squash) ip2(...) ...`
`exportfs -ra` reload separat ausführen, NICHT als gateway-gebundener Befehl (wird gekillt).