Wildcard cert with logs separated by hostname

1. The problem I’m having:

I am trying to segregate logs between different containers. Ideally the log directive could be used inside the handle directive, however that is not possible.

I understand if it were possible to sub in {host} to the log filename, it would create an inode attack vector. If log could be defined within handle, then it would only create a new file for domains that are defined in the config, then fall back on default for everything else.

I could define each subdomain explicitly, but then I lose the privacy of using a wildcard cert as then my subdomains are vulnerable to being scraped by would-be attackers, shodan, and the like.

So, I’m at an impasse.

2. Error messages and/or full log output:

not relevant

3. Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

4. How I installed and ran Caddy:

xcaddy build \
    --with github.com/caddyserver/transform-encoder \
    --with github.com/hslatman/caddy-crowdsec-bouncer/http \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/mholt/caddy-webdav \
    --with github.com/greenpau/caddy-security \
    --with github.com/porech/caddy-maxmind-geolocation

a. System environment:

lxc, debian.

b. Command:

ExecStart=/usr/local/bin/caddy run --environ --watch --config /etc/caddy/Caddyfile

c. Service/unit/compose file:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
Environment="HOME=/var/lib/caddy"
Environment="XDG_HOME=/var/lib/caddy"
ExecStart=/usr/local/bin/caddy run --environ --watch --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateDevices=yes
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

(auth) {
	reverse_proxy /outpost.goauthentik.io/* http://172.16.1.0:9000 {
		header_up host id.example.com
		header_up Connection keep-alive, Upgrade
		# header_up Upgrade websockets
	}
	forward_auth http://172.16.1.0:9000 {
		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
		header_up host id.example.com
		header_up Connection keep-alive, Upgrade
		# header_up Upgrade websockets
	}
	# # always forward outpost path to actual outpost
}
(tls_data) {
	tls {
		dns cloudflare [redacted]
		resolvers 1.1.1.1
	}
	log {
		output file /var/log/caddy/access.log {
			roll_size 1gb
			roll_keep 5
			roll_keep_for 720h
		}
		format console
		level INFO
	}
	#tls /gordo/proxmox/deploy_scripts/caddy/certs/-.example.com/fullchain.pem /gordo/proxmox/deploy_scripts/caddy/certs/-.example.com/key.pem
}

(subdomain-auth) {
	@sub-{args.0} {
		host {args.0}.example.com
		not remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
	}
	@sub-{args.0}-lan {
		host {args.0}.example.com
		remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
	}

	handle @sub-{args.0} {
		import proxy {args.1}
		import auth
	}
	handle @sub-{args.0}-lan {
		import proxy {args.1}
	}
}
(subdomain-auth-tls) {
	@sub-{args.0} {
		host {args.0}.example.com
		not remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
	}
	@sub-{args.0}-lan {
		host {args.0}.example.com
		remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
	}

	handle @sub-{args.0} {
		import proxy-tls {args.1}
		import auth
	}
	handle @sub-{args.0}-lan {
		import proxy-tls {args.1}
	}
}
*.example.com {
	import tls_data
	encode gzip
	@blocked {
		path *service-worker.js
	}
	@wan {
		not remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
	}
	respond @blocked 404
	import subdomain ha http://172.16.0.4:8123 # https://ha.example.com
	import subdomain id http://172.16.1.0:9000 # https://id.example.com
	import subdomain bw http://172.16.1.0:8081 # https://bw.example.com
	import subdomain plex http://172.16.1.0:32400 # https://plex.example.com
	import subdomain-auth invoke http://172.16.0.2:7861 # https://invoke.example.com
	import subdomain-auth-tls proxmox https://172.16.0.222:8006 # https://proxmox.example.com
	import subdomain-auth-tls prox https://172.16.0.7:8006 # https://prox.example.com
	import subdomain-auth radarr http://172.16.1.0:7878 # https://radarr.example.com
	import subdomain-auth sonarr http://172.16.1.0:8989 # https://sonarr.example.com
	import subdomain-auth overseerr http://172.16.1.0:5055 # https://overseerr.example.com
	import subdomain-auth tautulli http://172.16.1.0:8181 # https://tautulli.example.com
	import subdomain-auth nzbget http://172.16.1.0:6789 # https://nzbget.example.com
	import subdomain-auth flaresolverr http://172.16.1.0:8191 # https://flaresolverr.example.com
	import subdomain-auth prowlarr http://172.16.1.0:9696 # https://prowlarr.example.com
	import subdomain-auth lidarr http://172.16.1.0:8686 # https://lidarr.example.com
	import subdomain-auth stash http://172.16.1.0:9999 # https://stash.example.com
	import subdomain-auth qbittorrent http://172.16.1.0:8080 # https://qbittorrent.example.com
	import subdomain-auth frigate http://172.16.1.0:5000 # https://frigate.example.com
	import subdomain-auth webrtc http://172.16.1.0:8083 # https://frigate.example.com
	import subdomain-auth grafana http://172.16.1.0:3000 # https://grafana.example.com
	import subdomain-auth-tls opnsense https://172.16.0.254:8443 # https://opnsense.example.com
	import subdomain-auth wg http://172.16.1.0:51821 # https://wg.example.com
	import subdomain-auth home http://172.16.1.0:3001 # https://home.example.com
	import subdomain-auth cadvisor http://172.16.1.0:9081 # https://cadvisor.example.com
	import subdomain-auth kerberos http://172.16.1.0:8082 # https://kerberos.example.com

	file_server {
		hide .git
		root /gordo/Public/www/
	}
	root * /gordo/Public/www/

	@webDav host files.example.com
	handle @webDav {
		@get {
			method GET
			not path /.*
		}
		route {
			basicauth * bcrypt {
				alice [redacted]
			}
			file_server @get browse {
				root /gordo
				hide *~ *Zone.Identifier .git *.filebot*
			}
			webdav
		}
	}
	@static host static.example.com
	handle @static {
		@get {
			method GET
			not path /.*
		}
		file_server @get browse {
			root /gordo/Public
		}
	}

5. Links to relevant resources:

Domains are public information anyway though :thinking:

What exactly are you trying to do? All I really can figure is this:

I am trying to segregate logs between different containers

But it’s not clear to me what that is or why containerization is something that Caddy should be solving…

There isn’t currently a way to configure access logging per subdomain when using a wildcard site block unfortunately. The log directive was written to always auto-detect the hostname to log for using the site block’s hostnames.

But… I realized it would be pretty simple to implement after a bit of mental gymnastics to get my thinking there. I implemented it real quick, here’s the PR:

1 Like

segregate logs between different subdomains* while still using a wildcard cert.

Subdomains are not necessarily public information so long as they don’t get indexed. issued SSL certs get published and that information gets scraped, leading to “security researchers” sending traffic to domains with publicly issued letsencrypt certs. It is more private to use a wildcard cert for this reason. Obviously not a foolproof solution which is why most of my subdomains are run behind authentik via forward auth, but it avoids unnecessary traffic clogging up my logs.

wow thanks, hope it gets approved

Are you creating DNS records for your subdomains?

Are you creating DNS records for your subdomains?

the only DNS record is *.example.com, unless the subdomain needs an A record that goes to a different IP not pointed to caddy. on my domain www. has an A record pointed to a different webserver

Any DNS lookup is observable though, as they typically happen over plaintext. Even with DNS over TLS/HTTPS, the provider of that service can see what domains you’re looking up. I would strongly recommend not putting secrets in domain names.

I dont think there are any secrets in the domain names, just trying to remain as obscure as possible to avoid getting my http logs spammed with bots

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.