montana/Node/join.sh
2026-05-18 22:11:45 +03:00

208 lines
9.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 fail2ban
# 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
# fail2ban: ssh brute-force защита (memory project_fail2ban)
cat > /etc/fail2ban/jail.d/montana.conf <<F2B
[sshd]
enabled = true
maxretry = 3
findtime = 10m
bantime = 1h
bantime.increment = true
bantime.maxtime = 1w
backend = auto
F2B
systemctl enable --now fail2ban
# 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) — УНИВЕРСАЛЬНЫЙ ключ на все узлы.
# Публичные параметры зашиты в скрипте (UUID/PBK/SID — попадают в подписку клиента).
# privateKey — секрет, читается из env VPN_PRIVKEY либо из /etc/montana/vpn-privkey
# (admin кладёт по защищённому каналу). НЕ хранить в этом файле.
UUID="e6d355e2-2d79-4c96-a373-3b0e6b6f4b0d"
PBK="EkTs2aGKnFNgFZ0f7wgft2sJp3VjwFQqIrwkZKM4gD8"
SID="302805bc0c25e504"
SNI="www.googletagmanager.com"
XRAY_PIN="v25.10.21" # фиксируем версию xray-install
if [ "$ROLE" = "vpn-backend" ]; then
if [ -z "${VPN_PRIVKEY:-}" ] && [ -f /etc/montana/vpn-privkey ]; then
VPN_PRIVKEY="$(cat /etc/montana/vpn-privkey)"
fi
: "${VPN_PRIVKEY:?нужен VPN_PRIVKEY (Reality privateKey, у admin) для ROLE=vpn-backend}"
PRIV="$VPN_PRIVKEY"
mkdir -p /etc/montana && chmod 700 /etc/montana
install -m 0600 /dev/stdin /etc/montana/vpn-privkey <<<"$PRIV"
curl -fsSL "https://github.com/XTLS/Xray-install/raw/${XRAY_PIN}/install-release.sh" | bash -s -- install --without-geodata 2>&1 | tail -3
mkdir -p /usr/local/etc/xray /var/log/xray
chown -R nobody:nogroup /var/log/xray 2>/dev/null || true
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}","listen":"0.0.0.0","port":443,"protocol":"vless",
"settings":{"clients":[{"id":"${UUID}","email":"montana-universal","flow":"xtls-rprx-vision"}],"decryption":"none"},
"streamSettings":{"network":"tcp","security":"reality",
"realitySettings":{"show":false,"dest":"${SNI}:443","xver":0,
"serverNames":["${SNI}"],"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
mkdir -p /etc/systemd/system/xray.service.d
cat > /etc/systemd/system/xray.service.d/30-autorestart.conf <<EOF
[Service]
Restart=always
RestartSec=3
ExecStopPost=/usr/local/bin/montana-vpn-deregister
[Unit]
StartLimitIntervalSec=300
StartLimitBurst=10
EOF
cat > /usr/local/bin/montana-vpn-deregister <<DRG
#!/bin/sh
# Удаляет наш IP из cdn.montana.quest multi-A при штатной остановке xray.
[ -f /etc/montana/orchestrator-token ] || exit 0
IP=\$(curl -s --max-time 5 https://api.ipify.org)
TOKEN=\$(cat /etc/montana/orchestrator-token)
curl -s --max-time 10 -X POST -H "Content-Type: application/json" \
--data "{\"ip\":\"\${IP}\",\"secret\":\"\${TOKEN}\"}" \
"${ORCH}/deregister" >/dev/null 2>&1 || true
DRG
chmod 755 /usr/local/bin/montana-vpn-deregister
if [ -n "${TOKEN:-}" ]; then
install -m 0600 /dev/stdin /etc/montana/orchestrator-token <<<"$TOKEN"
fi
systemctl daemon-reload
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