1
0
Fork
You've already forked iroh
0
No description
  • Shell 82.6%
  • Nix 11%
  • Jinja 6.4%
Paul Berz 93d553cb00 feat: NixOS-Modul für iroh-ssh Fork (nixos-rebuild switch support)
NixOS-Modul das das musl-Binary von S3 zieht und einen systemd-Service
aufbaut. Einbinden via imports = [ ./iroh-ssh-fork.nix ] in configuration.nix,
dann sudo nixos-rebuild switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026年06月30日 17:49:05 +02:00
ansible relay: auf iroh.hcnbg1.berz.systems:8443 umstellen (alter VM-Relay gelöscht) 2026年06月26日 16:17:16 +02:00
bash fix: opnsense-setup.sh — use --allow-file (not --allowed-nodes) 2026年06月30日 17:24:55 +02:00
bootstrap feat: NixOS-Modul für iroh-ssh Fork (nixos-rebuild switch support) 2026年06月30日 17:49:05 +02:00
ci iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
simulation iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
.gitignore iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
CLAUDE_CODE_PROMPT.md iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
KONZEPT.md iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
NODEID_FILTER.md iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00
README.md opnsense: FreeBSD RC-basierter iroh-ssh Setup (kein systemd) 2026年06月30日 08:58:42 +02:00
SETUP.md relay: auf iroh.hcnbg1.berz.systems:8443 umstellen (alter VM-Relay gelöscht) 2026年06月26日 16:17:16 +02:00
UPDATE_RUNBOOK.md iroh-ssh deployment: bootstrap script, ansible, docs 2026年06月25日 14:17:57 +02:00

iroh — SSH ohne offenen Port, mit hartem NodeID-Filter

SSH zu einem Server ohne offenen SSH-Port / ohne öffentliche IP-Erreichbarkeit, über einen eigenen iroh-Relay (TLS), abgesichert durch eine fail-closed NodeID-Allowlist auf iroh-Ebene plus SSH-Keys. Zwei unabhängige Schichten, zwei verschiedene Geheimnisse (iroh-Key des Geräts ⟂ SSH-Key).

Build & Quellcode des Binaries: Fork pb/iroh-ssh (iroh 1.0 + NodeID-Allowlist). Dieses Repo enthält Setup, Bootstrap-Skript und Ansible zum schnellen Reproduzieren.

📖 Schritt-für-Schritt für Client / Server / Relay / Ansible: siehe SETUP.md.

TL;DR — schnellstes Reproduzieren

Binary + Identität auf einer Maschine (druckt die iroh-ID):

curl -fsSL https://f.berz.dev/aiagent/iroh-ssh/latest/iroh-bootstrap.sh | sudo -E bash -s -- --role client

Server (nimmt iroh-ssh-Verbindungen NUR von erlaubten iroh-IDs an):

curl -fsSL https://f.berz.dev/aiagent/iroh-ssh/latest/iroh-bootstrap.sh | sudo bash -s -- \
 --role server --relay https://iroh.hcnbg1.berz.systems:8443 \
 --allow <iroh-ID-client-1> --allow <iroh-ID-client-2>

SSH-Pubkeys legst du selbst in authorized_keys ab; --allow regelt nur die iroh-Ebene.

Server OHNE Allowlist (offen — jede iroh-ID, die die Node-ID kennt, darf rein):

curl -fsSL https://f.berz.dev/aiagent/iroh-ssh/latest/iroh-bootstrap.sh | sudo bash -s -- \
 --role server --relay https://iroh.hcnbg1.berz.systems:8443 --no-allowlist

--no-allowlist lässt --allow-file im Service weg (allowlist.rs: Filter ist nur aktiv, wenn --allow-file/--allow gesetzt ist). Dann schützt nur noch die SSH-Schicht (authorized_keys / Passwort). Für Produktivziele die Allowlist bevorzugen.

⚠️ --role server ohne --allow und ohne --no-allowlist ist fail-closed: die Allowlist ist aktiv aber leer ⇒ alle Verbindungen werden abgewiesen.

Allowlist an einem bereits laufenden Server nachträglich abschalten (ohne Re-Bootstrap):

sudo sed -i 's# --allow-file [^ ]*##' /etc/systemd/system/iroh-ssh-server.service
sudo systemctl daemon-reload && sudo systemctl restart iroh-ssh-server

Oder gezielt eine iroh-ID freischalten statt öffnen:

echo '<client-iroh-ID>' | sudo tee -a /etc/iroh-ssh/allowed_nodes
sudo systemctl restart iroh-ssh-server

OPNsense / FreeBSD (kein systemd, kein apt):

