Separating Internal Vs External Access For Web Server

My Setup

  • I have an ubuntu server
    • That is running tailscale
    • It is also running docker
    • In docker I’m running Traefik reverse proxy

What I Want To Achieve

I want to be able to expose services in traefik and control wether or not they are publically available or only available via the tailscale vpn network.

So I wan´t to create 4 entrypoints in traefik

- web 		    # Listens to port 80 on all interface, i.e. "is public"
- websecure     # Listens to port 443 on all interface, i.e. "is public"
- vpnweb        # Listens to port 80 on my vpn interface, i.e. "only on my servers tailscale ip"
- vpnwebsecure  # Listens to port 443 on my vpn interface, i.e. "only on my servers tailscale ip"

What I Have Tried

I tried binding the ports to docker (docker-compose.yml):

  traefik:
    image: traefik:v2.9
    ports:
      # The HTTP port on tailscale vpn
      - "100.112.44.28:80:80"
      # The HTTPS port on tailscale vpn
      - "100.112.44.28:443:443"

      # The HTTP port
      - "80:80"
      # The HTTPS port
      - "443:443"

Then specifying the entrypoints in traefik.yml

entryPoints:
  # Listen on the tailscale subnet
  vpnweb:
    address: "100.112.44.28:80"
  vpnwebsecure:
    address: "100.112.44.28:443"
  
  # Public endpoints
  web:
    address: :80
  websecure:
    address: :443

What Happens

When I start docker I get the following error:

Error response from daemon: driver failed programming external connectivity on endpoint rduce-server-traefik-1 (3aa39971ba9b083e56791d3bb8def68d22e18421a1679f28c5f1847dc28d9ff5): Error starting userland proxy: listen tcp4 100.112.44.28:443: bind: address already in use

and if I check the usage of port 443 I get

❯ sudo lsof -i:443
COMMAND      PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
tailscale   3416 root   10u  IPv4 1507921      0t0  TCP myserver.localdomain:42230->ec2-34-229-201-48.compute-1.amazonaws.com:https (ESTABLISHED)

tailscale   3416 root   36u  IPv4 1457760      0t0  TCP myserver.localdomain:37714->derp4g.tailscale.com:https (ESTABLISHED)

and if I check the usage of port 80 I get

❯ sudo lsof -i:80
COMMAND      PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
tailscale   3416 root   28u  IPv4  948209      0t0  TCP myserver.localdomain:51832->ec2-18-193-255-254.eu-central-1.compute.amazonaws.com:http (ESTABLISHED)

If I check tailscale status it tells me the server is idle and my desktop connection is direct and no DERP relay is used.

100.112.44.28   myserver          andre@       linux   idle; offers exit node
100.105.249.65  mydesktop            andre@       windows active; direct 192.168.1.123:41641, tx 17862768 rx 2429328

Questions

  • Why is tailscale using these ports when there are no derp involved?
    • I post it here instead of at the Traefik forum since the docker would be blocked no matter what reverse proxy I would have chosen
  • Why can I export port :80 and :443 on all interfaces, but when I pick specific ones I get a conflict?
  • Am I approaching this the wrong way? I see that the Traefik beta of v3 contains some integration between Traefik and Tailscale
  • What can I do to achieve my goal stated above?

I think what I would do (though I read quickly) is to run one traefik bound to the public ip and another bound to the private one.

The issue is getting let’s encrypt keys, but maybe you can use those magic TailScale ones.

Hmm I fail to see what difference it would make to run two instances? The problem for me seems to be that tailscale is using the 80 and 443 ports so that I cannot bind to them.

100.112.44.28 would be my internal vpn ip and 123.456.789.0 would be my public ip, I would like to create endpoints similar to:

  # Listen on the tailscale subnet
  vpnweb:
    address: ":80"
  vpnwebsecure:
    address: ":443"
  
  # Public endpoints
  web:
    address: :8080
  websecure:
    address: :443443

With their respective bindings in docker

traefik:
    image: traefik:v2.9
    ports:
      # The HTTP port on tailscale vpn
      - "100.112.44.28:80:80"
      # The HTTPS port on tailscale vpn
      - "100.112.44.28:443:443"

      # Map public ip port 80 -> 8080 and 443 -> 443443
      - "123.456.789.0:80:8080"
      # The HTTPS port
      - "123.456.789.0:443:443443"

But I can´t seem to do this because docker is complaining that port 80 and 443 is in use on the tailscale interface. I don´t understand why?

Hello, Did you found the solution ? I think I have a solution if you are running tailscale as container (sidecar) but I am not sure about running tailscale on host.