#cloud-config
# Hokono Mix-Knoten fuer einen UNABHAENGIGEN Betreiber auf Hetzner Cloud (TASKS #76).
#
# Unterschied zur eigenen Flotte (infra/terraform-mp): kein AWS, kein SSM, kein Wickelschluessel aus
# einem fremden Parameter Store. Der Betreiber ist Herr seines Knotens: Der Wickelschluessel fuer
# node.key entsteht lokal, der Zugang ist SSH mit Schluessel (Passwort-Login aus), der Rest der
# Haertung ist identisch mit unserer Flotte (No-Log, nftables, statisches Binary aus der Registry mit
# Hash-Pruefung, gehaertete systemd-Unit).
#
# Vor dem Einsatz die zwei Platzhalter ersetzen (SSH-Public-Key, Betreiber-Tag; siehe ANLEITUNG).
# Peers: KEINE Liste mehr (#114). Der Mix holt das signierte Directory selbst von einem Gateway
# (Bootstrap-Adressen unten, DIRECTORY_FROM), prueft die Signaturen gegen die oeffentlichen
# Authority-Schluessel (authorities.txt aus der Registry) und erlaubt genau die Knoten daraus.
# Anlegen (hcloud CLI):
#   hcloud server create --name mix-<tag> --type cx23 --image debian-12 --location nbg1 \
#     --user-data-from-file cloud-init-mix.yaml
# Danach:  bash check.sh <ip> mix hokono-admin <ssh-key>   (Ports, Hash gegen Manifest, Logs, oeffentliche Schluessel)
package_update: true
package_upgrade: true
packages: [nftables, curl, ca-certificates, unattended-upgrades]
users:
  - name: hokono-admin
    groups: [sudo]
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - __SSH_PUBKEY__
ssh_pwauth: false
disable_root: true
write_files:
  - path: /etc/systemd/journald.conf.d/00-hokono-nolog.conf
    content: |
      [Journal]
      Storage=none
      ForwardToSyslog=no
      ForwardToKMsg=no
      ForwardToConsole=no
      ForwardToWall=no
  - path: /etc/nftables.conf
    content: |
      flush ruleset
      table inet filter {
        chain input {
          type filter hook input priority 0; policy drop;
          ct state established,related accept
          ct state invalid drop
          iif "lo" accept
          ip protocol icmp accept
          ip6 nexthdr icmpv6 accept
          tcp dport 22 accept
          tcp dport 1789 accept
        }
        chain forward { type filter hook forward priority 0; policy drop; }
        chain output  { type filter hook output priority 0; policy accept; }
      }
  - path: /usr/local/sbin/hokono-install-mix.sh
    permissions: "0755"
    content: |
      #!/bin/bash
      # Holt das statische mixnode-Binary aus der oeffentlichen Registry, prueft den Hash gegen das
      # veroeffentlichte Manifest und legt Wickelschluessel + Unit an. Idempotent.
      set -euo pipefail
      REG="${REG:-https://hokono.app/registry}"
      # Bootstrap: von hier holt der Mix das Directory (Gateways gw-hz-hel, gw-hz-nbg; Port 7443).
      # Danach kennt er alle Knoten aus dem Consensus selbst; die Liste hier muss nur EINEN
      # erreichbaren Eintrag behalten.
      DIRECTORY_FROM="${DIRECTORY_FROM:-95.217.167.198:7443,91.98.17.43:7443}"
      install -d -m 0755 /usr/local/bin
      id -u hokono >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin hokono
      install -d -m 0700 -o hokono -g hokono /var/lib/hokono
      tmp="$(mktemp -d)"; cd "$tmp"
      curl -fsSLO "$REG/SHA256SUMS"; curl -fsSLO "$REG/mixnode"; curl -fsSLO "$REG/PLATFORM"; curl -fsSLO "$REG/authorities.txt"
      grep -q "linux/amd64" PLATFORM || { echo "Registry-Plattform passt nicht"; exit 1; }
      sha256sum -c --ignore-missing SHA256SUMS
      install -m 0755 mixnode /usr/local/bin/hokono-mixnode
      cp SHA256SUMS /var/lib/hokono/SHA256SUMS.registry
      # Oeffentliche Authority-Schluessel (Pin-Liste, identisch zur App). Nur Signaturen mit diesen
      # Schluesseln zaehlen; ein Gateway kann dem Mix damit kein fremdes Directory unterschieben.
      grep -qE '^[0-9a-f]{64} [0-9a-f]{3904}$' authorities.txt || { echo "authorities.txt unplausibel"; exit 1; }
      install -m 0644 -o hokono -g hokono authorities.txt /var/lib/hokono/authorities.txt
      cd /; rm -rf "$tmp"
      # Wickelschluessel: lokal, nur root lesbar. Wer ihn loescht, kann node.key nicht mehr oeffnen.
      if [ ! -s /var/lib/hokono/wrap.key ]; then
        head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > /var/lib/hokono/wrap.key
        chmod 0600 /var/lib/hokono/wrap.key; chown hokono:hokono /var/lib/hokono/wrap.key
      fi
      cat > /etc/systemd/system/hokono-mix.service <<UNIT
      [Unit]
      Description=Hokono Mix (unabhaengiger Betreiber)
      After=network-online.target
      Wants=network-online.target
      [Service]
      User=hokono
      Group=hokono
      ExecStart=/usr/local/bin/hokono-mixnode --port 1789 --keyfile /var/lib/hokono/node.key --keywrap /var/lib/hokono/wrap.key --directory-from $DIRECTORY_FROM --authorities /var/lib/hokono/authorities.txt --directory-m 2 --rotate-days 7 --rotate-grace-hours 72 --replay-file /var/lib/hokono/replay.bin
      Restart=on-failure
      RestartSec=5
      StateDirectory=hokono
      StateDirectoryMode=0700
      StandardOutput=null
      StandardError=null
      NoNewPrivileges=yes
      ProtectSystem=strict
      ProtectHome=yes
      PrivateTmp=yes
      ReadWritePaths=/var/lib/hokono
      [Install]
      WantedBy=multi-user.target
      UNIT
      systemctl daemon-reload
      systemctl enable hokono-mix
      systemctl restart hokono-mix
      sleep 3
      systemctl is-active hokono-mix
      echo "Deskriptor-Material (nur oeffentlich):"; cat /var/lib/hokono/pubkeys.txt
runcmd:
  - systemctl restart systemd-journald
  - rm -rf /var/log/journal /run/log/journal
  - nft -c -f /etc/nftables.conf && systemctl enable --now nftables
  - sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config && systemctl restart ssh
  - /usr/local/sbin/hokono-install-mix.sh
  - echo "operator=__OPERATOR_TAG__ role=mix" > /var/lib/hokono/ROLE
  - rm -f /var/log/cloud-init-output.log /var/log/cloud-init.log
final_message: "Hokono-Mix bereit nach $UPTIME s. Naechster Schritt: check.sh, dann den JSON-Meldeblock aus der ANLEITUNG an Hokono."
