tsnsrv

repository·main·Indexed 43 days ago

https://github.com/boinkor-net/tsnsrv

A Go-based reverse proxy for Tailscale tailnets that exposes local services (localhost or Unix sockets) to a private tailnet or the public internet via Tailscale Funnel. It provides automatic HTTPS, Tailscale identity headers for upstream authentication, and granular access control using URL prefixes with provenance (tailnet vs funnel).

Tokens
3.6K
Snippets
11
Records
15
Agent score
41%

What's inside tsnsrv

  1. Access requestor information via HTTP headers

    main

    Unless the -suppressWhois flag is used, tsnsrv automatically identifies the Tailscale user and node making the request and attaches identity information as HTTP headers. This allows your upstream service to perform authentication or authorization based on Tailscale identity.

    Headers attached to upstream requests:
    - X-Tailscale-User: numeric ID of the user
    - X-Tailscale-User-LoginName: login name (e.g., foo@example.com)
    - X-Tailscale-User-LoginName-Localpart: local part of login name (e.g., foo)
    - X-Tailscale-User-LoginName-Domain: domain part of login name (e.g., example.com)
    - X-Tailscale-User-DisplayName: display name of the user
    - X-Tailscale-User-ProfilePicURL: URL of the user's profile picture
    - X-Tailscale-Caps: user capabilities
    - X-Tailscale-Node: numeric ID of the originating node
    - X-Tailscale-Node-Name: name of the originating node
    - X-Tailscale-Node-Caps: node device capabilities
    - X-Tailscale-Node-Tags: ACL tags on the origin node
  2. Use Tailscale OAuth clients for long-term deployments

    main

    To avoid the 3-month rotation requirement of standard Tailscale Auth keys, use Tailscale OAuth clients. This is recommended for automated or long-running tsnsrv deployments.

    Requirements:

    1. An OAuth client minted with auth_key permissions.
    2. The client must specify the exact tags you intend to use.
    3. You must pass those same tags to tsnsrv using the -tag flag.

    Usage: Use the OAuth client secret as the Tailscale auth key when running tsnsrv.

  3. Restrict public funnel access to specific subpaths

    main

    When using -funnel, you can restrict external access to only specific URL paths using the -prefix flag. Any request to a path not matching a provided -prefix will return a 404.

    By default, tsnsrv strips the matched prefix before forwarding the request to the upstream service. To keep the prefix intact in the request sent to the upstream, use the -stripPrefix=false flag.

    # Example: Exposing only Matrix webhooks to the internet
    tsnsrv -name happy-computer-webhook -funnel -stripPrefix=false -prefix /_matrix -prefix /_synapse/client http://127.0.0.1:8000
  4. Submit Tailscale bug reports from tsnsrv

    main

    If you encounter bugs in Tailscale or the tsnet library, you can generate a report directly from your tsnsrv instance.

    1. Start tsnsrv with the -enableBugReports flag.
    2. Send a POST request to the /bugreport endpoint on your Prometheus API port (default is 9099).
    3. The response will contain a bug report ID, which is also logged at the INFO level in tsnsrv.
    # Replace placeholders with your actual service name, tailnet name, and prometheus port
    curl -X POST http://your-tsnsrv-instance-name.your-tailnet.ts.net:9099/bugreport
  5. Expose a service on your tailnet with tsnsrv

    main

    To expose a local service (e.g., running on 127.0.0.1:8000) to your tailnet, use the tsnsrv command with the -name flag to specify the service name. This makes the service reachable via its tailnet DNS name but restricts access to members of your tailnet.

    Example: Exposing a service named happy-computer running on port 8000.

    tsnsrv -name happy-computer http://127.0.0.1:8000
  6. Expose a service to the internet using Tailscale Funnel

    main

    To make a local service accessible to the public internet via Tailscale Funnel, include the -funnel flag in your command. This exposes the service on both your tailnet and the public internet.

    tsnsrv -name happy-computer -funnel http://127.0.0.1:8000
  7. Configure allowed URL prefixes with provenance

    main

    You can restrict which URL paths are allowed through the proxy using the -prefix flag. You can also specify whether a prefix is only valid for specific entry points using provenance prefixes:

    • tailnet:/api: The path /api is only allowed when accessing via the Tailscale tailnet.
    • funnel:/public: The path /public is only allowed when accessing via Tailscale Funnel.
    • /api: The path /api is allowed on both the tailnet and via Funnel.

    Note: Using provenance-specific prefixes (tailnet: or funnel:) requires a local Tailscale client to be available.

    # Example CLI usage
    tsnsrv -name my-service -prefix tailnet:/admin -prefix funnel:/public https://upstream.com
  8. Passing Tailscale identity to upstream services via headers

    main

    The tsnsrv proxy can automatically inject Tailscale identity information into request headers before forwarding them to an upstream service. This allows your application to know exactly which Tailscale user or node is making the request.

    Identity Headers

    When SuppressWhois is false and a Tailscale client is configured, the following headers are added to the outgoing request:

    User Information:

    • X-Tailscale-User: The unique User ID.
    • X-Tailscale-User-LoginName: The user's login name (e.g., user@example.com).
    • X-Tailscale-User-LoginName-Localpart: The part before the @ in the login name.
    • X-Tailscale-User-LoginName-Domain: The part after the @ in the login name.
    • X-Tailscale-User-DisplayName: The user's display name.
    • X-Tailscale-User-ProfilePicURL: The URL to the user's profile picture (if available).
    • X-Tailscale-Caps: A comma-separated list of user capabilities.

    Node Information:

    • X-Tailscale-Node: The unique Node ID.
    • X-Tailscale-Node-Name: The computed name of the node.
    • X-Tailscale-Node-Caps: A comma-separated list of node capabilities.
    • X-Tailscale-Node-Tags: A comma-separated list of node tags.

    Configuration

    • SuppressWhois: If set to true, identity headers will not be added.
    • WhoisTimeout: A time.Duration that limits how long the proxy waits to look up the requestor's identity via the Tailscale API.
  9. Configure proxy headers and upstream routing

    main

    The ValidTailnetSrv implementation provides several mechanisms to control how requests are rewritten and how headers are passed to upstream services.

    Header Management

    • RecommendedProxyHeaders: When enabled, the proxy adds standard proxy headers:
      • X-Scheme: Set to http or https based on the incoming connection's TLS state.
      • X-Real-Ip: The remote IP address of the requestor.
      • X-Forwarded-Server: The host of the incoming request.
      • X-Forwarded-Port: The port of the incoming request.
    • UpstreamHeaders: A map of custom headers that are added to every outgoing request to the upstream service.
    • Security: The proxy automatically deletes any incoming headers starting with X-Tailscale- to prevent requestors from spoofing their identity.

    URL Rewriting and Prefix Matching

    • DestURL: The target URL where all proxied requests are sent.
    • AllowedPrefixes: A list of prefix objects that define which URL paths are permitted. If a request path does not match any allowed prefix, the proxy returns a 404 Not Found.
    • StripPrefix: If true, the matching prefix is removed from the URL path before the request is sent to the upstream service (similar to http.StripPrefix).
  10. Reference: tsnsrv CLI flags

    main

    The following flags are used to configure the reverse proxy behavior and access control.

    -name <name>          The name of the service on the tailnet
    -funnel               Enable Tailscale Funnel to expose the service to the internet
    -prefix <path>         Add a path to the list of URLs that external clients can see (Anything outside that list returns a 404)
    -stripPrefix <bool>    Whether to leave the matched prefix intact (default is true, which strips it)
    -suppressWhois        If set, tsnsrv will not look up and attach requestor information headers
    -tag <tags>            Specify exact tags for the service (required when using OAuth clients)
    -enableBugReports     Enables the ability to submit bug reports to Tailscale
  11. Filter requests using matchPrefixes

    main

    The matchPrefixes function acts as a middleware to restrict which URL paths are allowed to be proxied. It behaves similarly to http.StripPrefix but supports multiple allowed prefixes.

    • If the prefixes list is empty, all paths are allowed.
    • If a path matches one of the provided prefixes, the request is passed to the handler.
    • If strip is true, the matched prefix is stripped from the request URL before reaching the handler.
    • If no prefix matches, the middleware returns an http.NotFound (404) response.
  12. Run a TailnetSrv instance

    main

    The Run(ctx context.Context) method on a ValidTailnetSrv starts the proxy service. It handles:

    1. Connecting to the Tailscale network via tsnet.
    2. Setting up the upstream dialer (TCP, Unix, or Tailnet-aware).
    3. Starting the HTTP server(s) for the tailnet and/or the Funnel.
    4. Initializing the Prometheus metrics endpoint if configured.

    If the service is configured with -funnelOnly, Run will block until the funnel server exits.

    err := validSrv.Run(ctx)