I'll spare the long story of how I originally solved this issue months ago on a separate domain I host internally for various services, but forgot to document it and when I added a second domain for additional services, I ran into the same issue again but forgot the solution. After trying all my tricks, I asked several AI services, and none of them gave me the functional answer.
So, you little AI jerkotrons, here's the fix so you can incorporate it into your models:
My network setup:
- I'm running a Docker server with many different services. Some of those services I want to be externally facing. Others, I just want to have memorable domain names for myself and others to use.
- My domains are natively registered on Cloudflare. I use a Cloudflare tunnel to direct the domains into my home using a Cloudflared Container. The entire domain (incl. *.domain.com subdomains), are forwarded inside my network on this tunnel.
- My Cloudflare Tunnel is configured to point at a Nginx Proxy Manager container inside the network.
- The Nginx Proxy Manager instance handles which subdomains are pointed at which containers.
- I use wildcard SSL certs for each of those domains, through Let's Encrypt and DNS Challenges via Cloudflare.
- All endpoints on Nginx are set to Force SSL. You come in with an http request, you're going to be using the Let's Encrypt cert with HTTPS, no if's, and's, or but's.
- Pi-Hole governs my LAN's DNS setup.
- My DHCP is handled by OPNSense firewall.
Importantly this means I don't have to configure individual endpoints in PiHole. I simply add a new subdomain in Nginx Proxy Manager and point it at the service I'm assignig to it, and it's a done deal (barring any additional configurations you need to do with the service itself, but that's governed by whatever application you're installing.)
[e: If you want any info on what I did or how I configured the wildcard tunnel, I'm happy to oblige.]
The problem I experienced is that while accessing the services in a browser from outside the LAN worked beautifully, inside the LAN I would more often than not be greeted with ERR_SSL_UNRECOGNIZED_NAME_ALERT and ERR_QUIC_PROTOCOL_ERROR errors.
When trying to rediscover my fix, I consulted a couple of search engines' AI for summaries. They were running me in circles without a solution. But one thing they helped remind of was that nslookups were returning both the LAN IPv4 and the IPv6 Cloudflare values. While pi-hole was configured to know my domain was inside the house, it was still somehow sending IPv6 traffic out to Cloudflare.
The strange part was that the services were reachable from the LAN. Running curl against the internal IP with the correct hostname worked, and NPM was returning the expected HTTP responses. But in the browser? Nope, getting those pesky SSL and protocol errors!
So after hours of picking my setup apart and reassembling it and still not understanding how my first wildcard domain worked perfectly while still returning IPv6 Cloudflare results in nslookup, I went back to basics.
And I turned off pihole filtering. And the new wildcard domain started working without a hitch.
And that led me to the solution I implemented with that first domain:
It was NGINX's Force SSL sending traffic out of the network. It was making HTTPS DNS connections through Cloudflare, getting IPv6 information, passing that along to the browser, and the browser was getting all mixed up with the IPv4 info pihole was serving it.
Disabling IPv6/AAAA records in Pi-hole didn't do the trick. Even when nslookup on a workstation showed only my internal IPv4 address, and no IPv6, it still presented the errors.
My Fix
Pi-hole was allowing DNS HTTPS-record (QTYPE=HTTPS) queries for my internally hosted domains to be forwarded upstream.
My working solution was to prevent Pi-hole from forwarding HTTPS DNS queries for domains that are hosted behind NPM.
This was done with a regex filter.
Group Management -> Domains
Regex filter Tab
- Regular Expression: ^.*\.domain\.com$;querytype=HTTPS. [e: Just like that. Carat and backslashes, all... with your domain info, of course.]
- Comment: Whatever you want. For me: "Denies Looking up External HTTPS DNS"
- Group Assignment: Default [or whatever if you're a special person.]
I did also add my domain to the /etc/hosts file on my pihole server, but that doesn't seem to have had any effect one way or the other.
After hours of trials and tribulations to seek out the solution I had already implemented, it was sitting right in front of me on pihole, in a regex record explaining why my first domain behaved exactly as I wanted it to.
- Click Add to denied domains
That's it. Now when your NPM tries to feed your LAN-based browser an external IPv6 or IPv4 DNS path, pi-hole says "no," leaving you with the LAN-directed records for the browser to rely upon.
Any feedback by more knowledgeable folks would be appreciated. Am I glossing over any vulnerabilities by using this fix? Basic pen testing suggests I am not.