Skip to content

Latest commit

 

History

History

README.md

Headscale Stack

This project will deploy a headscale and headplane stack under the assumption that it will be fronted via a reverse proxy such as Caddy, Ngnix or Apache.

Deployment

CDN fronted

HEADSCALE_HOSTNAME=abcd12345.fastly.net docker compose up

Headscale Redirection Rules

The headscale server will expose port 5566 over plaintext HTTP for the reverse proxy to forward requests.

Tailscale agents uses the useragent Go-http-client/x.x or similar. Simple forwarding rules based on the user agent is one option.

Alternatively, the following paths at the reverse proxy should be routed to the headscale server on port 5566

  • /ts2021
  • /key?v=
  • /derp
  • /derp/*

Apache Rewrite Example

########### BEGIN TAILSCALE ###########

        # Match /ts2021, /derp, or /key paths
        RewriteCond %{REQUEST_URI} ^/(key|derp/latency-check)
        RewriteRule ^(.*) http://localhost:5566$1 [P,L,NC]

        RewriteCond %{REQUEST_URI} ^/(ts2021|derp)
        RewriteCond %{HTTP:Upgrade} websocket [NC]
        RewriteCond %{HTTP:Connection} upgrade [NC]
        RewriteRule ^(.*) ws://localhost:5566$1 [P,L,NC]

############ END TAILSCALE ############

Caddy example

{
    # Global error logging
    log {
        output file /var/log/caddy/error.log
        level INFO
    }

    on_demand_tls {
        ask http://127.0.0.1:10000
    }
}

# ---------------------------
# A hack to handle situations where the CDN uses
# the correct SNI but wrong Host header. So we need
# a match all host handler below and an on-demand TLS
# config so we can restrict to the origin hostname
# ---------------------------
http://127.0.0.1:10000 {

    @checkOrigin query domain=abcd-origin.somewhere.net 
    handle @checkOrigin {
        respond "OK" 200
    }

    handle {
        respond "Forbidden" 403
    }
}

# ---------------------------
# Host-less HTTPS listener with on-demand TLS
# ---------------------------
https:// {

    tls { 
        on_demand
    }
    
    log {
        output file /var/log/caddy/access.log
        level INFO
    }

    # ts2021|derp|key → localhost:5566
    @tailscale path_regexp ^/(ts2021|derp|key)
    handle @tailscale {
        reverse_proxy http://localhost:5566 {
            transport http {
                read_buffer 4096
                read_timeout 0
                write_timeout 0
            }
        }
    }

    # Default fallback -> Lighthouse
    handle {
        reverse_proxy http://somewhereelse
    }

}

Websockets

Tailscale will prefer UDP where possible and will try to punch a hole via STUN. If this fails, it will fallback to websockets over HTTPS. Tailscale does not use the standard websocket RFC6455 and it's framing protocol, but it's own protocol called TS2021. CDN's that support websockets typically will only passthrough RFC6455 compliant requests. Therefor you'll need to use the tailscale builds from the tailscale folder.

Headplane Admin Console

Headplane is setup to listen on port 3000. Operators should just use SSH port forwarding for accessing the admin console. If access via the reverse proxy is desired then mapping a route from /admin -> http://localhost:3000/admin is a possibility, but this endpoint should be secured to prevent access from unauthorized locations.

Use sudo docker exec headscale headscale apikey create to generate an API for accessing the admin console.