Files
DragonCore-Modules/install_bridge.sh
2026-05-30 10:34:08 -03:00

269 lines
8.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
REPO_URL="${REPO_URL:-}"
BRANCH="${BRANCH:-main}"
PORT="${PORT:-6969}"
PORT_SET=0
OPEN_FIREWALL="${OPEN_FIREWALL:-yes}"
INSTALL_DIR="/opt/dragoncore-bridge"
CONFIG_DIR="/etc/dragoncore-bridge"
BIN="/usr/local/bin/dragoncore-bridge"
SERVICE="/etc/systemd/system/dragoncore-bridge.service"
while [ $# -gt 0 ]; do
case "$1" in
--repo) REPO_URL="$2"; shift 2 ;;
--branch) BRANCH="$2"; shift 2 ;;
--port) PORT="$2"; PORT_SET=1; shift 2 ;;
--open-firewall) OPEN_FIREWALL="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
if [ "$(id -u)" != "0" ]; then
echo "Run as root."
exit 1
fi
need_cmd() { command -v "$1" >/dev/null 2>&1; }
rand_hex() { openssl rand -hex "$1"; }
extract_listen_port() {
cfg="$1"
[ -f "$cfg" ] || return 1
listen="$(grep -o '"listen"[[:space:]]*:[[:space:]]*"[^"]*"' "$cfg" | head -1 | cut -d '"' -f4 || true)"
port="${listen##*:}"
case "$port" in
''|*[!0-9]*) return 1 ;;
*) printf '%s\n' "$port" ;;
esac
}
port_in_use() {
p="$1"
if need_cmd ss; then
ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${p}$"
return $?
fi
if need_cmd netstat; then
netstat -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${p}$"
return $?
fi
if need_cmd lsof; then
lsof -iTCP:"$p" -sTCP:LISTEN -Pn >/dev/null 2>&1
return $?
fi
return 1
}
choose_free_port() {
base="$1"
case "$base" in ''|*[!0-9]*) base=6969 ;; esac
i=0
while [ "$i" -le 100 ]; do
cand=$((base + i))
if ! port_in_use "$cand"; then
printf '%s\n' "$cand"
return 0
fi
i=$((i + 1))
done
echo "No free TCP port found from $base to $((base + 100))." >&2
exit 1
}
# The bridge source is intentionally Go 1.13-compatible so old VPS images
# such as Ubuntu 20.04 can compile it with their distro package.
# If the distro Go is older than 1.13, install a known-good official Go.
GO_MIN_MAJOR=1
GO_MIN_MINOR=13
GO_VERSION="${GO_VERSION:-1.21.13}"
GO_BIN=""
go_version_ok() {
if ! need_cmd go; then
return 1
fi
ver="$(go version 2>/dev/null | awk '{print $3}' | sed 's/^go//; s/[^0-9.].*$//')"
major="$(printf '%s' "$ver" | cut -d. -f1)"
minor="$(printf '%s' "$ver" | cut -d. -f2)"
case "$major:$minor" in
*[!0-9:]*|:*) return 1 ;;
esac
if [ "$major" -gt "$GO_MIN_MAJOR" ]; then
return 0
fi
if [ "$major" -eq "$GO_MIN_MAJOR" ] && [ "$minor" -ge "$GO_MIN_MINOR" ]; then
return 0
fi
return 1
}
install_official_go() {
arch="$(uname -m)"
case "$arch" in
x86_64|amd64) goarch="amd64" ;;
aarch64|arm64) goarch="arm64" ;;
armv6l|armv7l) goarch="armv6l" ;;
i386|i686) goarch="386" ;;
*) echo "Unsupported CPU architecture for automatic Go install: $arch"; exit 1 ;;
esac
url="https://go.dev/dl/go${GO_VERSION}.linux-${goarch}.tar.gz"
tmp="/tmp/go${GO_VERSION}.linux-${goarch}.tar.gz"
echo "Installing Go ${GO_VERSION} from ${url} ..."
curl -fsSL "$url" -o "$tmp"
rm -rf /usr/local/go
tar -C /usr/local -xzf "$tmp"
ln -sf /usr/local/go/bin/go /usr/local/bin/go
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
GO_BIN="/usr/local/go/bin/go"
}
export DEBIAN_FRONTEND=noninteractive
if need_cmd apt-get; then
apt-get update -y
apt-get install -y ca-certificates curl wget git openssl build-essential sqlite3 lsof iproute2
if ! need_cmd go; then
apt-get install -y golang-go || true
fi
elif need_cmd yum; then
yum install -y ca-certificates curl wget git openssl gcc make golang sqlite lsof iproute || yum install -y ca-certificates curl wget git openssl gcc make sqlite lsof iproute
elif need_cmd dnf; then
dnf install -y ca-certificates curl wget git openssl gcc make golang sqlite lsof iproute || dnf install -y ca-certificates curl wget git openssl gcc make sqlite lsof iproute
fi
if go_version_ok; then
GO_BIN="$(command -v go)"
else
install_official_go
fi
if ! "$GO_BIN" version >/dev/null 2>&1; then
echo "Go installation failed. Cannot build dragoncore-bridge."
exit 1
fi
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR"
chmod 700 "$CONFIG_DIR"
if [ -n "$REPO_URL" ]; then
rm -rf "$INSTALL_DIR/src"
git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR/src"
else
# Local install: useful if this script is executed from inside the repository.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
rm -rf "$INSTALL_DIR/src"
mkdir -p "$INSTALL_DIR/src"
cp -a "$SCRIPT_DIR"/. "$INSTALL_DIR/src/"
fi
cd "$INSTALL_DIR/src"
if need_cmd systemctl; then systemctl stop dragoncore-bridge 2>/dev/null || true; fi
"$GO_BIN" build -trimpath -ldflags "-s -w" -o "$BIN" .
chmod 755 "$BIN"
USER_NAME="admin"
PASSWORD="$(rand_hex 10)"
TOKEN="$(rand_hex 24)"
PANEL_URL=""
PANEL_SERVER_ID="0"
PANEL_SERVER_IP=""
PANEL_PUSH_INTERVAL="60"
if [ -f "$CONFIG_DIR/config.json" ]; then
echo "Existing config found: $CONFIG_DIR/config.json"
USER_NAME="$(grep -o '"username"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_DIR/config.json" | head -1 | cut -d '"' -f4 || echo admin)"
PASSWORD="$(grep -o '"password"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_DIR/config.json" | head -1 | cut -d '"' -f4 || rand_hex 10)"
TOKEN="$(grep -o '"token"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_DIR/config.json" | head -1 | cut -d '"' -f4 || rand_hex 24)"
PANEL_URL="$(grep -o '"panel_url"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_DIR/config.json" | head -1 | cut -d '"' -f4 || echo '')"
PANEL_SERVER_ID="$(grep -o '"panel_server_id"[[:space:]]*:[[:space:]]*[0-9]*' "$CONFIG_DIR/config.json" | head -1 | grep -o '[0-9]*$' || echo 0)"
PANEL_SERVER_IP="$(grep -o '"panel_server_ip"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_DIR/config.json" | head -1 | cut -d '"' -f4 || echo '')"
PANEL_PUSH_INTERVAL="$(grep -o '"panel_push_interval_seconds"[[:space:]]*:[[:space:]]*[0-9]*' "$CONFIG_DIR/config.json" | head -1 | grep -o '[0-9]*$' || echo 60)"
EXISTING_PORT="$(extract_listen_port "$CONFIG_DIR/config.json" || true)"
if [ "$PORT_SET" = "0" ] && [ -n "$EXISTING_PORT" ]; then
PORT="$EXISTING_PORT"
fi
fi
REQUESTED_PORT="$PORT"
PORT="$(choose_free_port "$PORT")"
if [ "$PORT" != "$REQUESTED_PORT" ]; then
echo "Port $REQUESTED_PORT is already in use. Using free port $PORT instead."
fi
cat > "$CONFIG_DIR/config.json" <<EOF
{
"listen": ":$PORT",
"username": "$USER_NAME",
"password": "$PASSWORD",
"token": "$TOKEN",
"data_dir": "$CONFIG_DIR",
"panel_url": "$PANEL_URL",
"panel_server_id": $PANEL_SERVER_ID,
"panel_server_ip": "$PANEL_SERVER_IP",
"panel_push_interval_seconds": $PANEL_PUSH_INTERVAL
}
EOF
chmod 600 "$CONFIG_DIR/config.json"
cat > "$SERVICE" <<EOF
[Unit]
Description=DragonCore Generic Bridge API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=$BIN -config $CONFIG_DIR/config.json
Restart=always
RestartSec=3
User=root
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now dragoncore-bridge
sleep 1
if ! systemctl is-active --quiet dragoncore-bridge; then
echo "dragoncore-bridge failed to start. Last logs:" >&2
journalctl -u dragoncore-bridge -n 40 --no-pager >&2 || true
exit 1
fi
if [ "$OPEN_FIREWALL" = "yes" ] || [ "$OPEN_FIREWALL" = "true" ] || [ "$OPEN_FIREWALL" = "1" ]; then
if need_cmd ufw && ufw status 2>/dev/null | grep -qi "Status: active"; then
ufw allow "$PORT"/tcp || true
fi
if need_cmd firewall-cmd && firewall-cmd --state >/dev/null 2>&1; then
firewall-cmd --permanent --add-port="$PORT/tcp" || true
firewall-cmd --reload || true
fi
if need_cmd iptables; then
iptables -C INPUT -p tcp --dport "$PORT" -j ACCEPT 2>/dev/null || iptables -I INPUT -p tcp --dport "$PORT" -j ACCEPT || true
if need_cmd netfilter-persistent; then netfilter-persistent save || true; fi
if need_cmd iptables-save && [ -d /etc/iptables ]; then iptables-save > /etc/iptables/rules.v4 || true; fi
fi
fi
PUBLIC_IP="$(curl -fsS --max-time 4 https://api.ipify.org 2>/dev/null || hostname -I | awk '{print $1}')"
echo
printf '%s\n' '============================================================'
printf '%s\n' 'DragonCore Bridge installed.'
printf '%s\n' 'Use these values in the DragonCore Panel:'
printf 'API Type : %s\n' 'Bridge Generic'
printf 'IP : %s\n' "$PUBLIC_IP"
printf 'API Port : %s\n' "$PORT"
printf 'User : %s\n' "$USER_NAME"
printf 'Password : %s\n' "$PASSWORD"
printf 'Legacy token/Senha header: %s\n' "$TOKEN"
printf '%s\n' 'Service commands:'
printf '%s\n' ' systemctl status dragoncore-bridge'
printf '%s\n' ' journalctl -u dragoncore-bridge -f'
printf '%s\n' '============================================================'