#!/usr/bin/env bash # TunnelFleet installer — this is the exact file the dashboard one-liner runs. # Verify it before bash: # curl -fsSL https://tunnelfleet.com/install.sh -o tunnelfleet.sh # curl -fsSL https://tunnelfleet.com/install.sh.sha256 | sha256sum -c - # TUNNELFLEET_TOKEN=... TUNNELFLEET_URL=https://tunnelfleet.com TUNNELFLEET_IP=... bash tunnelfleet.sh # # Per-server secrets never live in this file. They are passed as environment # variables and fetched over HTTPS with Authorization: Bearer. set -euo pipefail : "${TUNNELFLEET_TOKEN:?Set TUNNELFLEET_TOKEN to the enrollment token from your dashboard}" : "${TUNNELFLEET_URL:=https://tunnelfleet.com}" TUNNELFLEET_AGENT_SHA256="f2a108ba0a3f0d68e1a9002c64d6cd6a620ed1cebf562de354d9ba3ffb1fc938" PROVISIONED_MARKER="/opt/tunnelfleet/.provisioned" if [[ -f "${PROVISIONED_MARKER}" ]]; then echo "ERROR: This VPS was already provisioned by TunnelFleet." echo "Found marker: ${PROVISIONED_MARKER}" exit 1 fi function tunnelfleetPing { curl --silent --output /dev/null \ -H "Authorization: Bearer ${TUNNELFLEET_TOKEN}" \ --data "message=$1" \ "${TUNNELFLEET_URL}/api/servers/status" || true } eval "$(curl -fsSL -H "Authorization: Bearer ${TUNNELFLEET_TOKEN}" \ "${TUNNELFLEET_URL}/api/servers/bootstrap")" if [[ -z "${AGENT_SHA256:-}" ]]; then echo "ERROR: Control plane did not return AGENT_SHA256." exit 1 fi if [[ "${AGENT_SHA256}" != "${TUNNELFLEET_AGENT_SHA256}" ]]; then echo "ERROR: Agent checksum from the control plane does not match this installer." echo "Re-download install.sh from ${TUNNELFLEET_URL}/install.sh and try again." exit 1 fi REGISTERED_IP="${TUNNELFLEET_IP:-${REGISTERED_IP:-}}" if [[ -n "${REGISTERED_IP}" ]]; then echo "Validating server IP address..." ACTUAL_IP=$(curl --silent --max-time 10 https://ipinfo.io/ip 2>/dev/null \ || curl --silent --max-time 10 https://ifconfig.me 2>/dev/null \ || echo "") if [[ -z "${ACTUAL_IP}" ]]; then echo "WARNING: Could not determine this server's public IP. Skipping IP validation." elif [[ "${ACTUAL_IP}" != "${REGISTERED_IP}" ]]; then echo "ERROR: IP address mismatch detected!" echo " Registered IP : ${REGISTERED_IP}" echo " Actual IP : ${ACTUAL_IP}" exit 1 else echo "IP validation passed: ${ACTUAL_IP}" fi fi curl -fsSL -H "Authorization: Bearer ${TUNNELFLEET_TOKEN}" \ "${TUNNELFLEET_URL}/api/servers/provision-script" \ -o /tmp/tunnelfleet-host.sh # Host payload is authenticated (Bearer) and includes protocol containers, # a read-only Docker pull credential scoped to TunnelFleet protocol images, # and optional organization SSH keys. It is not this checksummed file. # shellcheck disable=SC1091 source /tmp/tunnelfleet-host.sh rm -f /tmp/tunnelfleet-host.sh mkdir -p /etc/tunnelfleet cat > /etc/tunnelfleet/agent.env << EOF TUNNELFLEET_TOKEN=${TUNNELFLEET_TOKEN} TUNNELFLEET_URL=${TUNNELFLEET_URL} EOF chmod 600 /etc/tunnelfleet/agent.env curl -fsSL -H "Authorization: Bearer ${TUNNELFLEET_TOKEN}" \ "${TUNNELFLEET_URL}/agent/install" -o /usr/local/bin/tunnelfleet-agent echo "${TUNNELFLEET_AGENT_SHA256} /usr/local/bin/tunnelfleet-agent" | sha256sum -c - \ || { echo "Agent checksum mismatch — aborting."; exit 1; } chmod 755 /usr/local/bin/tunnelfleet-agent cat > /etc/systemd/system/tunnelfleet-agent.service << 'EOF' [Unit] Description=TunnelFleet Agent After=network.target [Service] Type=simple EnvironmentFile=/etc/tunnelfleet/agent.env ExecStart=/usr/local/bin/tunnelfleet-agent Restart=always RestartSec=5 ProtectHome=yes PrivateTmp=yes [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable tunnelfleet-agent systemctl start tunnelfleet-agent tunnelfleetPing "Agent running" mkdir -p "$(dirname "${PROVISIONED_MARKER}")" { echo "provisioned_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" } > "${PROVISIONED_MARKER}" chmod 644 "${PROVISIONED_MARKER}" echo "TunnelFleet provision marker written to ${PROVISIONED_MARKER}"