How to Route Proxy to Service

Hi there, I have the following infrastructure:

  • I have a hosted server that handles my domain with traefik. I use subdomains to route specific domains to specific containers
  • One specific domain with specific /api endpoint should be forwarded to a local machine that I have standing in my living room. It contains specific hardware that I want to make available.
  • I want to route my traffic via my proxy to this machine. I want to leverage twingate to make a stable and secure connection between my remote server, and local server. At home I dont have fixed IP, which twingate can also resolve for me dynamically.

Diagram zonder titel.drawio (2)

I have had partial success:

  • I have provisioned a service and headless client on the remote server. I can successfully curl the API in the terminal in the remote server, therefore proving that the connection to local server works.
  • I can provision an NGINX container to the specific subdomain with endpoint extension /api. When I go the url, I see NGINX running.

My problem (red arrow) is that I don’t know how to bridge the last gap. Note that I run most if not all my applications in containers. Ideally I would directly point my traefik proxy to this subdomain with specific endpoint. How does this work with a containerized proxy and the fact the headless client is installed on the OS. Any pointers would be extremely helpfull :slight_smile:

Hi @peda,

first off… thank you for sharing a diagram! those are always helpful.

So if I understand correctly, Traefik runs in a container? I assume, the headless client also runs on a container. If so, would you consider binding those 2 containers, perhaps? Maybe via a Docker Compose and have the container that runs Traefik leverage the networking stack of the container running the headless client?

This is not your use case but I have an example of a Docker Compose for a different scenario that involves a headless client providing networking for another container running a proxy (this is commonly use to provide connectivity to a Twingate network via a proxy for devices that don’t support a headless client directly, like IoT devices, etc.):

version: "1.0"

services:
  tg-headless-client:
    image: ubuntu:latest
    privileged: true
    command: >
      bash -c "apt-get update
      && apt-get install curl -y
      && curl https://binaries.twingate.com/client/linux/install.sh | bash
      && sudo twingate setup --headless /etc/twingate-service-key/service-key.json
      && sudo twingate start
      && sleep infinity"
    volumes:
      - /path/to/service/key/:/etc/twingate-service-key/
    ports:
      - "3128:3128"
    restart: always
  tg-proxy:
    image: ubuntu/squid
    volumes:
      - /path/to/squid.conf:/etc/squid/squid.conf
    restart: always
    network_mode: "service:tg-headless-client"
    depends_on:
      - tg-headless-client

Hey @Bren , I have massive trouble implementing this until i realized that I caslled the key service_key.json instead of service-key.json. #FML. Anyways. The container works, it install the dependencies, get the credentials and establishes the connection. I am still struggling with the red-arrow. I use the following docker-compose.yaml:

version: '3'

networks:
  traefik-proxy:
    external: true
services:
  proxy:
    image: "traefik:latest"
    container_name: "traefik"
    restart: always
    environment:
      - TRAEFIK_API_INSECURE=true
      - TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE=true
      - TRAEFIK_PROVIDERS_DOCKER_WATCH=true
      - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
      - TRAEFIK_LOG_LEVEL=INFO
      - TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_PERMANENT=true
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME=https
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO=websecure
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=:443
      - TRAEFIK_DOCKER_NETWORK=traefik-proxy
      # Certification resolver
      - IONOS_API_KEY=non_of_your_business  
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER=true
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_STORAGE=/letsencrypt/acme.json
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_EMAIL=non_of_your_business        
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_DNSCHALLENGE=true
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_DNSCHALLENGE_PROVIDER=ionos
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "~/docker_data/letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks:
      - traefik-proxy
    labels:
      - traefik.enable=true
      # dashboard
      - traefik.http.routers.traefik.rule=Host(`proxy.mydomain.com`)
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.tls.certresolver=myresolver
      - traefik.http.routers.traefik.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=non_of_your_business  twingate_trng:
    container_name: twingate_trng_SA
    image: ubuntu:latest
    privileged: true
    command: >
      bash -c "apt-get update
      && apt-get install curl -y
      && curl https://binaries.twingate.com/client/linux/install.sh | bash
      && sudo twingate setup --headless /etc/twingate-service-key/service-key.json
      && sudo twingate start
      && sleep infinity"
    volumes:
      - /root/proxy:/etc/twingate-service-key/
    # ports:
    #   - "3128:3128"
    restart: always
    networks:
      - traefik-proxy
    labels:
      - traefik.http.routers.etaai_api.rule=Host(`project.mydomain.com`) && PathPrefix(`/api`)
      - traefik.http.services.etaai_api.loadbalancer.server.url="alias.twingate.yolo/docs"
      - traefik.http.routers.etaai_api.entrypoints=websecure
      - traefik.http.routers.etaai_api.tls.certresolver=myresolver

