Protection against PTR Requests (Private reverse DNS resolving) flood/loop
Prerequisites
-
[X] I have checked the Wiki and Discussions and found no answer
-
[X] I have searched other issues and found no duplicates
-
[X] I want to request a feature or enhancement and not ask a question
The problem
When AGH instance points to Private reverse DNS resolver and the said resolver is using AGH as upstream (e.g. main router, or some coredns, docker resolver or other service) it often happens that the private client IP is not reverse resolvable (e.g. after client changes IP, the router no longer has information about that IP, or docker container changes IP and adguard home is restarted at that time or else) it happens that the Private reverse DNS resolver forwards the unresolvable PTR request from AGH back to AGH, which creates infinite loop and thus essentially DDOS-like attack internally.
I've replicated this successfully several times, my exact setup is as follows: AGH -> coredns_omada (omada clients IPs) -> docker 127.0.0.11 (docker containers IPs) -> AGH
But it should be easily reproducible on similar setup: AGH -> router -> AGH
Then simply do nslookup <nonexistent private IP> <IP of AGH> to create loop and flood of:
2024/01/31 21:40:02.567621 [error] dnsproxy: upstream 127.0.0.1:5053 failed to exchange ;22.0.20.172.in-addr.arpa. IN PTR in 2.004519601s: exchanging with 127.0.0.1:5053 over udp: read udp 127.0.0.1:45093->127.0.0.1:5053: i/o timeout
When client IP is resolvable, there is no problem, both internal network as well as docker containers client IPs are successfully resolved.
Adding *20.172.in-addr.arpa to disallowed domains resolves this loop problem of course.
Proposed solution
Not really sure how to tackle this, open to ideas. Of course the very ideal scenario would be that the Private DNS resolver uses different upstream than AGH, but thats sometimes not possible, e.g. when you set AGH as upstream in some routers, because you want your whole network using AGH.
There is already a best-effort protection against the case where AGH is sending PTRs to itself directly. But the thing is, there will always be ways of building a network configuration resulting in a recursion. I don't really see any solution that doesn't increase the complexity of code and/or configuration for users, but if anyone has any ideas, feel free to share.
@ainar-g Would it make sense in this fashion ?
When AGH sends a requests for example 1.1.20.172.in-addr.arpa, until it is resolved (or not resolved), it also disallows further requests towards itself (disallowed domains feature) temporarily ? This would prevent this situation I guess, so in case it is not found on the chain of private reverse dns servers, the last in chain would not be allowed to request AGH again to resolve it. So basically something like disallowing outstanding private reverse dns PRT queries
An interesting proposal, but there are a few issues. Dropping queries like that could make AGH unusable in cases where devices in a network send the same PTR requests actively, for example when the TTL, which is common for all records, expires in the devices. That would also require some sort of global locking for preventing data races, and we strive to avoid those unless completely necessary. Also, ideally that blocking would need to be per-upstream, since some of them may lead back to AGH but not others, further complicating things. And testing this would be a nightmare, heh.