Headscale

repository·main·Indexed 12 days ago

https://github.com/juanfont/headscale

An open-source, self-hosted alternative to the Tailscale control server for managing Wireguard-based overlay networks (tailnets). Includes documentation on local development using cmd/dev, integration testing with the hi runner, and the v2 HTTP API for Tailscale ecosystem compatibility.

Tokens
45.6K
Snippets
165
Records
233
Agent score
98%

What's inside Headscale

  1. Overview of Headscale features and compatibility

    main

    Headscale is an open-source, self-hosted alternative to the Tailscale control server. It provides a wide range of features designed to be compatible with the Tailscale ecosystem, allowing users to manage their own private mesh networks.

    Supported Features

    • Node Registration: Supports Web authentication and Pre-authenticated keys.
    • DNS: Includes MagicDNS, Global and restricted nameservers (split DNS), search domains, and Headscale-specific extra DNS records.
    • File Sharing: Supports Taildrive and Taildrop.
    • Network Routing: Supports Tags, Subnet routers, Exit nodes, and Route filtering with Via.
    • Connectivity: Dual stack (IPv4/IPv6), Ephemeral nodes, Embedded DERP server, and Peer relays.
    • Policy & Security: ACLs, Grants, Autogroups, Auto approvers (for subnet routers and exit nodes), Tailscale SSH, Node attributes, and Policy tests (tests and sshTests).
    • Identity: Node registration via OpenID Connect (OIDC) with basic registration and user profile updates from the identity provider.

    Planned/In-Progress Features

    • Funnel
    • Serve
    • Network flow logs
  2. What is Headscale

    main
    Headscale is an open source, self-hosted implementation of the Tailscale control server. It acts as an exchange point for Wireguard public keys between nodes in a Tailscale network (tailnet). It manages IP address assignment, user boundaries, machine sharing, and advertised routes. Unlike the proprietary Tailscale control server, Headscale is designed for personal use or small open-source organizations, implementing a single tailnet scope.
  3. Explore community tools for headscale

    main

    The following third-party tools, client libraries, and scripts are maintained by the community and are not officially supported by the headscale authors:

    • Kubernetes Management: headscale-operator (Headscale Kubernetes Operator).
    • Routing: tailscale-manager (Dynamically manage Tailscale route advertisements).
    • Database Migration: headscalebacktosqlite (Migrate headscale from PostgreSQL back to SQLite).
    • Identity & Group Management: headscale-pf (Populates user groups based on user groups in Jumpcloud or Authentik).
    • Development: headscale-client-go (A Go client implementation for the Headscale HTTP API).
    • Monitoring:
      • headscale-zabbix (A Zabbix Monitoring Template for the Headscale Service).
      • tailscale-exporter (A Prometheus exporter for Headscale that provides network-level metrics using the Headscale API).
  4. Check Tailscale client and OS compatibility

    main

    Headscale aims to support the last 10 releases of the Tailscale client across all supported operating systems. While most platforms work out of the box, some require specific configurations to connect to a Headscale server.

    Supported Operating Systems

    OSSupports headscale
    LinuxYes
    OpenBSDYes
    FreeBSDYes
    WindowsYes (requires specific configuration)
    AndroidYes (requires specific configuration)
    macOSYes (requires specific configuration)
    iOSYes (requires specific configuration)
    tvOSYes (requires specific configuration)
  5. Requirements for Tailscale Control Protocol (WebSockets)

    main

    Tailscale clients use a custom protocol that requires the reverse proxy to support WebSockets with two specific requirements:

    1. The POST method must be used to upgrade the WebSocket connection.
    2. The Upgrade header value must be tailscale-control-protocol.

    If your proxy only supports standard WebSocket upgrades, the Tailscale Control Protocol will fail.

  6. Understand Headscale API v2 conventions and compatibility

    main

    Headscale's v2 HTTP API is served at /api/v2. It is designed to be partially compatible with the Tailscale API to support the Tailscale ecosystem (e.g., Terraform/OpenTofu provider, tscli, and the official Go client).

    Key Conventions:

    • Tailnet Path: The {tailnet} path segment must always be - (representing the single Headscale tailnet). Any other value results in a 404 error.
    • Authentication: Supports HTTP Basic (using the key as the username) or Bearer tokens.
      • Admin API keys start with hskey-api-… (these have all-access permissions).
      • OAuth access tokens start with hskey-oauthtok-… (these are scope-limited).
    • Error Format: Uses Tailscale's error body structure: {"message":"...","data":...,"status":"..."}.
    • Scopes: Operations require specific Tailscale scopes (e.g., auth_keys, devices:core, policy_file). OAuth tokens are restricted to their granted scopes, while admin API keys bypass scope checks.
  7. Manage variable scoping in `EventuallyWithT`

    main

    When using variables across multiple EventuallyWithT blocks, you must declare them at the function scope. Inside the block, use the assignment operator = instead of the short declaration operator :=. Using := inside the block creates a shadowed variable that is invisible to the outer scope.

    var nodes []*v1.Node
    var err error
    assert.EventuallyWithT(t, func(c *assert.CollectT) {
        nodes, err = headscale.ListNodes()   // Use = not :=
        assert.NoError(c, err)
        assert.Len(c, nodes, 2)
    }, 10*time.Second, 500*time.Millisecond, "nodes should have expected routes")
    
    // nodes is accessible here because it was declared at function scope
  8. Choose a Let's Encrypt challenge type

    main

    Headscale supports two ACME challenge types for Let's Encrypt validation:

    HTTP-01 (Default)

    Headscale must be reachable on port 80 for automated validation. By default, Headscale listens on port 80 on all local IPs for this purpose.

    If you are running Headscale as a non-root user and cannot use setcap, you can change the validation address using tls_letsencrypt_listen. However, you must then configure a firewall or proxy to forward traffic from port 80 to the address specified in tls_letsencrypt_listen.

    TLS-ALPN-01

    Headscale listens for validation on the IP:port combination defined in listen_addr. Let's Encrypt will only connect to port 443 for the validation callback. If your listen_addr is not port 443, you must use a firewall rule or proxy to forward traffic from port 443 to your listen_addr.

  9. Access Headscale metrics and debug endpoints

    main

    Headscale provides endpoints to introspect the Go runtime, memory usage, connected nodes, pending registrations, active policies (including SSH), the current DERPMap, and Prometheus metrics.

    • Metrics: http://localhost:9090/metrics (default)
    • Debug Overview: http://localhost:9090/debug/ (default)

    Security Warning

    Keep these endpoints private. By default, they listen on localhost:9090. Do not expose them to the public internet.

    • To change the address/port, use metrics_listen_addr in the configuration file.
    • To disable the endpoints completely, set metrics_listen_addr: null.
  10. Verify DERP client access

    main

    DERP access can be restricted to nodes belonging to your Tailnet.

    Embedded DERP

    Client verification is enabled by default for the embedded server. You can explicitly set it in config.yaml:

    derp:
      server:
        verify_clients: true

    3rd-party DERP

    If you are running a standalone Tailscale derper instance, you can configure it to verify clients against Headscale:

    • Use the -verify-client-url flag pointing to your Headscale /verify endpoint (e.g., https://headscale.example.com/verify).
    • Use the -verify-client-url-fail-open flag to control behavior when the Headscale instance is unreachable. By default, it allows access if Headscale is unreachable.
  11. Important considerations for community packages

    main

    Community packages for various Linux distributions often provide improved OS integration, such as:

    • Setting up a dedicated local user account to run headscale
    • Providing a default configuration
    • Installing headscale as a system service

    Warning: Community packages might be outdated or unmaintained. If you require the current stable version or wish to test pre-releases, use the official releases.

  12. Use Autogroups in Headscale policies

    main

    Autogroups allow you to write policy rules that automatically include users or devices based on specific properties, avoiding manual lists.

    Supported Autogroups

    AutogroupDescriptionUsage Constraint
    autogroup:internetAccess to the internet via exit nodes.Can only be used in destinations
    autogroup:memberIncludes all personal (untagged) devices.N/A
    autogroup:taggedIncludes all devices that have at least one tag.N/A
    autogroup:selfIncludes devices where the same user is authenticated on both source and destination.Can only be used in destinations
    autogroup:nonrootAllows access to any user except root.Can only be used in the users field of SSH rules
    autogroup:danger-allResolves to all IP addresses (0.0.0.0/0 and ::/0).Can only be used as a source

    Performance Warning: autogroup:self

    Using autogroup:self can cause performance degradation in large deployments because filter rules must be compiled per-node. If performance issues occur, replace it with explicit user-to-user rules:

    {
      "grants": [
        {
          "src": ["boss@"],
          "dst": ["boss@"],
          "ip": ["*"]
        }
      ]
    }
    # Example: Using autogroup:internet
    {
      "grants": [
        {
          "src": ["alice@"],
          "dst": ["autogroup:internet"],
          "ip": ["*"]
        }
      ]
    }