# OPNsense Shell — ohne Allowlist:
fetch -qo - https://f.berz.dev/aiagent/iroh-ssh/latest/opnsense-setup.sh | sh
# Mit Allowlist:
ALLOW="<iroh-ID>" fetch -qo - https://f.berz.dev/aiagent/iroh-ssh/latest/opnsense-setup.sh | sh
# Web-UI Tunnel (nach dem Setup):
iroh-ssh -L 8443:127.0.0.1:443 -N root@<NODE-ID> # → https://localhost:8443

Nutzt FreeBSD daemon(8) + RC-System statt systemd. Binary: native FreeBSD 14 ELF. Details: bash/opnsense-setup.sh und pb/iroh-sshFREEBSD.md.


Komplettes 3-Knoten-Setup reproduzierbar (Server A + Relay B + Client C):

cd ansible
# inventory.ini (IPs/Bootstrap-Zugang) und group_vars/all.yml (Relay-Domain, E-Mail) anpassen
ansible-playbook -i inventory.ini site.yml

Architektur

 C: Client/PC ──TLS/QUIC──▶ B: iroh-relay (Docker, LetsEncrypt, access = "everyone")
 │ iroh (direkt ODER via Relay) さんかく
 └──────────────────────────────────────────────────┘
 ▼
 A: Zielserver — iroh-ssh server (systemd, --persist, --relay-url, --allow-file)
 sshd nur auf 127.0.0.1:22 (kein offener Port nach außen)

Zugriffskontrolle liegt pro Server, nicht am Relay:

  • Server-Allowlist (/etc/iroh-ssh/allowed_nodes): wer per SSH rein darf (Client-iroh-IDs). Fail-closed: aktiv + leer ⇒ alles abgewiesen, bevor ein Byte den sshd erreicht. Neuen Client freischalten = nur hier (+ SSH-Key) eintragen — kein zentraler Relay-Schritt.
  • SSH-Keys: der eigentliche Login-Riegel (PasswordAuthentication no).
  • Relay = reiner Transport, offen (relay_access_mode: everyone). Ein Fremder am Relay erreicht nur den Accept-Handler und wird dort vom NodeID-Filter abgewiesen (kein Zugriff). Optional härtbar: relay_access_mode: shared_token oder allowlist.

Repo-Inhalt

bootstrap/iroh-bootstrap.sh One-liner: Binary + persistente Identität (+ optional Server-Rolle)
ansible/ reproduzierbares 3-Knoten-Setup (A/B/C), idempotent
 inventory.ini A (Zielserver), B (Relay), C (lokal)
 inventory-iroh.ini A über iroh erreichen (ansible_host = A_ID)
 group_vars/all.yml Versionen, Relay-Domain, Binary-URL (MinIO-Mirror)
 site.yml + roles/ iroh_ssh_server / iroh_ssh_client / iroh_relay
bash/ dieselben Schritte als einfache Skripte
simulation/resilience-test.sh Ausfall-Szenarien (Reboot, Relay down, Reconnect)
KONZEPT.md ausführlicher Leitfaden + Sicherheits-Einordnung
NODEID_FILTER.md Design des NodeID-Filters (Fork)
UPDATE_RUNBOOK.md Fork aktuell halten: rebase -> tag -> rollout

Build- & Release-Kette (Fork pb/iroh-ssh)

  1. Code im Fork pb/iroh-ssh (Basis: rustonbsd/iroh-ssh, gehoben auf iroh 1.0).
  2. git tag vX && git push --tagsWoodpecker baut statisches musl-Binary → Forgejo-Release (Asset iroh-ssh.linux + .sha256).
  3. Release wird auf MinIO gespiegelt (f.berz.dev/aiagent/iroh-ssh/<tag>/...), damit Server/Clients es ohne mTLS per curl/get_url laden. Build ist reproduzierbar (lokaler Build == CI-Asset, gleicher sha256).
  4. Allowlist-Patch ist isoliert (src/allowlist.rs + 1 Hook) → Upstream-PR via upstream-pr.patch/upstream-pr.md im Fork. Updates: siehe UPDATE_RUNBOOK.md.

Relay (produktiv)

Rolle Wert
Relay https://iroh.hcnbg1.berz.systems:8443 (Kubernetes hcnbg1, Wildcard-TLS, QUIC 7842/udp)

Der Relay läuft als App im GitOps-Cluster (~/code/kubernetes, apps/iroh-relay, hcnbg1). Der frühere Hetzner-Test-Aufbau (separater VM-Relay + Ziel-Server) wurde entfernt.

Im Test bewiesen: positiver Connect, negativer (nicht gelistete iroh-ID wird vor sshd abgewiesen), fail-closed, Reboot-Recovery (gleiche iroh-ID), ansible -m ping über iroh.

Zweiten Relay aktivieren

DNS + Server bereitstellen, in group_vars/all.yml den zweiten relay_hosts-Eintrag einkommentieren, ansible-playbook -i inventory.ini site.yml — der Server-Service bekommt automatisch eine zweite --relay-url. Persistente Keys/IDs bleiben.