How to identify clients when using DNS-over-HTTPS (thru doh-proxy)

I followed the instructions in HMN76V's post for setting up DNS-over-HTTPS with a PiHole.

It works (except pi.hole doesn't resolve over DoH; I assume fixable, but NBD - I can just use its IP). However, "as expected," DNS requests that go through the local doh-proxy on the raspberry pi, and then into pihole itself are each identified as being from the localhost client.

Clients that connect directly to PiHole unencrypted, are correctly-identified.

How can I fix this?

I want to use DoH on my network, but I also want to be able to use the grouping features to apply wider or narrower filters to various sets of devices (i.e. work computers maybe get some leeway, "smart" devices get aggressively filtered).

If PiHole supported DoH natively (all I could find was a closed, undiscussed feature request from 2021), this wouldn't be a problem since DoH requests could terminate directly in PiHole and it would get the correct IP for each client....

Can this even be done with additional software on a pihole server, or will I have to have native DoH support in the PiHole to be able to identify DoH clients?

Since "DNS queries (unlike http) don't have a notion of 'forwarded for' headers," achieving this with additional software around PiHole is actually impossible, right?

Please help me end my wild goose-chase in either success, or closure!

PiHole Info:

  • pihole has a static IP lease in DHCP pool of router; router is the LAN's DHCP server
  • pihole is set as sole DNS in router's DHCP config
  • lighttpd forwards DoH queries on <pihole IP>:443 to doh-server on localhost:3000
  • doh-server terminates SSL & forwards unencrypted DNS queries to pihole-FTL on localhost:53
  • (upstream DNS is a local unbound on localhost:5335 but I don't think that's relevant to my DoH identification issues)

EDIT: After some progress, I realized that I don't just need A DoH server, I need a DoH server that can ensure there is EDNS client subnet information for pihole to consume.

I did at least manage to get DoH traffic clearly identified, though DoH clients are still all combined:

  1. Start the DoH Proxy on a unique IP; 127.0.1.2:
    /etc/systemd/system/doh-proxy.service

     [Unit]
     Description=DNS over HTTPS server proxy
     After=syslog.target network-online.target
     
     [Service]
     Type=simple
     User=doh-proxy
     ExecStart=/usr/local/bin/doh-proxy --server-address 127.0.0.1:53 --local-bind-address 127.0.1.2:3000 --listen-address 127.0.1.2:3000 --hostname doh-proxy.hole
     Restart=on-failure
     RestartSec=10
     KillMode=process
     
     [Install]
     WantedBy=multi-user.target
    
  2. Local DNS entry for the name in /etc/hosts:

     ...
     127.0.1.2       doh-proxy.hole
    
  3. Modify lighttpd conf for the proxy to use that new IP:

    /etc/lighttpd/conf-available/20-doh-proxy.conf

     # Load necessary lighttpd modules
     server.modules += ("mod_openssl", "mod_proxy")
    
     # Forward DoH DNS queries to doh-proxy
     proxy.server = ("/dns-query" => ( (
       "host" => "127.0.1.2",
       "port" => 3000
     ) ) )
    
     $HTTP["remoteip"] != "127.0.0.1" {
       # Ensure the Pi-hole Block Page knows that this is not a blocked domain
       setenv.add-environment = ("fqdn" => "true")
    
       # Enable the SSL engine when using port 443
       $SERVER["socket"] == ":443" {
     	ssl.engine = "enable"
     	ssl.pemfile = "/root/certs/pi.hole.pem"
     	ssl.honor-cipher-order = "enable"
     	ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
       }
    
       # Redirect HTTP to HTTPS
       $HTTP["scheme"] == "http" {
     	$HTTP["host"] =~ ".*" {
     	  url.redirect = (".*" => "https://%0$0")
     	}
       }
     }
    

Now, DoH queries don't show up as localhost but as doh-proxy.hole.

The thinking was that if I could do this on-demand, in software, for every client coming in to DoH, that I'd be able to classify requests properly.
Connection -> read client hostname -> create /etc/hosts entry -> spin up new doh-proxy process configured for that client name -> some sort of teardown/cleanup logic for unused ones

It looks like the theory's sound, but it would require nontrival logic to be written somewhere.

And, for some reason FIREFOX connections immediately die with "cannot look up DNS" when attempting to connect; once unbound has finished finding the answer, you can refresh and it works.
Brave/Chrome work normally. I suspect this means that, after adding this new IP and name, the DoH pipeline returns immediately with no answer if unbound doesn't have an answer, rather than waiting.
I have not been able to find any logs to shed light on the situation.

For now, my DoH setup is still not really viable... I still need a feasible, reliable way to identify requesting clients.

Pi-hole's embedded DNS server pihole-FTL is a tailored fork of dnsmasq. Unless upstream dnsmasq would add support for DoH, it is unlikely that this would be added to Pi-hole.

That's wrong.
EDNS0 Client Subnet (ECS) can convey information about a DNS request's originating network, up to the full IP address.

If your DoH proxy software would support sending ECS information, you'd only have to enable ECS parsing in Pi-hole, by setting EDNS0_ECS=true in your pihole-FTL.conf.

It's not entirely clear to me what doh-proxy you'd be using, but a precursory search suggests that there are DoH proxies that would support EDNS0, e.g. GitHub - tinkernels/doh-proxy: A DNS-protocol proxy for DNS-over-HTTPS providers, such as Google and Cloudflare

-edns-subnet string
Specify a subnet to be sent in the edns0-client-subnet option;
take your own risk of privacy to use this option;
no: will not use edns_subnet;
auto: will use your current external IP address;
net/mask: will use specified subnet, e.g. 66.66.66.66/24.
(default "auto")

I'm using DNSCrypt/doh-server as described in HMN76V's post; edited my original post to include that link.

Thank you for the pointer to EDNS0_ECS=true!

I set my wacky IP/host config back to normal so my DoH queries were showing up as vanilla "localhost" again on 127.0.0.1, set that config in my pihole-FTL.conf, restarted pihole, and... doesn't seem to work, so perhaps the DNSCrypt/doh-server doesn't support that (though it looks like it should...?). I'll try out tinkernels/doh-proxy with the EDNS config and see if that works!

A post was split to a new topic: Configure dnsdist as DoH proxy in front of Pi-hole, and serve Pi-hole v5 UI via HTTPS

Thank you for coming back and sharing that here. :slight_smile:

Your latest reply has moved away from a general question about identifying clients when using Pi-hole behind a DoH/DoT proxy to a guide configuring a specific software package to use ECS.

I also note that you've included instructions for making Pi-hole's UI available via HTTPS, which is a different task altogether and also
a. wasn't part of your original question
b. is already covered by a few existing topics, e.g. Enabling HTTPS for your Pi-hole Web Interface
c. will be supported out of the box by upcoming Pi-hole v6

I'm therefore going to split your last post into a separate topic in the Community How-Tos category.
Feel free to edit that topic's title if my choice would need some improvement. :wink:

At the time of asking my original question, I didn't realize that I needed both

  1. to configure pihole to consider EDNS Client Subnet, and - specifically in the case of DNS-over-HTTPS,
  2. a DoH gateway that could attach Client Subnet information.

You can see me realizing this in my follow-up posts.

I believe the below part of my eventual solution answers my actual problem even though my very first post hadn't fully crystallized it yet. I think this answer belongs with my question:


What Worked for Me

Short answer is that dnsdist can attach a mask of the client IP as a client subnet, which pihole will pick up when EDNS0_ECS=true in the pi-hole config to allow DNS-over-HTTPS queries into pihole to be correctly associated with the local client that made the query.

Assuming

  1. pihole IP of 192.168.1.254
  2. /usr/local/ssl/crt contains an SSL cert & key for the pihole,

Then the below config will work for dnsdist to expose a DNS-over-HTTPS server on the default SSL port that forwards requests to the pihole with the full client IP attached, allowing identification of the clients as I originally wanted.

-- listen for DNS-over-HTTPs queries on 443, authenticated by our pihole cert
addDOHLocal(
		'192.168.1.254:443',
		'/usr/local/ssl/crt/pi.hole.crt',
		'/usr/local/ssl/crt/pi.hole.key'
)

-- prepare to forward DNS queries to pihole w/ client subnet info
pihole = newServer({
	address="192.168.1.254:53",
	name="pihole",
	useClientSubnet=true
})

pihole:setUp()

-- send full client IP as subnet
setECSSourcePrefixV4(32)

NOTE: do not use 127.0.0.1 for the newServer; while this will work to resolve DNS queries, it will cause the pihole to respond with that localhost IP for pi.hole lookups and you won't be able to browse the web admin at pi.hole!

NOTE: pihole:setUp() disables dnsdist's health-check queries to pihole. In this setup there is no alternative to pihole, and it's not dnsdist's job to solve pihole being down - queries should always go to pihole and if pihole's down they just won't resolve!

NOTE: Probably several DoH servers would work in place of dnsdist when exposed directly on a port. However, my larger task that led to this question was to do this on the same default HTTPS port (443), while also serving the pihole web admin on the same port, which meant something in-between the client and the DoH server, and the need to preserve and/or attach new client subnet information so that dnsdist had something to forward to pihole! My full solution to that is documented in a different post, "Run a DNS-over-HTTPS (DoH) server & pihole’s web admin on the same (default) SSL port while still identifying DoH clients".

Your solution inspired me to investigate DoH client identification once again. I found this: cuddlyclara/SimpleDoHServer which provides functionality that the project I was previously using (doh-proxy 0.0.9) does not, though it was supposed to support an --ecs option. With this new find and my already reconfigured lighttpd for secure connections, I am finally able to get DoH with client identification in pi-hole.

1 Like

As I see it, your original question "How to identify clients when using DNS-over-HTTPS (thru doh-proxy)" is answered by:

That answer is universally applicable to any DoH proxy software.

The extra information you have provided is answering a specific follow up question, i.e. "how do I configure my xyz proxy doh software to aenable ECS?", where xyz is a personal choice. You even switched your DoH software, as your original choice may not have supported ECS.
And that prompted me to elevate your specific instructions to a separate Community-Howto guide for using dnsdist as a DoH proxy in front of Pi-hole.
If you want to maintain your instructions in two places (this topic as well as the Community-Howto), that's your choice. :wink:

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