Files
netbird/release_files/install-steamos.sh
Ashley Mensah 76838e9170 fix(client): remove NB_ENABLE_NETSTACK_LOCAL_FORWARDING default
In netstack mode with local forwarding enabled, ICMP packets get
replied to by both the gVisor netstack and the native OS (via the
forwarder), causing duplicate ping responses. Local forwarding is
only needed when remote peers must reach host-local services, so
it should not be on by default.
2026-04-30 12:45:16 +02:00

401 lines
12 KiB
Bash
Executable File

#!/bin/bash
# NetBird installer for SteamOS (Steam Deck)
#
# Installs NetBird as a rootless, user-level service running entirely from /home.
# Uses netstack mode (userspace WireGuard) — no root, no sysext, no TUN device needed.
# Survives all SteamOS updates without intervention.
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/netbirdio/netbird/main/release_files/install-steamos.sh | bash
# bash install-steamos.sh --update
# bash install-steamos.sh --uninstall
#
# Environment variables:
# NETBIRD_RELEASE - Version to install (default: "latest")
# GITHUB_TOKEN - GitHub token for rate-limited API calls
# NB_MANAGEMENT_URL - Custom management server URL
# NB_ADMIN_URL - Custom admin dashboard URL
# NB_SETUP_KEY - Setup key for automatic authentication
set -euo pipefail
OWNER="netbirdio"
REPO="netbird"
BINARY="netbird"
INSTALL_DIR="${HOME}/.local/bin"
CONFIG_DIR="${HOME}/.config/netbird"
STATE_DIR="${HOME}/.local/share/netbird"
SYSTEMD_DIR="${HOME}/.config/systemd/user"
SERVICE_NAME="netbird"
NETBIRD_RELEASE="${NETBIRD_RELEASE:-latest}"
TAG_NAME=""
# --- Logging ---
info() { printf '\033[1;32m[netbird]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[netbird]\033[0m %s\n' "$*" >&2; }
error() { printf '\033[1;31m[netbird]\033[0m %s\n' "$*" >&2; exit 1; }
# --- Validation ---
check_steamos() {
if [[ ! -f /etc/os-release ]]; then
error "Cannot detect OS: /etc/os-release not found"
fi
. /etc/os-release
# Accept steamos, or allow --force for other immutable Linux distros
if [[ "${ID:-}" != "steamos" ]] && [[ "${FORCE:-}" != "true" ]]; then
warn "This script is designed for SteamOS (detected: ${ID:-unknown})"
warn "Set FORCE=true to install anyway on immutable Linux distros"
exit 1
fi
info "Detected ${PRETTY_NAME:-SteamOS}"
}
check_arch() {
case "$(uname -m)" in
x86_64|amd64) ARCH="amd64" ;;
aarch64|arm64) ARCH="arm64" ;;
*)
error "Unsupported architecture: $(uname -m)"
;;
esac
}
check_dependencies() {
local missing=""
for cmd in curl tar systemctl; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing="$missing $cmd"
fi
done
if [[ -n "$missing" ]]; then
error "Missing required commands:$missing"
fi
# Verify user-level systemd is functional
if ! systemctl --user status >/dev/null 2>&1; then
error "systemctl --user is not functional. Is systemd user session running?"
fi
}
# --- Release fetching (adapted from install.sh) ---
get_release() {
local release="$1"
if [[ "$release" == "latest" ]]; then
local url="https://pkgs.netbird.io/releases/latest"
else
local url="https://api.github.com/repos/${OWNER}/${REPO}/releases/tags/${release}"
fi
local output=""
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
output=$(curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" "$url")
else
output=$(curl -fsSL "$url")
fi
TAG_NAME=$(echo "$output" | grep -Eo '"tag_name":\s*"v([0-9]+\.){2}[0-9]+"' | tail -n 1)
echo "$TAG_NAME" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+'
}
download_binary() {
local dest_dir="${1:-$INSTALL_DIR}"
local version
version=$(get_release "$NETBIRD_RELEASE")
if [[ -z "$version" ]]; then
error "Failed to determine NetBird version"
fi
local version_num="${version#v}"
local tarball="${BINARY}_${version_num}_linux_${ARCH}.tar.gz"
local checksums="${BINARY}_${version_num}_checksums.txt"
local base_url="https://github.com/${OWNER}/${REPO}/releases/download/${version}"
info "Downloading NetBird ${version} for ${ARCH}..."
local tmp_dir
tmp_dir=$(mktemp -d)
trap "rm -rf '$tmp_dir'" EXIT
local auth_header=""
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
auth_header="Authorization: token ${GITHUB_TOKEN}"
fi
# Download tarball and checksums
curl -fsSL ${auth_header:+-H "$auth_header"} -o "${tmp_dir}/${tarball}" "${base_url}/${tarball}"
curl -fsSL ${auth_header:+-H "$auth_header"} -o "${tmp_dir}/${checksums}" "${base_url}/${checksums}"
# Verify checksum
info "Verifying checksum..."
local expected
expected=$(grep " ${tarball}$" "${tmp_dir}/${checksums}" | awk '{print $1}')
if [[ -z "$expected" ]]; then
error "Checksum for ${tarball} not found in ${checksums}"
fi
local actual
actual=$(sha256sum "${tmp_dir}/${tarball}" | awk '{print $1}')
if [[ "$expected" != "$actual" ]]; then
error "Checksum mismatch for ${tarball}: expected ${expected}, got ${actual}"
fi
info "Checksum verified"
tar -xzf "${tmp_dir}/${tarball}" -C "$tmp_dir" "$BINARY"
mkdir -p "$dest_dir"
mv "${tmp_dir}/${BINARY}" "${dest_dir}/${BINARY}"
chmod 755 "${dest_dir}/${BINARY}"
info "Installed ${dest_dir}/${BINARY} (${version})"
}
# --- Systemd user service ---
write_service_unit() {
mkdir -p "$SYSTEMD_DIR"
mkdir -p "$CONFIG_DIR"
mkdir -p "$STATE_DIR"
cat > "${SYSTEMD_DIR}/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=NetBird Client (SteamOS)
Documentation=https://netbird.io/docs
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=NB_USE_NETSTACK_MODE=true
Environment=NB_CONFIG=${CONFIG_DIR}/config.json
Environment=NB_STATE_DIR=${STATE_DIR}
Environment=NB_DAEMON_ADDR=unix://${STATE_DIR}/netbird.sock
Environment=NB_LOG_FILE=${STATE_DIR}/client.log
Environment=NB_DISABLE_DNS=true
ExecStart=${INSTALL_DIR}/${BINARY} service run
Restart=on-failure
RestartSec=5
TimeoutStopSec=10
[Install]
WantedBy=default.target
EOF
info "Created systemd user service"
}
enable_service() {
systemctl --user daemon-reload
systemctl --user enable "${SERVICE_NAME}.service"
systemctl --user start "${SERVICE_NAME}.service"
# Enable lingering so the service runs even when not logged into a desktop session
# This requires loginctl which may or may not work without root on SteamOS
if command -v loginctl >/dev/null 2>&1; then
loginctl enable-linger "$(whoami)" 2>/dev/null || \
warn "Could not enable linger. Service will only run while logged in."
fi
info "Service enabled and started"
}
# --- Install ---
do_install() {
check_steamos
check_arch
check_dependencies
# Check for existing installation
if [[ -x "${INSTALL_DIR}/${BINARY}" ]]; then
warn "NetBird is already installed at ${INSTALL_DIR}/${BINARY}"
warn "Use --update to update or --uninstall to remove first"
exit 1
fi
download_binary
write_service_unit
enable_service
# Configure shell environment so the CLI finds the daemon socket and binary
local shell_rc="${HOME}/.bashrc"
if [[ -f "${HOME}/.zshrc" ]]; then
shell_rc="${HOME}/.zshrc"
fi
local daemon_addr="unix://${STATE_DIR}/netbird.sock"
if ! grep -qF "# Added by NetBird installer" "$shell_rc" 2>/dev/null; then
cat >> "$shell_rc" <<SHELLRC
# Added by NetBird installer
export PATH="${INSTALL_DIR}:\$PATH"
export NB_DAEMON_ADDR="${daemon_addr}"
export NB_CONFIG="${CONFIG_DIR}/config.json"
SHELLRC
info "Added NetBird environment to ${shell_rc}"
fi
# Also export for the current script so auto-connect works
export PATH="${INSTALL_DIR}:${PATH}"
export NB_DAEMON_ADDR="${daemon_addr}"
export NB_CONFIG="${CONFIG_DIR}/config.json"
info ""
info "NetBird installed successfully!"
info ""
info "The daemon is running. To connect:"
info ""
if [[ -n "${NB_SETUP_KEY:-}" ]]; then
info " Connecting with provided setup key..."
"${INSTALL_DIR}/${BINARY}" up --setup-key "$NB_SETUP_KEY" \
${NB_MANAGEMENT_URL:+--management-url "$NB_MANAGEMENT_URL"} \
${NB_ADMIN_URL:+--admin-url "$NB_ADMIN_URL"} || \
warn "Auto-connect failed. Run 'netbird up --setup-key <KEY>' manually."
else
info " With a setup key (recommended for Steam Deck):"
info " netbird up --setup-key <YOUR-SETUP-KEY>"
info ""
info " With SSO (device flow):"
info " netbird up"
info " Then open the printed URL on your phone or PC."
fi
info ""
info "Check status: netbird status"
info "View logs: journalctl --user -u ${SERVICE_NAME} -f"
}
# --- Update ---
do_update() {
if [[ ! -x "${INSTALL_DIR}/${BINARY}" ]]; then
error "NetBird is not installed. Run without --update to install."
fi
local installed_version
installed_version=$("${INSTALL_DIR}/${BINARY}" version 2>/dev/null || echo "unknown")
local latest_version
latest_version=$(get_release "latest")
latest_version="${latest_version#v}"
if [[ "$installed_version" == "$latest_version" ]]; then
info "Already on latest version (${installed_version})"
exit 0
fi
info "Updating ${installed_version} -> ${latest_version}"
check_arch
# Download and verify new binary to a staging directory before touching the running service
local staging_dir
staging_dir=$(mktemp -d)
trap "rm -rf '$staging_dir'" RETURN
download_binary "$staging_dir"
# Regenerate the unit file in case paths or env vars changed
write_service_unit
# Only stop the service after the new binary is ready
systemctl --user stop "${SERVICE_NAME}.service" 2>/dev/null || true
# Atomic swap: move staged binary into place
mv "${staging_dir}/${BINARY}" "${INSTALL_DIR}/${BINARY}"
chmod 755 "${INSTALL_DIR}/${BINARY}"
systemctl --user daemon-reload
systemctl --user start "${SERVICE_NAME}.service"
info "Updated to ${latest_version}"
}
# --- Uninstall ---
do_uninstall() {
info "Uninstalling NetBird..."
# Stop and disable service
systemctl --user stop "${SERVICE_NAME}.service" 2>/dev/null || true
systemctl --user disable "${SERVICE_NAME}.service" 2>/dev/null || true
# Remove files
rm -f "${SYSTEMD_DIR}/${SERVICE_NAME}.service"
rm -f "${INSTALL_DIR}/${BINARY}"
systemctl --user daemon-reload
info "Removed binary and service"
# Ask about config/state
if [[ -d "$CONFIG_DIR" ]] || [[ -d "$STATE_DIR" ]]; then
info ""
info "Config and state directories still exist:"
[[ -d "$CONFIG_DIR" ]] && info " ${CONFIG_DIR}"
[[ -d "$STATE_DIR" ]] && info " ${STATE_DIR}"
info ""
info "To remove them (this deletes auth tokens and config):"
info " rm -rf ${CONFIG_DIR} ${STATE_DIR}"
fi
info "NetBird uninstalled"
}
# --- Main ---
main() {
local action="${1:-}"
case "$action" in
--update)
do_update
;;
--uninstall)
do_uninstall
;;
--help|-h)
cat <<USAGE
NetBird installer for SteamOS (Steam Deck)
Usage:
install-steamos.sh Install NetBird
install-steamos.sh --update Update to latest version
install-steamos.sh --uninstall Remove NetBird
Environment variables:
NETBIRD_RELEASE Version to install (default: latest)
GITHUB_TOKEN GitHub token for API rate limits
NB_SETUP_KEY Setup key for automatic authentication
NB_MANAGEMENT_URL Custom management server URL
NB_ADMIN_URL Custom admin dashboard URL
FORCE Set to "true" to install on non-SteamOS systems
Files:
${INSTALL_DIR}/${BINARY} Binary
${CONFIG_DIR}/config.json Config
${STATE_DIR}/ State, socket, logs
${SYSTEMD_DIR}/${SERVICE_NAME}.service Systemd unit
USAGE
;;
"")
do_install
;;
*)
error "Unknown option: $action (use --help for usage)"
;;
esac
}
main "$@"