Access certain services only if connected to tailscale

Hi everyone

1. The problem I’m having:

I’m struggling to make caddy and tailscale work the way I want. I’ve followed various tutorials but I’m not a native speaker and I think the truth is that I struggle to catch the inner logic behind the whole DNS stuff.

  • I have a Synology nas running caddy, tailscale and a 2 services as docker containers
    • Tailscale NAS IP : 100.XX.XX.X
  • I own a domain with a DNS A entry making *.example.com pointing to my Public router IP
  • Tailscale is installed on a few other devices (laptop, phones…), it seems to be working fine as it is, I’ve customized my NAS machine as NAS for magicdns

I want service1.example.com to be served to anyone and service2.example.com to be served only to people using tailscale.

service1 works fine and is accessible to anyone, certificates also work

2. Error messages and/or full log output:

If the bind comment is active, caddy doesn’t start, if uncommented as here, I get the 403 message even though I’m connected with tailscale.

3. Caddy version:

Version is 2.8.4

4. How I installed and ran Caddy:

I’ve built a docker package with xcaddy in order to have infomaniak DNS.

FROM caddy:builder-alpine AS builder

RUN xcaddy build --with github.com/caddy-dns/infomaniak

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

d. My complete Caddy config:


{
email XXX@example.com
}

(ts_host) {

#bind {env.TAILNET_IP} 

@blocked not remote_ip 100.64.0.0/10

tls {

resolvers 1.1.1.1

dns infomaniak {env.API_TOKEN}

}

respond @blocked "Unauthorized" 403

}

*.example.com {

tls {

dns infomaniak {env.API_TOKEN} #this part seems to work fine

}

}

service1.example.com{

reverse_proxy 192.168.1.2:XXXX #this works but not if I put my tailscale NAS IP ??

}

service2.example.com {

import ts_host

reverse_proxy 192.168.1.2:YYYY

}

5. Links to relevant resources:

I’ve tried to follow this guide here as it seems close to what I try to achieve but I might be misguided.

A couple of questions:

  1. Is any of your Tailscale devices in your NAS LAN configured as a subnet router for your NAS LAN?
  2. If you have a subnet router, by default it does SNAT of your Tailscale traffic to the LAN IP address of the Tailscale subnet router device. Did you disable such SNAT?

If 1=yes and 2=no, your Tailscale traffic for service2.example.com is coming from the LAN IP of your Tailscale subnet router and not from 100.64.0.0/10, so adjust your @blocked matcher accordingly.

First of all thank you for your help :slight_smile:

For additional contexte, here is the thing, my NAS is behind a router. The router forwards ports 80/443 to caddy inside a docker container using another port as 443 is reserved by other synology services I can’t disable.

I have setup my nas as a subnet router (on the NAS and the tailscale portal)

I’ve configured my domain to redirect *.int.example.com with a CNAME to the tailscale IP adress of my NAS

The problem is that port 443 is reserved and I think that when I type XXX.int.example.com it doesn’t even reach caddy, as if it was directly going to the NAS, as it then redirect me directly to the NAS admin page.

Don’t know it if helps, but going back to your answer I would say 1/yes 2/no, but not clear as what I should do as caddy doesn’t seem to catch the traffic at all

Thanks

I have the same setup as you have. Almost the same.

  • *.example.com is pointed to the public IP of my home router
  • Port 443 on the router is forwarded to Caddy running inside a docker container on my NAS on port 8443
  • My NAS runs Tailscale as a subnet router with Tailscale SNAT enabled (default setup)
  • public.example.com has no restrictions and available from the Internet
  • private.example.com has @blocked defined as anyone not from 192.168.100.0/24 (my LAN), which includes my NAS, which includes my tailnet. You could probably narrow it down to just your NAS IP. That allows only my LAN and tailnet to talk to private.example.com

Edit: I should also mention that my Caddy container runs with host network.

Oh, I have one more thing configured - a Tailscale App Connector for private.example.com, which makes sure that I always talk to private.example.com via Tailscale, when I’m connected to my tailnet.

So just to be sure,

  • you forward private.example.com with a CNAME to the tailscale IP of your NAS ?
  • you just edit caddy as such ?
@blocked not remote_ip 192.168.100.0/24

did you also need to use the "bind {env.TAILNET_IP} " command in caddy ? (caddy doesn’t run it I try to)

Thx

No, port 443 on my home router is forwarded to LAN IP of my NAS, port 8443, where Caddy listens.

I do not bind or forward to the Tailscale IP at all.

Ok, I tried to mirror your config, I get systematically blocked, I guess the app connector is crucial, seems complicated though :slight_smile: currently trying

Could you also show me your tailscale ACL or see if you see something wrong in mine, I tried the App Connector, but still no luck

{
	"acls": [
		{
			"action": "accept",
			"src": [
				"*",
				"autogroup:member",
			],
			"dst": [
				"*:*",
				"autogroup:internet:*",
			],
		},
	],

	"tagOwners": {
		"tag:internal-app": [],
	},

	"autoApprovers": {
		"routes": {
			"0.0.0.0/0": ["tag:internal-app"],
			"::/0":      ["tag:internal-app"],
		},
	},

	"nodeAttrs": [
		{
			"target": ["*"],
			"app": {
				"tailscale.com/app-connectors": [
					{
						"name":       "Internal",
						"connectors": ["tag:internal-app"],
						"domains": [
							"*.int.example.com",
						],
					},
				],
			},
		},
	],

	"ssh": [
		{
			"action": "check",
			"src": [
				"autogroup:member",
			],
			"dst": [
				"autogroup:self",
			],
			"users": [
				"autogroup:nonroot",
				"root",
			],
		},
	],
}

I’d share my Caddy config with you, but I’m travelling right now and typing from my phone - sorry about that!

1 Like

Looks good. I would probably do this:

"domains": [
	"int.example.com",
	"*.int.example.com",
],

i.e. add both int.example.com and *.int.example.com

*.int.example.com on its own doesn’t cover int.example.com