Reverse Proxy and HTTPS for multiple services over Tailscale

1. Caddy version (caddy version):

v2.5.1

2. How I run Caddy:

Using Caddy as a reverse-proxy for HTTPS on Tailscale VPN.

a. System environment:

Ubuntu 20.04, systemd for init, services running inside Docker containers, Docker version 20.10.17, access via Tailscale 1.26.0

b. Command:

sudo systemctl start caddy

c. Service/unit/compose file:


d. My complete Caddyfile or JSON config:

origin.tailnet-c74e.ts.net {
        reverse_proxy origin:34843
}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:1080
#}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:8096
#}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:9090
#}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:3303
#}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:8000
#}

#origin.tailnet-c74e.ts.net {
#       reverse_proxy origin:8001
#}

3. The problem I’m having:

I have Caddy serving HTTPS in reverse-proxy mode to a service on port 34843 using a Tailscale-issued domain. (*.tailnet-c74e.ts.net). Nothing is exposed to the public internet. This is working great. I have a number of other services running on the same machine, but on different ports (1080, 8096, 9090, 3303, 8000, 8001) for which I would also like to enable HTTPS. So far as I can tell, it is not possible to modify the A-record for my tailscale domain to dish out subdomains. I don’t care how my URLs are formatted or if there are subdomains involved, but I’d like to have TLS working for each of the additional services in my Caddyfile. Right now I have entries for each service but they’re commented out since the host/domain name for each is duplicative. Is there a way to do this with i.e. wildcards? If there is already a document outlining this use case, please point me in the right direction; my own searches have not been fruitful.

4. Error messages and/or full log output:

No errors per se, just not sure how I should proceed.

5. What I already tried:

I have tried formatting the URLs in my Caddy file as, for example:

origin.tailnet-c74e.ts.net:1080 {
      reverse_proxy origin:1080
}

As well as

rockinsvc.origin.tailnet-c74e.ts.net {
       reverse_proxy origin:1080
}

neither of which actually work, which is unsurprising given that I’m just winging it over here.

6. Links to relevant resources:

Looks like Tailscale doesn’t support custom subdomains per-machine yet:

You’ll either need to use a different port for each service, or route differently based on something in the URI like a sub-path (but beware, many backend apps don’t like being proxied in subfolders).

Actually, you probably could serve on any tailnet Host / domain name that you want (including a subdomain such as service.origin.tailnet-c74e.ts.net) as long as the clients do the following:

  • Force the DNS to resolve to the machine of interest (origin.tailnet-c74e.ts.net in your case I believe). This might involve running your own DNS server/resolver or hard-coding the resolution IP into clients (for example, curl has a --resolve flag to pin a domain to an IP address manually).
  • Put origin.tailnet-c74e.ts.net in the ServerName in the ClientHello.
  • Send the service.origin.tailnet-c74e.ts.net subdomain in the Host header of subsequent HTTP requests. (Caddy won’t allow this if you enable TLS client auth, just FYI.)

So, definitely not convenient… but probably doable in theory?

Thanks for the quick reply.

I’m still a relative neophyte in this whole DNS realm so I’m having some trouble interpreting the language in your first sentence “… need to use a different port for each service”. Am I not already doing this? Each service hangs on its own port number as indicated in the Caddyfile. Are we using the term “port” in some other context here? Just addressing my own ignorance of the subject matter with this question.

The more I read about this the more it seems like I will need to fire up a DNS server to get this going. I was hoping I could get away with something a bit more basic like editing /etc/hosts or something on that order of complexity but it seems that’s not in the cards. I wonder if I could address this with a Pi-hole or even just basic DNSmasq? If I get it working I will circle back here and say how I did it for posterity.

I currently am having this exact problem.

I have one service of my caddy stack working like this:

computername .yy-yy. ts.net {
reverse_proxy localhost:90
}

now hitting that url in my network gives me beautifull ssl connection. The problem is now indeed, how do I go about getting my other 10 services also over ssl?

Bit surprised this isn’t communicated anywhere in the tailscale docs.

The hacky way would be to spin up a vm per subdomain en then a caddy with reverse proxy, but seems a bit convuluted?

any news? on this?

Yeah, that’s what I mean. I’m saying that if I were you I’d probably just do it the way you’re doing it :man_shrugging: since I don’t know another simple way right now.