Using Caddy with NextDNS rewrites

1. The problem I’m having:

I’m using NextDNS (via Tailscale) to configure a rewrite for *.localhost.internal → my MacBook’s Tailscale IP address. When I query a NextDNS endpoint to check the records, I see an A record for it.

Is there a way to configure Caddy, Lets Encrypt, or some other CA to use a custom DNS resolver so they see the A record?

2. Error messages and/or full log output:

2024/12/21 22:53:41.546	INFO	using adjacent Caddyfile
2024/12/21 22:53:41.548	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2024/12/21 22:53:41.548	WARN	Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies	{"adapter": "caddyfile", "file": "Caddyfile", "line": 2}
2024/12/21 22:53:41.550	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2024/12/21 22:53:41.550	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2024/12/21 22:53:41.551	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2024/12/21 22:53:41.551	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["localhost.internal"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"body":"hello world","handler":"static_response"}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
2024/12/21 22:53:41.552	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2024/12/21 22:53:41.553	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": true}
2024/12/21 22:53:41.553	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2024/12/21 22:53:41.553	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2024/12/21 22:53:41.554	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2024/12/21 22:53:41.554	INFO	http	enabling automatic TLS certificate management	{"domains": ["localhost.internal"]}
2024/12/21 22:53:41.555	INFO	autosaved config (load with --resume flag)	{"file": "/root/.config/caddy/autosave.json"}
2024/12/21 22:53:41.555	INFO	serving initial configuration
2024/12/21 22:53:41.557	INFO	tls.obtain	acquiring lock	{"identifier": "localhost.internal"}
2024/12/21 22:53:41.561	INFO	tls.obtain	lock acquired	{"identifier": "localhost.internal"}
2024/12/21 22:53:41.563	INFO	tls.obtain	obtaining certificate	{"identifier": "localhost.internal"}
2024/12/21 22:53:41.564	DEBUG	events	event	{"name": "cert_obtaining", "id": "93fff3c1-2059-4f81-9d99-c44c31e8bb2f", "origin": "tls", "data": {"identifier":"localhost.internal"}}
2024/12/21 22:53:41.565	DEBUG	tls.obtain	trying issuer 1/1	{"issuer": "acme-staging-v02.api.letsencrypt.org-directory"}
2024/12/21 22:53:41.565	INFO	tls.issuance.acme	waiting on internal rate limiter	{"identifiers": ["localhost.internal"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}
2024/12/21 22:53:41.566	INFO	tls.issuance.acme	done waiting on internal rate limiter	{"identifiers": ["localhost.internal"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}
2024/12/21 22:53:41.566	INFO	tls.issuance.acme	using ACME account	{"account_id": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/177129554", "account_contact": []}
2024/12/21 22:53:41.568	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc0000ef480"}
2024/12/21 22:53:41.572	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/root/.local/share/caddy", "instance": "8d56a6de-9e8b-4064-9c3b-a597ae4caa72", "try_again": "2024/12/22 22:53:41.572", "try_again_in": 86399.999998658}
2024/12/21 22:53:41.573	INFO	tls	finished cleaning storage units
2024/12/21 22:53:41.746	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "GET", "url": "https://acme-staging-v02.api.letsencrypt.org/directory", "headers": {"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["820"],"Content-Type":["application/json"],"Date":["Sat, 21 Dec 2024 22:53:41 GMT"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/21 22:53:41.746	DEBUG	tls.issuance.acme.acme_client	creating order	{"account": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/177129554", "identifiers": ["localhost.internal"]}
2024/12/21 22:53:41.799	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "HEAD", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce", "headers": {"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Date":["Sat, 21 Dec 2024 22:53:41 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["NqoFGPoyW5z0aF9F6uFrCahB-yzEXxmi8_1WtysXl9pK0lY1oFY"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/21 22:53:41.856	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-order", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["177129554"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["225"],"Content-Type":["application/problem+json"],"Date":["Sat, 21 Dec 2024 22:53:41 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["NqoFGPoyDcG4XdNfEtPHFqDCZNciExHXu0apxXKLan3_DQgtynY"],"Server":["nginx"]}, "status_code": 400}
2024/12/21 22:53:41.856	ERROR	tls.obtain	could not get certificate from issuer	{"identifier": "localhost.internal", "issuer": "acme-staging-v02.api.letsencrypt.org-directory", "error": "HTTP 400 urn:ietf:params:acme:error:rejectedIdentifier - Invalid identifiers requested :: Cannot issue for \"localhost.internal\": Domain name does not end with a valid public suffix (TLD)"}
2024/12/21 22:53:41.856	DEBUG	events	event	{"name": "cert_failed", "id": "3eca5d04-5ff7-43bc-9cf1-f34b10f7847c", "origin": "tls", "data": {"error":{},"identifier":"localhost.internal","issuers":["acme-staging-v02.api.letsencrypt.org-directory"],"renewal":false}}
2024/12/21 22:53:41.856	ERROR	tls.obtain	will retry	{"error": "[localhost.internal] Obtain: [localhost.internal] creating new order: attempt 1: https://acme-staging-v02.api.letsencrypt.org/acme/new-order: HTTP 400 urn:ietf:params:acme:error:rejectedIdentifier - Invalid identifiers requested :: Cannot issue for \"localhost.internal\": Domain name does not end with a valid public suffix (TLD) (ca=https://acme-staging-v02.api.letsencrypt.org/directory)", "attempt": 1, "retrying_in": 60, "elapsed": 0.29466728, "max_duration": 2592000}
^C2024/12/21 22:53:43.135	INFO	shutting down	{"signal": "SIGINT"}
2024/12/21 22:53:43.135	WARN	exiting; byeee!! 👋	{"signal": "SIGINT"}
2024/12/21 22:53:43.135	INFO	http	servers shutting down with eternal grace period
2024/12/21 22:53:43.136	INFO	admin	stopped previous server	{"address": "localhost:2019"}
2024/12/21 22:53:43.136	INFO	shutdown complete	{"signal": "SIGINT", "exit_code": 0}

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

a. System environment:

Debian 12 amd64

b. Command:

caddy run

d. My complete Caddy config:

{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
    debug
}

localhost.internal {
	respond "test"
}

Hi @proudparrot2,

That looks like the Certificate Authority being tried is Let’s Encrypt, yet “Domain name does not end with a valid public suffix (TLD)” above.
Let’s Encrypt offers Domain Validation (DV) certificates.

Thanks for pointing that out

I tried again with local.host (it’s propagated in NextDNS)

2024/12/22 00:48:16.013	INFO	using adjacent Caddyfile
2024/12/22 00:48:16.014	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2024/12/22 00:48:16.015	WARN	Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies	{"adapter": "caddyfile", "file": "Caddyfile", "line": 2}
2024/12/22 00:48:16.016	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2024/12/22 00:48:16.016	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2024/12/22 00:48:16.017	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2024/12/22 00:48:16.017	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["local.host"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"body":"hello world","handler":"static_response"}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
2024/12/22 00:48:16.017	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2024/12/22 00:48:16.018	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": true}
2024/12/22 00:48:16.018	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2024/12/22 00:48:16.018	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2024/12/22 00:48:16.018	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2024/12/22 00:48:16.019	INFO	http	enabling automatic TLS certificate management	{"domains": ["local.host"]}
2024/12/22 00:48:16.019	INFO	autosaved config (load with --resume flag)	{"file": "/root/.config/caddy/autosave.json"}
2024/12/22 00:48:16.019	INFO	serving initial configuration
2024/12/22 00:48:16.021	INFO	tls.obtain	acquiring lock	{"identifier": "local.host"}
2024/12/22 00:48:16.025	INFO	tls.obtain	lock acquired	{"identifier": "local.host"}
2024/12/22 00:48:16.027	INFO	tls.obtain	obtaining certificate	{"identifier": "local.host"}
2024/12/22 00:48:16.027	DEBUG	events	event	{"name": "cert_obtaining", "id": "15524952-13ea-4856-9e63-30853b9253d1", "origin": "tls", "data": {"identifier":"local.host"}}
2024/12/22 00:48:16.028	DEBUG	tls.obtain	trying issuer 1/1	{"issuer": "acme-staging-v02.api.letsencrypt.org-directory"}
2024/12/22 00:48:16.029	INFO	tls.issuance.acme	waiting on internal rate limiter	{"identifiers": ["local.host"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}
2024/12/22 00:48:16.029	INFO	tls.issuance.acme	done waiting on internal rate limiter	{"identifiers": ["local.host"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}
2024/12/22 00:48:16.029	INFO	tls.issuance.acme	using ACME account	{"account_id": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/177129554", "account_contact": []}
2024/12/22 00:48:16.030	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc0008f8000"}
2024/12/22 00:48:16.034	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/root/.local/share/caddy", "instance": "8d56a6de-9e8b-4064-9c3b-a597ae4caa72", "try_again": "2024/12/23 00:48:16.034", "try_again_in": 86399.999998828}
2024/12/22 00:48:16.034	INFO	tls	finished cleaning storage units
2024/12/22 00:48:16.194	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "GET", "url": "https://acme-staging-v02.api.letsencrypt.org/directory", "headers": {"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["820"],"Content-Type":["application/json"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/22 00:48:16.195	DEBUG	tls.issuance.acme.acme_client	creating order	{"account": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/177129554", "identifiers": ["local.host"]}
2024/12/22 00:48:16.246	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "HEAD", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce", "headers": {"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["StwmGFQhI_qDhhVPpvcXvqPezMPxdpFRsNjkSksVFe2DCTMRkdE"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/22 00:48:16.327	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-order", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["177129554"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["356"],"Content-Type":["application/json"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Location":["https://acme-staging-v02.api.letsencrypt.org/acme/order/177129554/21484815234"],"Replay-Nonce":["StwmGFQhUR7Q2NfaMEOv030mWCH5FLchAvjx3zhtoosalsPKs9M"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 201}
2024/12/22 00:48:16.384	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/authz/177129554/15460663054", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["177129554"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["836"],"Content-Type":["application/json"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["NqoFGPoy5mf9N681OOgnNENCV3sjQ8hiBRSIy6rPAUH4od_jdYo"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/22 00:48:16.384	INFO	tls.issuance.acme.acme_client	trying to solve challenge	{"identifier": "local.host", "challenge_type": "http-01", "ca": "https://acme-staging-v02.api.letsencrypt.org/directory"}
2024/12/22 00:48:16.385	DEBUG	tls.issuance.acme.acme_client	waiting for solver before continuing	{"identifier": "local.host", "challenge_type": "http-01"}
2024/12/22 00:48:16.385	DEBUG	tls.issuance.acme.acme_client	done waiting for solver	{"identifier": "local.host", "challenge_type": "http-01"}
2024/12/22 00:48:16.443	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall/177129554/15460663054/jZapKw", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["177129554"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["201"],"Content-Type":["application/json"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\"","<https://acme-staging-v02.api.letsencrypt.org/acme/authz/177129554/15460663054>;rel=\"up\""],"Location":["https://acme-staging-v02.api.letsencrypt.org/acme/chall/177129554/15460663054/jZapKw"],"Replay-Nonce":["NqoFGPoye9JLjpUqfmThNFdlGfzTBYbQj0EK95Q7X-vhRY9r7qg"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/22 00:48:16.444	DEBUG	tls.issuance.acme.acme_client	challenge accepted	{"identifier": "local.host", "challenge_type": "http-01"}
2024/12/22 00:48:16.750	DEBUG	tls.issuance.acme.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/authz/177129554/15460663054", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.8.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["177129554"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["743"],"Content-Type":["application/json"],"Date":["Sun, 22 Dec 2024 00:48:16 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["StwmGFQhWdEhG6n6bywniypc4P6thOsJYepZuNyat8-FzxYqQZE"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2024/12/22 00:48:16.751	ERROR	tls.issuance.acme.acme_client	challenge failed	{"identifier": "local.host", "challenge_type": "http-01", "problem": {"type": "urn:ietf:params:acme:error:dns", "title": "", "detail": "DNS problem: NXDOMAIN looking up A for local.host - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for local.host - check that a DNS record exists for this domain", "instance": "", "subproblems": []}}
2024/12/22 00:48:16.751	ERROR	tls.issuance.acme.acme_client	validating authorization	{"identifier": "local.host", "problem": {"type": "urn:ietf:params:acme:error:dns", "title": "", "detail": "DNS problem: NXDOMAIN looking up A for local.host - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for local.host - check that a DNS record exists for this domain", "instance": "", "subproblems": []}, "order": "https://acme-staging-v02.api.letsencrypt.org/acme/order/177129554/21484815234", "attempt": 1, "max_attempts": 3}
^C2024/12/22 00:48:17.363	INFO	shutting down	{"signal": "SIGINT"}
2024/12/22 00:48:17.363	WARN	exiting; byeee!! 👋	{"signal": "SIGINT"}
2024/12/22 00:48:17.363	INFO	http	servers shutting down with eternal grace period
2024/12/22 00:48:17.363	INFO	admin	stopped previous server	{"address": "localhost:2019"}
2024/12/22 00:48:17.363	INFO	shutdown complete	{"signal": "SIGINT", "exit_code": 0}

And yeah, no A record found

1 Like

Yes Caddy has resolvers config in a few spots.

You cannot get a publicly trusted cert with a “fake” domain like *.internal, (i.e. you can’t get a cert from Let’s Encrypt). You can have Caddy issue a TLS cert though using its internal CA, which means you’d need to grab Caddy’s root CA cert and install it on any/all devices that would connect to your server.

Or, you can use Tailscale .ts.net domains which deals with automating TLS automatically if Caddy can access the Tailscale daemon on the same machine.

1 Like

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