From c0e80820010cc5b0fdea1eb69bafef7f575e3201 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 8 Feb 2026 20:23:31 +0800 Subject: first --- client.sh | 44 ++++++++++++++++++ docs | 37 +++++++++++++++ server.sh | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100755 client.sh create mode 100644 docs create mode 100755 server.sh diff --git a/client.sh b/client.sh new file mode 100755 index 0000000..2118a73 --- /dev/null +++ b/client.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Install tailscale and register with a headscale server +# Copy to a VM and run: sudo ./client.sh +# Example: sudo ./client.sh 37.27.166.243 hskey-auth-xxxx +# macOS CLI: echo 'alias tailscale="/Applications/Tailscale.app/Contents/MacOS/Tailscale"' >> ~/.zshrc +set -e + +die() { echo "Error: $1" >&2; exit 1; } +info() { echo " $1"; } + +[[ $EUID -eq 0 ]] || die "Must run as root" + +SERVER_IP="${1:?Usage: $0 }" +AUTH_KEY="${2:?Usage: $0 }" +LOGIN_SERVER="http://${SERVER_IP}:8080" + +echo "=== Installing Tailscale Client ===" +echo "Server: $LOGIN_SERVER" +echo "" + +# 1. Install tailscale +if command -v tailscale &>/dev/null; then + info "Tailscale already installed: $(tailscale version | head -1)" +else + info "Installing tailscale..." + dnf install -y tailscale +fi + +# 2. Start tailscaled +info "Starting tailscaled..." +systemctl enable --now tailscaled + +# 3. Register with headscale +info "Registering with headscale at $LOGIN_SERVER..." +tailscale up --login-server "$LOGIN_SERVER" --authkey "$AUTH_KEY" + +# 4. Show status +echo "" +echo "=== Connected ===" +tailscale status +echo "" +echo "To disconnect: tailscale down" +echo "To switch server: tailscale down && tailscale up --login-server http://:8080 --authkey --force-reauth" +echo "To remove: tailscale down && systemctl disable --now tailscaled && dnf remove -y tailscale" diff --git a/docs b/docs new file mode 100644 index 0000000..7384a99 --- /dev/null +++ b/docs @@ -0,0 +1,37 @@ +# Headscale +# exercise to assert that it works + +## 1. Create VMs +python3 vm.py c mk testvm2-1 --image fedora42 --vcpu 6 --ram 4 --auto-download +python3 vm.py c mk testvm2-2 --image fedora42 --vcpu 6 --ram 4 --auto-download + +## 2. Check available public IPs +python3 vm.py n ipv4 --pool + +## 3. Attach public IP to server VM +python3 vm.py n ipv4 --attach 37.27.166.243 testvm2-1 + +## 4. SCP install.sh to server VM and run it +scp -i ~/k/k1 -o StrictHostKeyChecking=no /root/hypervisor/vm-claude/headscale/install.sh user@:/tmp/install.sh +sshi testvm2-1.i "sudo bash /tmp/install.sh 37.27.166.243" +# Output includes the auth key, e.g.: +# hskey-auth-JK4Q793swFSJ-owovbUSFU1T71UyRiywcrIgERltcWq14h6vXT2LIFFA1naYkKLLGfL8E46cgUTOQ + +## 5. SCP client.sh to client VM and run it +scp -i ~/k/k1 -o StrictHostKeyChecking=no /root/hypervisor/vm-claude/headscale/client.sh user@:/tmp/client.sh +sshi testvm2-2.i "sudo bash /tmp/client.sh 37.27.166.243 " + +## 6. Verify - ping server's tailscale IP from client +sshi testvm2-2.i "ping -c 3 100.64.0.1" + +## Cleanup +python3 vm.py n ipv4 --detach 37.27.166.243 testvm2-1 +echo "y" | python3 vm.py c rm testvm2-1 +echo "y" | python3 vm.py c rm testvm2-2 + +## Useful commands on the headscale server VM +headscale node list # list all registered nodes +headscale users list # list users +headscale preauthkeys create --user --expiration 2160h --reusable # new auth key +headscale preauthkeys list --user # list auth keys +curl http://:8080/health # health check diff --git a/server.sh b/server.sh new file mode 100755 index 0000000..41b88ce --- /dev/null +++ b/server.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Install and configure headscale locally +# Copy this directory to a VM and run: sudo ./install.sh +# 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 }" + +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://: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 " +echo "" +echo "Create an auth key:" +echo " headscale preauthkeys create --user --expiration 2160h --reusable" +echo "" +echo "List nodes:" +echo " headscale node list" -- cgit