So, in essence, I have an API that is connected via the container that I build using your script. I have a couple of endpoints like:

  • alias.twingate.yolo/ping
  • alias.twingate.yolo/docs

and so on.

The endresult should be something along the lines of:

  • project.mydomain.com/api/ping is being routed to alias.twingate.yolo/ping
  • project.mydomain.com/api/docs is being routed to alias.twingate.yolo/docs

and so on

I think the routing therefore should be something along the lines of:

  • project.mydomain.com/api/* is being routed to alias.twingate.yolo/*

So I am new to this topic and I am wondering how to handle this. Do I need an extra proxy for this or something?

More research made me find this: Traefik RedirectRegex Documentation - Traefik Do I need to redirect the traffic basically? And if so, I should probably use port 3128 to get to the client, shouldn’t I?

@peda,

A couple of things I’m noticing:

  • You only have one service proxy in your stack, and you load two images and container names and so on in it. I feel like you just lost the second service entry at some point, but the way it’s shown up there won’t work as is.
  • Based on your flow chart you basically need the Twingate Headless Client container to act as the Internet connection for your Traefik container. When you set up a stack with multiple containers, and one is acting as the network/Internet connection for the rest, that’s the one you need to load up with any port mappings. Think of it as that container (the one for the TG Headless Client in this case) is the main network connection both internally and externally for anything else in this stack, so you map all of the ports to it and the other containers will be able to utilize them.
  • The container for the TG Headless Client doesn’t need to be assigned a network, you can omit that parameter and let it pull in its own bridge (unless you want it to use another bridge, or host mode)
  • Port 3128 in our example is used because that’s what you’d point other services to in order to use the SOCK5 proxy via Squid, that example stack is basically opening up an internal network wide SOCK5 proxy you can point any other machine at, and then give them access to TG Resources (that are assigned to the Service Account loaded on to the Headless Client) or the Internet in general; you can ignore that if it’s not needed in your specific case

This is more what you should be trying, I’ve never used Traefik though so you’ll need to modify it to work:

version: '3'

services:
  tg-client:
    container_name: twingate_trng_SA
    image: ubuntu:latest
    privileged: true
    command: >
      bash -c "apt-get update
      && apt-get install curl -y
      && curl https://binaries.twingate.com/client/linux/install.sh | bash
      && sudo twingate setup --headless /etc/twingate-service-key/service-key.json
      && sudo twingate start
      && sleep infinity"
    volumes:
      - /root/proxy:/etc/twingate-service-key/
    ports: #all ports that need to be mapped should be with the Headless Client container
      - "80:80"
	  - "443:443"
    restart: always
  proxy:
    image: "traefik:latest"
    container_name: "traefik"
    restart: always
    environment:
      - TRAEFIK_API_INSECURE=true
      - TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE=true
      - TRAEFIK_PROVIDERS_DOCKER_WATCH=true
      - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
      - TRAEFIK_LOG_LEVEL=INFO
      - TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_PERMANENT=true
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME=https
      - TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO=websecure
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=:443
      - TRAEFIK_DOCKER_NETWORK=traefik-proxy
      # Certification resolver
      - IONOS_API_KEY=non_of_your_business  
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER=true
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_STORAGE=/letsencrypt/acme.json
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_EMAIL=non_of_your_business        
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_DNSCHALLENGE=true
      - TRAEFIK_CERTIFICATESRESOLVERS_MYRESOLVER_ACME_DNSCHALLENGE_PROVIDER=ionos
    volumes:
      - "~/docker_data/letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    network_mode: "service:tg-client" #tells this container to use the first service as its network bridge
	depends_on: #tells this container to not load until the first service does, and if the first one stops this one stops as well
	  - tg-client
    labels:
      - traefik.enable=true
      # dashboard
      - traefik.http.routers.traefik.rule=Host(`proxy.mydomain.com`)
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.tls.certresolver=myresolver
      - traefik.http.routers.traefik.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=non_of_your_business  twingate_trng:

I removed the networks at the top and from the Headless Client service, moved that to the top of the stack, moved the port mappings to it, and updated the network_mode in the Traefik container and added a dependency on the Headless Client container.

In theory now your Traefik container should be able to access the Internet in general, as well as any private Resources that you’ve assigned to the Service Account that’s loaded into this Headless Client (via the service key). As long as this Docker host has a connection to the Internet then the Client should be able to get out and talk to whatever.