tsbridge

repository·main·Indexed 43 days ago

https://github.com/jtdowney/tsbridge

A tsnet-powered reverse proxy that allows multiple backend services on a Tailnet to be exposed from a single process. It supports static TOML configuration and dynamic discovery via Docker labels.

Tokens
21.6K
Snippets
62
Records
78
Agent score
52%

What's inside tsbridge

  1. Determine if tsbridge is suitable for your use case

    main

    tsbridge is designed for relatively trusted environments. Evaluate your deployment against these guidelines:

    Recommended Use Cases:

    • Home labs
    • Personal development environments
    • Small team internal networks
    • Testing and staging environments

    Do NOT use tsbridge for:

    • Security-critical production workloads
    • Public internet services (unless intentionally exposed via Tailscale Funnel)
    • High-security enterprise deployments
    • Workloads subject to strict compliance requirements (e.g., PCI DSS, HIPAA)
  2. Configure service backend addresses and ports

    main

    To proxy a service, you must specify how to reach it.

    Important: Do not use localhost for the backend address. In Docker, localhost refers to the tsbridge container itself, not your service container. Instead, use the container name or the specific port.

    • Use tsbridge.service.port to specify the port reached via the container's Docker name.
    • Use tsbridge.service.backend_addr for an explicit address (e.g., https://api.example.com:443). This takes precedence over port.
    # Good - uses container name
    - "tsbridge.service.port=8080"
    
    # Bad - localhost is the tsbridge container!
    - "tsbridge.service.backend_addr=localhost:8080"
  3. Understand the tsbridge configuration structure

    main

    The tsbridge configuration is organized into three main sections within a TOML file:

    1. [tailscale]: Contains Tailscale-specific authentication and settings (e.g., OAuth credentials, state directory).
    2. [global]: Defines default settings that apply to all services, such as timeouts, observability, and security settings.
    3. [[services]]: A list of service definitions. Each service requires a name and backend_addr. Services can override specific fields defined in the [global] section.
    [tailscale]
    # Tailscale authentication and settings
    
    [global]
    # Default settings for all services
    
    [[services]]
    # Service definitions (multiple allowed)
  4. Quick Install tsbridge via systemd

    main

    Follow these steps to deploy tsbridge as a systemd service on Linux. This process creates a dedicated system user, installs the binary, sets up configuration directories with appropriate permissions, and enables the service.

    Prerequisites:

    • You must have the tsbridge binary, a config.toml file, a tsbridge.service file, and a tsbridge.env.example file available in your current directory.
    • Root/sudo access.
    # 1. Create user
    sudo useradd --system --shell /bin/false --home-dir /var/lib/tsbridge --create-home tsbridge
    
    # 2. Install binary
    sudo install -o root -g root -m 755 tsbridge /usr/local/bin/
    
    # 3. Create config directory
    sudo install -d -o tsbridge -g tsbridge -m 755 /etc/tsbridge
    
    # 4. Install your config
    sudo install -o tsbridge -g tsbridge -m 640 config.toml /etc/tsbridge/
    sudo install -o tsbridge -g tsbridge -m 600 tsbridge.env.example /etc/tsbridge/tsbridge.env
    
    # 5. Install service
    sudo install -m 644 tsbridge.service /etc/systemd/system/
    sudo systemctl daemon-reload
    
    # 6. Start it
    sudo systemctl start tsbridge
    sudo systemctl enable tsbridge
  5. Quick Start Guide for tsbridge

    main

    To get tsbridge running, follow these three steps:

    1. Get OAuth Credentials: Generate an OAuth client at Tailscale Admin Settings. Ensure you select both Read and Write scopes under Auth Keys, and assign a tag (e.g., tag:server) that your services will use.
    2. Create a Config File: Define your Tailscale settings and services in a tsbridge.toml file.
    3. Run the Binary: Export your Client ID and Secret as environment variables and execute tsbridge pointing to your config.

    Your service will then be accessible at https://<service-name>.<tailnet>.ts.net.

    # 1. Set credentials
    export TS_OAUTH_CLIENT_ID=your-client-id
    export TS_OAUTH_CLIENT_SECRET=your-client-secret
    
    # 2. Run tsbridge
    tsbridge -config tsbridge.toml
  6. Configure the Docker socket endpoint

    main

    If your Docker daemon is not at the default unix:///var/run/docker.sock, you can specify an alternative endpoint using the following priority order:

    1. The -docker-socket CLI flag.
    2. The DOCKER_HOST environment variable.
    3. The default socket path.

    This is useful for remote daemons, Docker Desktop on macOS, or Rootless Docker.

    # Using the CLI flag
    tsbridge -provider docker -docker-socket tcp://docker-host:2375
    
    # Using DOCKER_HOST in docker-compose
    services:
      tsbridge:
        image: ghcr.io/jtdowney/tsbridge:latest
        command: ["-provider", "docker"]
        environment:
          - DOCKER_HOST=unix:///var/run/docker.sock
  7. Debug and manage Multi-Compose deployments

    main

    Use the following commands to inspect logs, verify connectivity, and clean up the environment.

    Logs and Inspection

    # Check tsbridge logs
    docker compose -f tsbridge-compose.yml logs -f tsbridge
    
    # Check service logs
    docker compose -f services-compose.yml logs -f whoami
    
    # Verify network connectivity
    docker network inspect tsbridge-shared-network
    
    # Test connectivity between containers
    docker exec -it multi-compose-tsbridge-1 ping whoami

    Stopping and Cleanup

    # Stop services
    docker compose -f services-compose.yml down
    
    # Stop tsbridge
    docker compose -f tsbridge-compose.yml down
    
    # Clean up (removes network and volumes)
    docker compose -f tsbridge-compose.yml down -v
    docker compose -f services-compose.yml down -v
    # Check tsbridge logs
    docker compose -f tsbridge-compose.yml logs -f tsbridge
    
    # Check service logs
    docker compose -f services-compose.yml logs -f whoami
    
    # Verify network connectivity
    docker network inspect tsbridge-shared-network
    
    # Test connectivity between containers
    docker exec -it multi-compose-tsbridge-1 ping whoami
  8. Deploy the Multi-Compose setup

    main

    There are two ways to deploy this setup. The recommended approach is to start tsbridge first so it can initialize the shared network.

    This method allows tsbridge to create the network automatically.

    # Start tsbridge (creates the network)
    docker compose -f tsbridge-compose.yml up -d
    
    # Start services (uses existing network)
    docker compose -f services-compose.yml up -d

    Option 2: Create network manually first

    If you prefer to manage the network lifecycle yourself, create the network before starting the compose files.

    # Create network manually
    docker network create tsbridge-shared-network
    
    # Start both (any order)
    docker compose -f tsbridge-compose.yml up -d
    docker compose -f services-compose.yml up -d
    # Option 1: Start tsbridge first (recommended)
    docker compose -f tsbridge-compose.yml up -d
    docker compose -f services-compose.yml up -d
    
    # Option 2: Create network manually first
    docker network create tsbridge-shared-network
    docker compose -f tsbridge-compose.yml up -d
    docker compose -f services-compose.yml up -d
  9. Run tsbridge with Docker label discovery

    main

    To use tsbridge to automatically discover and proxy services in a Docker environment, run the container with the -provider docker command and mount the Docker socket. This allows tsbridge to watch for container events and automatically create proxies for containers labeled with tsbridge.enabled=true.

    services:
      tsbridge:
        image: ghcr.io/jtdowney/tsbridge:latest
        command: ["-provider", "docker"]
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - tsbridge-state:/var/lib/tsbridge
        environment:
          - TS_OAUTH_CLIENT_ID=${TS_OAUTH_CLIENT_ID}
          - TS_OAUTH_CLIENT_SECRET=${TS_OAUTH_CLIENT_SECRET}
        labels:
          - "tsbridge.tailscale.oauth_client_id_env=TS_OAUTH_CLIENT_ID"
          - "tsbridge.tailscale.oauth_client_secret_env=TS_OAUTH_CLIENT_SECRET"
          - "tsbridge.tailscale.state_dir=/var/lib/tsbridge"
          - "tsbridge.tailscale.default_tags=tag:server"
    
      myapp:
        image: myapp:latest
        labels:
          - "tsbridge.enabled=true"
          - "tsbridge.service.name=myapp"
          - "tsbridge.service.port=8080"
    
    volumes:
      tsbridge-state:
  10. Secure the metrics endpoint

    main

    The Prometheus metrics endpoint has no authentication. To prevent unauthorized access:

    • Default State: Metrics are disabled by default.
    • Binding: When enabling metrics, bind the endpoint to 127.0.0.1 or another protected interface rather than a public-facing one.
  11. Use tsbridge with Docker labels (Dynamic Configuration)

    main

    When using -provider docker, tsbridge watches the Docker daemon for container labels to automatically expose services. This works similarly to Traefik.

    To use this mode:

    1. Run tsbridge with -provider docker.
    2. Mount /var/run/docker.sock into the tsbridge container.
    3. Add tsbridge.enabled=true and tsbridge.service.name=<name> labels to your target containers.

    Example docker-compose.yml setup:

    services:
      tsbridge:
        image: ghcr.io/jtdowney/tsbridge:latest
        command: ["-provider", "docker"]
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - tsbridge-state:/var/lib/tsbridge
        environment:
          - TS_OAUTH_CLIENT_ID=${TS_OAUTH_CLIENT_ID}
          - TS_OAUTH_CLIENT_SECRET=${TS_OAUTH_CLIENT_SECRET}
        labels:
          - "tsbridge.tailscale.oauth_client_id_env=TS_OAUTH_CLIENT_ID"
          - "tsbridge.tailscale.oauth_client_secret_env=TS_OAUTH_CLIENT_SECRET"
          - "tsbridge.tailscale.state_dir=/var/lib/tsbridge"
          - "tsbridge.tailscale.default_tags=tag:server"
    
      whoami:
        image: traefik/whoami
        labels:
          - "tsbridge.enabled=true"
          - "tsbridge.service.name=whoami"
          - "tsbridge.service.port=80"
    
    volumes:
      tsbridge-state: