commit a7f5c4b3e7341d9137e9b411edaf1d17eb389363 Author: Dejan R. Date: Mon Nov 24 16:58:23 2025 +0000 first commit diff --git a/config/config.yaml b/config/config.yaml new file mode 100644 index 0000000..c40b930 --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,41 @@ +server_url: https://headscale.rozic-dev.com +listen_addr: 0.0.0.0:8080 +metrics_listen_addr: 0.0.0.0:9090 + +noise: + private_key_path: /var/lib/headscale/noise_private.key + +prefixes: + v4: 100.64.0.0/10 + v6: fd7a:115c:a1e0::/48 + +derp: + server: + enabled: false + urls: + - https://controlplane.tailscale.com/derpmap/default + auto_update_enabled: true + update_frequency: 3h + +dns: + magic_dns: true + base_domain: tailnet.rozic-dev.com + override_local_dns: true + nameservers: + global: + - 1.1.1.1 + - 8.8.8.8 + +log: + level: info + format: text + +database: + type: sqlite + sqlite: + path: /var/lib/headscale/db.sqlite + write_ahead_log: true + wal_autocheckpoint: 1000 + +unix_socket: /var/run/headscale/headscale.sock +unix_socket_permission: "0770" diff --git a/config/config.yaml.bak b/config/config.yaml.bak new file mode 100644 index 0000000..89616ba --- /dev/null +++ b/config/config.yaml.bak @@ -0,0 +1,31 @@ +server_url: https://headscale.rozic-dev.com +listen_addr: 0.0.0.0:8080 + +# REQUIRED by newer Headscale (TS2021/Noise) +noise: + private_key_path: /var/lib/headscale/noise_private.key + +# Tailnet address pools +prefixes: + v4: 100.64.0.0/10 + v6: fd7a:115c:a1e0::/48 + +# Use Tailscale public DERP (built-in disabled so you don't need extra ports) +derp: + server: + enabled: false + urls: + - https://controlplane.tailscale.com/derpmap/default + +# NEW DNS schema (dns_config is deprecated). Since override_local_dns is true, +# nameservers.global MUST be set. +dns: + override_local_dns: true + nameservers: + global: + - 1.1.1.1 + - 8.8.8.8 + +log: + level: info + format: text diff --git a/data/db.sqlite b/data/db.sqlite new file mode 100644 index 0000000..a9a8e89 Binary files /dev/null and b/data/db.sqlite differ diff --git a/data/noise_private.key b/data/noise_private.key new file mode 100644 index 0000000..44c291a --- /dev/null +++ b/data/noise_private.key @@ -0,0 +1 @@ +privkey:70c34ac681bd47a23e573560825f98c9cef03b6b02e6656b57b9d98f75fe1a58 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fa5b4b6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,60 @@ +# Compose v2 (no "version" key needed) +x-default: &default + restart: unless-stopped + networks: + - traefik + logging: + driver: json-file + options: + max-size: 50m + max-file: "2" + +services: + headscale: + <<: *default + image: docker.io/headscale/headscale:latest + container_name: headscale + command: serve + # Optional: expose metrics to host only + ports: + - "127.0.0.1:9090:9090" + volumes: + - ./config:/etc/headscale + - ./lib:/var/lib/headscale + - ./run:/var/run/headscale + healthcheck: + test: ["CMD", "headscale", "health"] + interval: 30s + timeout: 10s + retries: 5 + labels: + - "traefik.enable=true" + # Headscale API at / + - "traefik.http.routers.headscale.rule=Host(`headscale.rozic-dev.com`)" + - "traefik.http.routers.headscale.entrypoints=websecure" + - "traefik.http.routers.headscale.tls.certresolver=letsencrypt" + - "traefik.http.services.headscale.loadbalancer.server.port=8080" + + headscale-ui: + <<: *default + image: ghcr.io/gurucomputing/headscale-ui:latest + container_name: headscale-ui + # UI listens on 8080 (and 8443) in the container by default + labels: + - "traefik.enable=true" + # Serve UI at /web on the *same* host to avoid CORS + - "traefik.http.routers.headscale-ui.rule=Host(`headscale.rozic-dev.com`) && PathPrefix(`/web`)" + - "traefik.http.routers.headscale-ui.entrypoints=websecure" + - "traefik.http.routers.headscale-ui.tls.certresolver=letsencrypt" + - "traefik.http.services.headscale-ui.loadbalancer.server.port=8080" + + # 🔐 BASIC AUTH MIDDLEWARE + - "traefik.http.middlewares.headscale-ui-basicauth.basicauth.users=Dejan:$$2y$$05$$EAuwBj8Ac5YTRn90WSeQuezJxFZF0ghIC/mBlf6S.x8Bb/5jI49WC" + + # Attach middleware to router + - "traefik.http.routers.headscale-ui.middlewares=headscale-ui-basicauth" + +networks: + traefik: + external: true + name: traefik_default diff --git a/head-scale-connect.sh b/head-scale-connect.sh new file mode 100755 index 0000000..61a01c1 --- /dev/null +++ b/head-scale-connect.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -e + +# ============================ +# CONFIG +# ============================ +HEADSCALE_URL="https://headscale.rozic-dev.com" +TAILSCALE_BIN="tailscale" +DEFAULT_HOSTNAME="$(hostname)" + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "ERROR: Command '$1' not found. Please install it first." >&2 + exit 1 + fi +} + +require_cmd "$TAILSCALE_BIN" + +if ! command -v sudo >/dev/null 2>&1 && [[ $EUID -ne 0 ]]; then + echo "WARNING: 'sudo' not found and you are not root." + echo "You must run this script as root for 'tailscale up' to work." + exit 1 +fi + +AUTHKEY="${1:-}" + +if [[ -z "$AUTHKEY" ]]; then + echo -n "Enter Headscale pre-auth key: " + read -r AUTHKEY +fi + +if [[ -z "$AUTHKEY" ]]; then + echo "ERROR: No auth key provided." >&2 + exit 1 +fi + +echo -n "Hostname to register on Headscale [${DEFAULT_HOSTNAME}]: " +read -r CUSTOM_HOSTNAME +HOSTNAME_TO_USE="${CUSTOM_HOSTNAME:-$DEFAULT_HOSTNAME}" + +echo +echo "Connecting this device to Headscale:" +echo " Server : ${HEADSCALE_URL}" +echo " Host : ${HOSTNAME_TO_USE}" +echo + +TS_CMD=( + "$TAILSCALE_BIN" up + --reset + --login-server="${HEADSCALE_URL}" + --auth-key="${AUTHKEY}" + --hostname="${HOSTNAME_TO_USE}" +) + +if [[ $EUID -ne 0 ]]; then + echo "Running: sudo ${TS_CMD[*]}" + sudo "${TS_CMD[@]}" +else + echo "Running: ${TS_CMD[*]}" + "${TS_CMD[@]}" +fi + +echo +echo "✅ Done! This device should now be visible in Headscale." diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..ab2e685 --- /dev/null +++ b/install.sh @@ -0,0 +1,119 @@ +#!/bin/bash +set -e + +# =============================================================== +# Headscale Self-Hosted Installation Script +# Compatible with Traefik (network: traefik_default) +# =============================================================== + +# --- Configuration --- +DOMAIN="headscale.rozic-dev.com" +EMAIL="your@email.com" # For Let's Encrypt via Traefik +NETWORK="traefik_default" +INSTALL_DIR="/home/Dejan/Docker/Headscale" + +# --- Create folders --- +echo "📁 Creating folder structure..." +mkdir -p "${INSTALL_DIR}/config" "${INSTALL_DIR}/data" +cd "${INSTALL_DIR}" + +# --- Create config.yaml --- +echo "📝 Creating Headscale config file..." +cat > "${INSTALL_DIR}/config/config.yaml" < "${INSTALL_DIR}/docker-compose.yml" <<'EOF' +version: "3.8" + +x-default: &default + restart: unless-stopped + networks: + - traefik + logging: + driver: json-file + options: + max-size: 50m + max-file: "2" + +services: + headscale: + <<: *default + image: headscale/headscale:latest + container_name: headscale + command: serve + environment: + - HEADSCALE_LOG_LEVEL=info + - HEADSCALE_SERVER_URL=https://headscale.rozic-dev.com + - HEADSCALE_LISTEN_ADDR=0.0.0.0:8080 + - HEADSCALE_DB_TYPE=sqlite3 + - HEADSCALE_DB_PATH=/var/lib/headscale/db.sqlite + - HEADSCALE_EPHEMERAL_NODE_INACTIVITY_TIMEOUT=30m + volumes: + - ./data:/var/lib/headscale + - ./config:/etc/headscale + labels: + - "traefik.enable=true" + - "traefik.http.routers.headscale.rule=Host(`headscale.rozic-dev.com`)" + - "traefik.http.routers.headscale.entrypoints=websecure" + - "traefik.http.routers.headscale.tls.certresolver=letsencrypt" + - "traefik.http.services.headscale.loadbalancer.server.port=8080" + +networks: + traefik: + external: true + name: traefik_default +EOF + +# --- Start container --- +echo "🚀 Starting Headscale container..." +docker compose up -d + +# --- Wait for container startup --- +sleep 5 + +# --- Create user and auth key --- +echo "👤 Creating default Headscale user..." +docker exec -it headscale headscale users create dejan || true + +echo "🔑 Creating reusable pre-auth key..." +docker exec -it headscale headscale preauthkeys create --user dejan --reusable --ephemeral=false + +echo +echo "✅ Headscale is now running!" +echo "🌍 URL: https://${DOMAIN}" +echo "💡 To connect a client:" +echo " tailscale up --login-server https://${DOMAIN} --authkey " +echo +EOF + +--- + +## 🧠 Usage + +1. Copy this file to your server, e.g.: + +```bash +nano install.sh diff --git a/key b/key new file mode 100644 index 0000000..4913947 --- /dev/null +++ b/key @@ -0,0 +1 @@ +1. -F1mrl1.xK7SrZqjJ2mEHUN1HP0Rj-YHkED179tN \ No newline at end of file diff --git a/lib/db.sqlite b/lib/db.sqlite new file mode 100644 index 0000000..a65179f Binary files /dev/null and b/lib/db.sqlite differ diff --git a/lib/db.sqlite-shm b/lib/db.sqlite-shm new file mode 100644 index 0000000..c8116f6 Binary files /dev/null and b/lib/db.sqlite-shm differ diff --git a/lib/db.sqlite-wal b/lib/db.sqlite-wal new file mode 100644 index 0000000..b365f88 Binary files /dev/null and b/lib/db.sqlite-wal differ diff --git a/lib/noise_private.key b/lib/noise_private.key new file mode 100644 index 0000000..a54b953 --- /dev/null +++ b/lib/noise_private.key @@ -0,0 +1 @@ +privkey:78376c92546f9fecdffd10e45dd95e2d56e88c3a8a890843c1439a13b3d16a47 \ No newline at end of file