Support PROXY Protocol
When using AGH through a load balancer or reverse proxy (probably because I don't want to expose the AGH directly or just want to high availability), DoH can get the user's original IP address through the HTTP header but regular DNS and DoT cannot.
This isn't good for use cases where the AGH is deployed on a server. This is especially important for sites that use GeoDNS for CDN allocation because AGH needs the user's original IP to send the ECS to the recursive DNS server.
This situation is also mentioned in this issue (#1789), where @ameshkov says, "Unfortunately, there are no such options for TLS/TCP."
But in fact, both Nginx and HAProxy can use PROXY Protocol to send the user's real IP over Layer 4. nginx.com has written an article describing how to use Nginx as a DoH / DoT gateway. We just need to add proxy_protocol on; in the stream section to send the user's original IP via PROXY Protocol, but this requires AGH support.
I am also interested in this feature. Right now, I am using Kubernetes with Traefik and MetalLB to expose ports on my nodes. All my services are behind Traefik except AGH because of the missing distinction of the clients in AGH. I seeh @ameshkov has remarked it to be in the mileston v1.0.0 would love to see it earlier. ;-) Regards!
Same Here.
@Akruidenberg don't forget to thumbs up first post.
@dvonessen @Akruidenberg
Although AdGuard Home does not support this feature at this time, the same effect can be achieved with go-mmproxy.
https://github.com/path-network/go-mmproxy
The go-mmproxy gives AdGuard Home the ability to obtain the original IP of the client sent by the load balancer via PROXY Protocol.
Oh, there's a ready-to-use golang code for that? That's nice, makes it easier to implement inside AGH when we finally are ready to.
Meanwhile, please use go-mmproxy for that.
Oh, there's a ready-to-use golang code for that? That's nice, makes it easier to implement inside AGH when we finally are ready to.
Meanwhile, please use go-mmproxy for that.
Sadly, there is no docker support for mmproxy.
@Akruidenberg It seems to be possible to use mmproxy on Docker / k8s.
https://andrewmichaelsmith.com/2020/02/preserving-client-ip-in-kubernetes/ https://hub.docker.com/r/unixfox/go-mmproxy
https://github.com/path-network/go-mmproxy
i try it (https://hub.docker.com/r/unixfox/go-mmproxy) but do not support ipv6 my nginx config:
map $ssl_preread_server_name $dot_map {
alist.example.com dot; }
upstream dot{
server 192.168.1.2:25577;
}
server { listen 853 ; listen [::]:853 ; proxy_pass $dot_map; ssl_preread on;
proxy_protocol on;
}
my docker go-mmproxy config :
#!/bin/sh sleep 5 ip rule add from 127.0.0.1/8 iif lo table 123 ip route add local 0.0.0.0/0 dev lo table 123
ip -6 rule add from ::1/128 iif lo table 123 ip -6 route add local ::/0 dev lo table 123
echo -en "0.0.0.0/0\n::/0\n" > allowed-networks.txt /usr/bin/go-mmproxy -l 0.0.0.0:25577 -4 127.0.0.1:853 -6 [::1]:853 --allowed-subnets allowed-networks.txt -v 2 /bin/sh
my adguard home listen 853 for dot i checked adguard home it worked for ipv4 only, only have client real ip for ipv4 adguard home version v0.108.0-b.29 go-mmproxy log:

Bump. I need this feature for Nginx in order to be able to securely and correctly proxy DoT and DoUDP / DoTCP.
I'm quite unsure why this has been put on the back burner, it's quite an important feature to add.
It would be really nice if AdGuard Home worked on adding support for The PROXY protocol, by doing so it would allow us to identify DoT clients while using a reverse proxy.
I'm currently running AdGuard Home together with Traefik in Docker using Docker Compose. Traefik is really convenient for routing to multiple Docker containers using labels, and for managing multiple certificates without human input.
docker-compose.yaml
version: "3"
services:
traefik:
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --certificatesresolvers.myresolver.acme.dnschallenge=true
- --certificatesresolvers.myresolver.acme.dnschallenge.provider=duckdns
- --certificatesresolvers.myresolver.acme.email=webmaster@👀.duckdns.org
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
- --entrypoints.websecure.address=:443
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.dot.address=:853
container_name: traefik
depends_on:
- adguardhome
environment:
- DUCKDNS_TOKEN=👀
image: traefik:v3.0
ports:
- 80:80
- 443:443
- 853:853
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
adguardhome:
container_name: adguardhome
image: adguard/adguardhome
labels:
- traefik.enable=true
- traefik.http.routers.adguardhome-https.rule=Host(`adguardhome.👀.duckdns.org`)
- traefik.http.routers.adguardhome-https.entrypoints=websecure
- traefik.http.routers.adguardhome-https.tls.certresolver=myresolver
- traefik.http.services.adguardhome-https.loadbalancer.server.port=3000
- traefik.http.routers.adguardhome-https.service=adguardhome-https
- traefik.tcp.routers.adguardhome_dot.rule=HostSNI(`adguardhome.👀.duckdns.org`) || HostSNIRegexp(`^.+\.adguardhome\.👀\.duckdns\.org$`)
- traefik.tcp.routers.adguardhome_dot.entrypoints=dot
- traefik.tcp.routers.adguardhome_dot.tls.domains[0].main=adguardhome.👀.duckdns.org
- traefik.tcp.routers.adguardhome_dot.tls.domains[0].sans=*.adguardhome.👀.duckdns.org
- traefik.tcp.routers.adguardhome_dot.tls.certresolver=myresolver
# - traefik.tcp.services.adguardhome_dot.loadbalancer.proxyprotocol.version=2
# - traefik.tcp.routers.adguardhome_dot.service=adguardhome_dot
restart: unless-stopped
volumes:
- ./adguardhome/work:/opt/adguardhome/work
- ./adguardhome/conf:/opt/adguardhome/conf
AdGuardHome.yaml
dns:
trusted_proxies:
- 127.0.0.0/8
- ::1/128
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
tls:
enabled: true
server_name: adguardhome.👀.duckdns.org
allow_unencrypted_doh: true
I would also find PROXY protocol support extremly helpful! Would really like to the that feature.
I just wasted 3 nights to do passing real-IP through DoT and found out that AG does not support it.
I think this is an important feature need to implement since DoT is used in Android device a lot.
Bumping this thread since as of 2024, this is still unsupported and i truly believe that we should not resort to unsafe workarounds with go-mmproxy
I would also appreciate support for that. I've moved from static blocklist zones to AdGuard recently, so I could change them dynamically should I need to – but since my AdGuard is behind CoreDNS, I am unable to use per-client filtering.
Also interested in seeing proxy protocol supported.
Just adding another "I'd love to see this in AGH" vote.
I'd also love AGH to support PROXY protocol for DoT client identification behind Traefik
Also look forward to PROXY protocol support...
I got bored enough some time ago to start hacking on CoreDNS and AdGuard to see if I can pass the client IP between them. And it seems to work... kind of. As far as I can tell the issue is that the PROXY frame is only ever sent when the connection is initially made, which is all well and good as long as you only ever send single client's queries through the connection. CoreDNS however, caches connections to reduce latency, so you will end up with all the queries sent through this connection having the source IP of the client it was initially opened for — which doesn't help us much, if what we want from this is correct client IPs.
I am not a network engineer, so I might be wrong, but it seems like the PROXY Protocol might be the wrong tool for this use case (they even explicitly call out multiplexing different clients onto a single connection as something that the protocol is not designed for in the RFC)? Some time later I've noticed that you can attach arbitrary data to a DNS packet with EDNS, so maybe that could be an option? I'll try to play around with that later, if I find some time (no hard commitments though, I suck at that).
@jaen the PROXY Protocol is designed for TCP/UDP load balancers. In your use case, what you have implemented is actually a DNS proxy, and for DNS proxies, application-layer implementations (EDNS Client Subnet) should be preferred.
One more vote from me - I run two AdGuard Home servers behind a Hetzner LB (tcp forwarding of 443 and 853) and the only thing that's missing is support by AdGuard for the proxy protocol so I can see real client IPs in the logs.
+4 years of people requesting this and it has yet to happen lol
+4 years of people requesting this and it has yet to happen lol
Who says it's gonna happen? 🤣 I've lost all hope 🤣
what are you using as a server/lb ?
@bcookatpcsd yes Technitium appears to support the proxy protocol, however when I tried it I couldn't get it to work. Their implementation is very weird, requiring a different port for the proxy protocol, I didn't understand why or how to set it up.
The solution should be simple: if the DNS forwarder (AGH in our case) supports the proxy protocol, that should be transparent. I set my load balancer to forward tcp packets from 853 to 853, and enable the proxy protocol on it, AGH should support that for that stream, without having to add any more ports (which I don't see how exactly would be done anyway as I want the client's IP reported for DoT connections on 853, not on some other random port).
dnsdist:
newServer({ address="[::1]:538", useProxyProtocol=true, tcpFastOpen=true, healthCheckMode='lazy', name="pxy-Technitium", checkName="accounts.google.com", sockets="4", pool="pc-proxyv2" })
newServer({ address="[::1]:539", useProxyProtocol=true, tcpFastOpen=true, healthCheckMode='lazy', name="pxy-unbound", checkName="accounts.google.com", sockets="4", pool="pc-guest" })
unbound:
server:
#@ interface: 10.192.144.253@53
#@ port: 53
interface: ::1@539
proxy-protocol-port: 539
outgoing-interface: 10.192.144.253
You need different ports because 'dns53 protocol', 'doh protocol', 'dot protocol', and 'proxy protocol' are all different..
Of course you could run all of it on 'port 53' but then you would have to bind a lot of different IPs together..
This is dnsdist on the outside (dns53 and doh), sending to one server via proxy protocol for one range of IPs, and sending to another server via proxy protocol for a different range of IPs..
YMMV
my 0.02
Please, this would make my life so much easier.
.. more examples here
https://github.com/TechnitiumSoftware/DnsServer/discussions/1099
HTH
waiting for this... and why it can't just read the X-Forwarded-For header passed from the nginx reverse proxy DoH?