montana/Node/join.sh
Montana Moscow Node 4cbe83d0e7 Montana/Node/join.sh: dual-mode permissioned/permissionless
ROLE=node-only (default) — без TOKEN, использует /vpn/node/announce.
P2P-узел Montana, ставится через wget+manifest с хаба. Появляется
на /net/ карте как валидатор. НЕ попадает в Helsinki cascade VPN.

ROLE=vpn-backend — нужен TOKEN от admin, /vpn/node/register.
Узел дополнительно генерирует Reality keypair и попадает в cascade
balancer Helsinki — обслуживает VPN-клиентов.

Бинарь и manifest теперь публично доступны на хабе:
  HUB/Node/bin/montana-node
  HUB/Node/genesis-manifest.json
2026-05-15 16:57:34 +03:00

160 lines
7.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Montana Node — авто-присоединение к сети.
#
# Два режима:
#
# 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
#
# 2. Permissioned VPN-backend (нужен TOKEN от admin) — узел дополнительно
# попадает в Helsinki cascade VPN balancer:
# ROLE=vpn-backend TOKEN=<orch_token> \
# ALIAS=cologne LABEL=Köln COUNTRY=DE HOSTING=Hetzner \
# COORDS="50.94,6.96" bash join.sh
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
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"
# 1. Зависимости
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates openssl unzip jq
# 2. ufw
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
echo "y" | ufw enable >/dev/null
# 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
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 <<UNIT
[Unit]
Description=Montana Local Node (M8 cross-machine, Proof-of-Time)
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=montana
Group=montana
ExecStart=/usr/local/bin/montana-node start --data-dir /var/lib/montana --listen /ip4/0.0.0.0/tcp/8444 --genesis-manifest /etc/montana/genesis-manifest.json
Restart=on-failure
RestartSec=10
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/montana
CPUQuota=200%
LimitNOFILE=8192
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now montana-node
sleep 5
systemctl is-active montana-node && echo "montana-node: active" || echo "!! montana-node fail"
# 4. xray (только для vpn-backend role)
PBK="" UUID="" SID=""
if [ "$ROLE" = "vpn-backend" ]; then
curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh | bash -s -- install --without-geodata 2>&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 <<XCFG
{
"log": {"loglevel":"warning","access":"/var/log/xray/access.log","error":"/var/log/xray/error.log"},
"dns": {"servers":["https+local://1.1.1.1/dns-query","1.1.1.1"],"queryStrategy":"UseIP"},
"inbounds": [{"tag":"reality-${ALIAS}-backend","listen":"0.0.0.0","port":443,"protocol":"vless",
"settings":{"clients":[{"id":"${UUID}","email":"${ALIAS}-cascade","flow":"xtls-rprx-vision"}],"decryption":"none"},
"streamSettings":{"network":"tcp","security":"reality",
"realitySettings":{"show":false,"dest":"www.googletagmanager.com:443","xver":0,
"serverNames":["www.googletagmanager.com"],"privateKey":"${PRIV}","shortIds":["${SID}"]}},
"sniffing":{"enabled":true,"destOverride":["http","tls","quic"]}}],
"outbounds":[{"tag":"direct","protocol":"freedom","settings":{"domainStrategy":"UseIP"}},
{"tag":"blocked","protocol":"blackhole"},{"tag":"dns-out","protocol":"dns"}],
"routing":{"rules":[{"type":"field","port":"53","outboundTag":"dns-out"},
{"type":"field","ip":["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8"],"outboundTag":"blocked"},
{"type":"field","protocol":["bittorrent"],"outboundTag":"blocked"}]}
}
XCFG
xray -test -config /usr/local/etc/xray/config.json >/dev/null
systemctl enable --now xray
fi
# 5. Регистрация в orchestrator
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"
cat <<DONE
╔══════════════════════════════════════════════════╗
║ Montana Node "$ALIAS" подключён ($ROLE) ║
╠══════════════════════════════════════════════════╣
║ montana-node :8444 — p2p Bootstrap→CandidateVdf
║ $([ "$ROLE" = vpn-backend ] && echo "xray :443 — Reality VPN backend cascade
║ reality_pbk: $PBK" || echo "(VPN не включён — это p2p-only узел)")
На /net/ карте появишься через ~30 сек (pull-aggregator).
$([ "$ROLE" = vpn-backend ] && echo "║ В Helsinki cascade balancer уже добавлен.")
╚══════════════════════════════════════════════════╝
DONE