diff --git a/Node/join.sh b/Node/join.sh index 26796936..1a6c1003 100755 --- a/Node/join.sh +++ b/Node/join.sh @@ -1,70 +1,64 @@ #!/bin/bash -# Montana Node — авто-присоединение к сети. +# Montana Node — полностью децентрализованное присоединение к сети. +# По спеке Montana Protocol v35.25.0 — admission через VDF + Selection event, +# без admin-токенов и человеческих gate-keepers. # -# Два режима: +# Одна команда (любой человек, никакого TOKEN): +# ALIAS=cologne LABEL=Köln COUNTRY=DE HOSTING=Hetzner COORDS="50.94,6.96" \ +# bash <(curl -sk https://hub.montana.quest/efir369999/montana/raw/branch/main/Node/join.sh) # -# 1. Permissionless P2P-only (любой может) — узел в сети Montana, на карте /net/, -# НО НЕ в cascade VPN balancer: -# ALIAS=cologne LABEL=Köln COUNTRY=DE HOSTING=Hetzner \ -# COORDS="50.94,6.96" bash join.sh +# Поднимает оба слоя сразу (Montana Mesh whitepaper §2: оператор обязан запустить оба): +# - montana-node :8444 — p2p TimeChain, self-sovereign identity, VDF kandidatura +# - xray :443 — VPN backend (Reality+Vision), готов к включению в cascade # -# 2. Permissioned VPN-backend (нужен TOKEN от admin) — узел дополнительно -# попадает в Helsinki cascade VPN balancer: -# ROLE=vpn-backend TOKEN= \ -# ALIAS=cologne LABEL=Köln COUNTRY=DE HOSTING=Hetzner \ -# COORDS="50.94,6.96" bash join.sh +# Жизненный цикл узла: +# 1. join.sh поднимает оба слоя на новом сервере +# 2. POST /vpn/node/join → orchestrator проверяет TCP :8444 + NodeTable membership +# 3. Если узел уже в NodeTable Active (повторный join после VDF) → попадает в cascade сразу +# 4. Иначе — статус 'candidate' (виден на /net/ карте). Montana-node идёт CandidateVdf (~10ч). +# 5. После selection event → Active в NodeTable + 13 Ɉ welcome-bonus +# 6. Orchestrator-promote.timer (каждые 5 минут) auto-promote в cascade когда видит Active + set -euo pipefail -: "${ALIAS:?нужен ALIAS, например: amsterdam}" -: "${LABEL:?нужен LABEL, например: Amsterdam}" -: "${COUNTRY:?нужен COUNTRY 2-буквы, например: NL}" -: "${HOSTING:?нужен HOSTING, например: DigitalOcean}" -: "${COORDS:?нужны COORDS \"lat,lon\", например: 52.37,4.89}" -ROLE="${ROLE:-node-only}" # node-only | vpn-backend +: "${ALIAS:?нужен ALIAS (например: cologne)}" +: "${LABEL:?нужен LABEL (например: Köln)}" +: "${COUNTRY:?нужен COUNTRY (2 буквы, например: DE)}" +: "${HOSTING:?нужен HOSTING (например: Hetzner)}" +: "${COORDS:?нужны COORDS \"lat,lon\" (например: 50.94,6.96)}" + ORCH="${ORCH:-https://montana.quest/vpn/node}" HUB="${HUB:-https://hub.montana.quest/efir369999/montana/raw/branch/main}" -BOOTSTRAP_FROM="${BOOTSTRAP_FROM:-cdn.montana.quest}" - -if [ "$ROLE" = "vpn-backend" ]; then - : "${TOKEN:?для vpn-backend нужен TOKEN от admin}" -fi PUBLIC_IP="$(curl -s https://api.ipify.org)" -echo "==> Montana Node join: $ALIAS ($LABEL, $COUNTRY/$HOSTING) role=$ROLE ip=$PUBLIC_IP" +echo "==> Montana Node join: $ALIAS ($LABEL, $COUNTRY/$HOSTING) @ $PUBLIC_IP" -# 1. Зависимости +# 1. зависимости export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq curl ca-certificates openssl unzip jq -# 2. ufw +# 2. ufw — оба порта открыты (whitepaper §2: и :8444 и :443) ufw default deny incoming >/dev/null 2>&1 || true ufw default allow outgoing >/dev/null 2>&1 || true ufw allow 22/tcp >/dev/null -ufw allow 8444/tcp >/dev/null # p2p Montana — всегда открыт -[ "$ROLE" = "vpn-backend" ] && ufw allow 443/tcp >/dev/null +ufw allow 8444/tcp >/dev/null +ufw allow 443/tcp >/dev/null echo "y" | ufw enable >/dev/null -# 3. montana-node — скачать бинарь с хаба, manifest с хаба +# 3. montana-node бинарь + manifest с хаба (публично) mkdir -p /etc/montana -if [ ! -x /usr/local/bin/montana-node ]; then - echo "==> Скачиваю montana-node бинарь с $HUB/Node/bin/montana-node" - curl -sL "$HUB/Node/bin/montana-node" -o /usr/local/bin/montana-node - chmod +x /usr/local/bin/montana-node -fi -if [ ! -f /etc/montana/genesis-manifest.json ]; then - curl -sL "$HUB/Node/genesis-manifest.json" -o /etc/montana/genesis-manifest.json -fi +[ -x /usr/local/bin/montana-node ] || { curl -sL "$HUB/Node/bin/montana-node" -o /usr/local/bin/montana-node; chmod +x /usr/local/bin/montana-node; } +[ -f /etc/montana/genesis-manifest.json ] || curl -sL "$HUB/Node/genesis-manifest.json" -o /etc/montana/genesis-manifest.json id montana &>/dev/null || useradd -r -m -d /var/lib/montana -s /usr/sbin/nologin montana mkdir -p /var/lib/montana chown -R montana:montana /var/lib/montana && chmod 700 /var/lib/montana - [ -f /var/lib/montana/identity.bin ] || sudo -u montana /usr/local/bin/montana-node init --data-dir /var/lib/montana cat > /etc/systemd/system/montana-node.service <&1 | tail -3 - mkdir -p /usr/local/etc/xray - UUID="$(xray uuid)" - KEYS="$(xray x25519)" - PRIV="$(echo "$KEYS" | awk '/PrivateKey/ {print $NF}')" - PBK="$(echo "$KEYS" | awk '/Password/ {print $NF}')" - SID="$(openssl rand -hex 8)" - cat > /usr/local/etc/xray/config.json <&1 | tail -3 +mkdir -p /usr/local/etc/xray +UUID="$(xray uuid)" +KEYS="$(xray x25519)" +PRIV="$(echo "$KEYS" | awk '/PrivateKey/ {print $NF}')" +PBK="$(echo "$KEYS" | awk '/Password/ {print $NF}')" +SID="$(openssl rand -hex 8)" + +cat > /usr/local/etc/xray/config.json </dev/null - systemctl enable --now xray -fi +xray -test -config /usr/local/etc/xray/config.json >/dev/null +systemctl enable --now xray -# 5. Регистрация в orchestrator +# 6. POST /join (без TOKEN — admission по спеке Montana) LAT="$(echo "$COORDS" | cut -d, -f1)" LON="$(echo "$COORDS" | cut -d, -f2)" - -if [ "$ROLE" = "vpn-backend" ]; then - ENDPOINT="$ORCH/register" - PAYLOAD=$(jq -nc \ - --arg alias "$ALIAS" --arg ip "$PUBLIC_IP" --arg country "$COUNTRY" \ - --arg hosting "$HOSTING" --arg label "$LABEL" --argjson lat "$LAT" --argjson lon "$LON" \ - --arg pbk "$PBK" --arg uuid "$UUID" --arg sid "$SID" --arg role "$ROLE" --arg secret "$TOKEN" \ - '{alias:$alias,ip:$ip,country:$country,hosting:$hosting,label:$label,coords:[$lat,$lon],reality_pbk:$pbk,reality_uuid:$uuid,reality_sid:$sid,role:$role,secret:$secret}') -else - ENDPOINT="$ORCH/announce" - PAYLOAD=$(jq -nc \ - --arg alias "$ALIAS" --arg ip "$PUBLIC_IP" --arg country "$COUNTRY" \ - --arg hosting "$HOSTING" --arg label "$LABEL" --argjson lat "$LAT" --argjson lon "$LON" \ - '{alias:$alias,ip:$ip,country:$country,hosting:$hosting,label:$label,coords:[$lat,$lon]}') -fi - -RESP=$(curl -sk -X POST "$ENDPOINT" -H 'Content-Type: application/json' -d "$PAYLOAD") -echo "orchestrator ($ROLE → $ENDPOINT): $RESP" +PAYLOAD=$(jq -nc \ + --arg alias "$ALIAS" --arg ip "$PUBLIC_IP" --arg country "$COUNTRY" \ + --arg hosting "$HOSTING" --arg label "$LABEL" --argjson lat "$LAT" --argjson lon "$LON" \ + --arg acc "$ACCOUNT_ID" --arg nid "$NODE_ID" \ + --arg pbk "$PBK" --arg uuid "$UUID" --arg sid "$SID" \ + '{alias:$alias,ip:$ip,country:$country,hosting:$hosting,label:$label,coords:[$lat,$lon],account_id:$acc,node_id:$nid,reality_pbk:$pbk,reality_uuid:$uuid,reality_sid:$sid}') +RESP=$(curl -sk -X POST "$ORCH/join" -H 'Content-Type: application/json' -d "$PAYLOAD") +echo +echo "orchestrator: $RESP" cat <