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