summaryrefslogtreecommitdiff
path: root/server.sh
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-02-08 20:23:31 +0800
committerYour Name <you@example.com>2026-02-08 20:23:31 +0800
commitc0e80820010cc5b0fdea1eb69bafef7f575e3201 (patch)
tree94b8be5284bec668e1911bbe65e1b215ccd7657a /server.sh
Diffstat (limited to 'server.sh')
-rwxr-xr-xserver.sh151
1 files changed, 151 insertions, 0 deletions
diff --git a/server.sh b/server.sh
new file mode 100755
index 0000000..41b88ce
--- /dev/null
+++ b/server.sh
@@ -0,0 +1,151 @@
1#!/bin/bash
2# Install and configure headscale locally
3# Copy this directory to a VM and run: sudo ./install.sh <public_ip>
4# Example: sudo ./install.sh 37.27.166.244
5# docs:
6# this script assumes the ip addresses is pointed to the current machine, and this script runs on 0.0.0.0
7# configuration is kinda manual cuz the official packaging is for .deb and i want rhel based system
8# fallback (Designated Encrypted Relay for Packets) is disabled. if NAT traversal fails, there will be no connection
9set -e
10
11die() { echo "Error: $1" >&2; exit 1; }
12info() { echo " $1"; }
13
14[[ $EUID -eq 0 ]] || die "Must run as root"
15
16SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
17PUBLIC_IP="${1:?Usage: $0 <public_ip>}"
18
19HEADSCALE_VERSION="0.28.0"
20ARCH="amd64"
21
22echo "=== Installing Headscale ==="
23echo "Public IP: $PUBLIC_IP"
24echo ""
25
26# 1. Download and install binary from GitHub releases (no RPM exists, only deb and bare binary)
27info "Downloading headscale v${HEADSCALE_VERSION}..."
28curl -fsSL -o /var/tmp/headscale "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${ARCH}"
29install -m 755 /var/tmp/headscale /usr/local/bin/headscale
30rm -f /var/tmp/headscale
31info "Installed: $(headscale version 2>/dev/null | head -1)"
32
33# 2. Create headscale user/group (same as what the deb postinst does)
34info "Creating headscale user/group..."
35groupadd --force --system headscale
36useradd --system --shell /usr/sbin/nologin --gid headscale --home-dir /var/lib/headscale --comment headscale headscale 2>/dev/null || true
37
38# 3. Create directories: /etc/headscale (config), /var/lib/headscale (data/db), /var/run/headscale (socket)
39info "Creating directories..."
40mkdir -p /etc/headscale /var/lib/headscale /var/run/headscale
41chown headscale:headscale /var/lib/headscale /var/run/headscale
42
43# 4. Install systemd service (from official packaging/systemd/headscale.service,
44# only change: ExecStart points to /usr/local/bin/headscale instead of /usr/bin/headscale)
45info "Installing systemd service..."
46cat > /etc/systemd/system/headscale.service <<'EOF'
47[Unit]
48After=network.target
49Description=headscale coordination server for Tailscale
50X-Restart-Triggers=/etc/headscale/config.yaml
51
52[Service]
53Type=simple
54User=headscale
55Group=headscale
56ExecStart=/usr/local/bin/headscale serve
57ExecReload=/usr/bin/kill -HUP $MAINPID
58Restart=always
59RestartSec=5
60
61WorkingDirectory=/var/lib/headscale
62ReadWritePaths=/var/lib/headscale
63
64AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_CHOWN
65CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_CHOWN
66LockPersonality=true
67NoNewPrivileges=true
68PrivateDevices=true
69PrivateMounts=true
70PrivateTmp=true
71ProcSubset=pid
72ProtectClock=true
73ProtectControlGroups=true
74ProtectHome=true
75ProtectHostname=true
76ProtectKernelLogs=true
77ProtectKernelModules=true
78ProtectKernelTunables=true
79ProtectProc=invisible
80ProtectSystem=strict
81RemoveIPC=true
82RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
83RestrictNamespaces=true
84RestrictRealtime=true
85RestrictSUIDSGID=true
86RuntimeDirectory=headscale
87RuntimeDirectoryMode=0750
88StateDirectory=headscale
89StateDirectoryMode=0750
90SystemCallArchitectures=native
91SystemCallFilter=@chown
92SystemCallFilter=@system-service
93SystemCallFilter=~@privileged
94UMask=0077
95
96[Install]
97WantedBy=multi-user.target
98EOF
99
100# 5. Install config — example config with two changes:
101# - server_url: http://<public_ip>:8080 (so clients know the public address)
102# - listen_addr: 0.0.0.0:8080 (listen on all interfaces, not just localhost)
103info "Downloading example config..."
104curl -fsSL -o /var/tmp/config-example.yaml "https://raw.githubusercontent.com/juanfont/headscale/v${HEADSCALE_VERSION}/config-example.yaml"
105cp /var/tmp/config-example.yaml /etc/headscale/config.yaml
106rm -f /var/tmp/config-example.yaml
107sed -i "s|server_url: http://127.0.0.1:8080|server_url: http://${PUBLIC_IP}:8080|" /etc/headscale/config.yaml
108sed -i "s|listen_addr: 127.0.0.1:8080|listen_addr: 0.0.0.0:8080|" /etc/headscale/config.yaml
109# Disable DERP relays — all nodes have public IPs, force direct WireGuard connections only
110sed -i 's| - https://controlplane.tailscale.com/derpmap/default| # - https://controlplane.tailscale.com/derpmap/default|' /etc/headscale/config.yaml
111sed -i 's| auto_update_enabled: true| auto_update_enabled: false|' /etc/headscale/config.yaml
112chown -R headscale:headscale /etc/headscale
113
114# 6. Start headscale
115info "Enabling and starting headscale..."
116systemctl daemon-reload
117systemctl enable --now headscale
118
119# 7. Create default user and reusable auth key
120info "Creating default user..."
121headscale users create default 2>/dev/null || true
122USER_ID=$(headscale users list -o json 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['id'])")
123info "Creating auth key (reusable, 90 days)..."
124AUTH_KEY=$(headscale preauthkeys create --user "$USER_ID" --expiration 2160h --reusable)
125
126# 8. Register this machine as a tailscale client
127info "Installing tailscale client on this machine..."
128dnf install -y tailscale
129systemctl enable --now tailscaled
130tailscale up --login-server "http://${PUBLIC_IP}:8080" --authkey "$AUTH_KEY"
131
132echo ""
133echo "=== Headscale installed ==="
134echo ""
135echo "Health check:"
136echo " curl http://${PUBLIC_IP}:8080/health"
137echo ""
138echo "Auth key (reusable, 90 days):"
139echo " $AUTH_KEY"
140echo ""
141echo "Connect a client:"
142echo " sudo ./client.sh ${PUBLIC_IP} ${AUTH_KEY}"
143echo ""
144echo "Create a new user:"
145echo " headscale users create <username>"
146echo ""
147echo "Create an auth key:"
148echo " headscale preauthkeys create --user <USER_ID> --expiration 2160h --reusable"
149echo ""
150echo "List nodes:"
151echo " headscale node list"