1. The problem I’m having:
I am trying to restrict access to portainer using my domain but only within tailscale network so that sensitive service like this is not exposed outside of my network. But since I have setup authentik oauth, i want to use it with domain name instead of ipaddress:port.
2. Error messages and/or full log output:
When I access from outside of my LAN network, although with tailscale connected and active, I am getting response denied message that I have setup to show for access outside of my network. So looks like caddy is not recognizing tailscale network access or I have incorrectly setup the remote_ip config. I am not sure if I have set it up correctly, but even though I am connected in my tailscale net, I still cant access portainer using the domain name. It works using IP, so the tailscale net is active.
3. Caddy version: v2.10.0
4. How I installed and ran Caddy:
version: "3.8"
services:
caddy:
build:
context: .
dockerfile: Dockerfile
container_name: caddy
restart: unless-stopped
env_file:
- .env
environment:
- CLOUDFLARE_API_TOKEN=${CADDY_CLOUDFLARE_API_TOKEN} # Replace with your actual token (**don't commit this file to version control!**)
- CLOUDFLARE_EMAIL=${CADDY_CLOUDFLARE_EMAIL}
- ACME_AGREE=true
ports:
- "${COMPOSE_PORT_HTTPS:-443}:443"
- "${COMPOSE_PORT_HTTP:-80}:80"
- 2019:2019
volumes:
- /volume1/docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- /volume1/docker/caddy/data:/data
- /volume1/docker/caddy/config:/config
- /volume1/docker/caddy:/etc/caddy
- /volume1/docker/caddy/logs:/var/log/caddy/
- /volume1/docker/caddy/index.html:/usr/share/caddy/index.html
command: caddy run --config /etc/caddy/Caddyfile
networks:
- caddyNet
networks:
caddyNet:
external: true
a. System environment:
Ugreen NAS running UGOS (based on debian), Docker container. Tailscale is installed on the NAS and acts as exit node (192.168.3.198 is the DNS setup on tailscale admin console). Adguard can see tailscale client as it is running in host mode for DNS level ad blocking. All client pcs are connected to the same tailnet.b. Command:
PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.
c. Service/unit/compose file:
version: "3.8"
services:
caddy:
build:
context: .
dockerfile: Dockerfile
container_name: caddy
restart: unless-stopped
env_file:
- .env
environment:
- CLOUDFLARE_API_TOKEN=${CADDY_CLOUDFLARE_API_TOKEN} # Replace with your actual token (**don't commit this file to version control!**)
- CLOUDFLARE_EMAIL=${CADDY_CLOUDFLARE_EMAIL}
- ACME_AGREE=true
ports:
- "${COMPOSE_PORT_HTTPS:-443}:443"
- "${COMPOSE_PORT_HTTP:-80}:80"
- 2019:2019
volumes:
- /volume1/docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- /volume1/docker/caddy/data:/data
- /volume1/docker/caddy/config:/config
- /volume1/docker/caddy:/etc/caddy
- /volume1/docker/caddy/logs:/var/log/caddy/
- /volume1/docker/caddy/index.html:/usr/share/caddy/index.html
command: caddy run --config /etc/caddy/Caddyfile
networks:
- caddyNet
networks:
caddyNet:
external: true
d. My complete Caddy config:
{
admin 0.0.0.0:2019
email {env.CLOUDFLARE_EMAIL}
log {
output file /var/log/caddy/access.log {
roll_size 10MB
roll_keep 10
roll_keep_for 336h
}
format json
}
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
acme_ca https://acme-v02.api.letsencrypt.org/directory
servers {
trusted_proxies cloudflare {
interval 12h
timeout 15s
}
}
dynamic_dns {
provider cloudflare {env.CLOUDFLARE_API_TOKEN}
domains {
mydomain.com
}
check_interval 5m
}
}
(authentikv2) {
reverse_proxy /outpost.goauthentik.io/* http://192.168.3.198:9300 {
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
route {
forward_auth http://192.168.3.198:9300 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
trusted_proxies private_ranges
transport http {
dial_timeout 10s
response_header_timeout 30s
}
}
}
}
(at_ugnas) {
reverse_proxy 192.168.3.198:{args[0]}
}
(ts_gateway) {
@allowLocal {
remote_ip 192.168.0.0/16
}
@allowTailscale {
remote_ip 100.64.0.0/10
}
handle @allowLocal {
reverse_proxy {args.0}
}
handle @allowTailscale {
reverse_proxy {args.0}
}
respond "🔒 Access requires Tailscale VPN or LAN connection" 403
}
*.mydomain.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
ca https://acme-v02.api.letsencrypt.org/directory
resolvers 1.1.1.1
}
encode gzip zstd
@caddy host caddy.mydomain.com
handle @caddy {
root * /usr/share/caddy
php_fastcgi localhost:80
file_server
}
@ug host ug.mydomain.com
handle @ug {
import at_ugnas "7080"
#reverse_proxy 192.168.3.198:7080
}
@ak host ak.mydomain.com
handle @ak {
reverse_proxy ak-server:9000
}
@portainer-ug host portainer-ug.mydomain.com
handle @portainer-ug {
import ts_gateway "http://192.168.3.198:9000"
}
@adguard host adguard.mydomain.com
handle @adguard {
import authentikv2
import at_ugnas "9080"
#reverse_proxy 192.168.3.198:9080
}
}