Skip to content

Add rate-limit exempt client IPs#2856

Open
simpliq-marvin wants to merge 1 commit intopi-hole:developmentfrom
simpliq-marvin:feat/rate-limit-exempt-ips
Open

Add rate-limit exempt client IPs#2856
simpliq-marvin wants to merge 1 commit intopi-hole:developmentfrom
simpliq-marvin:feat/rate-limit-exempt-ips

Conversation

@simpliq-marvin
Copy link
Copy Markdown

@simpliq-marvin simpliq-marvin commented Apr 21, 2026

This adds a small escape hatch for setups where a trusted client can legitimately generate short DNS bursts and ends up tripping Pi-hole's per-client rate limiting.

A practical example is a work laptop coming back after a restart, VPN reconnect, or similar network reset. In that state it can generate a short burst of DNS traffic that is genuine, but still large enough to hit the global per-client limit. For users who want to keep rate limiting enabled overall, this adds a narrow opt-in way to exempt specific trusted clients.

The change adds a new dns.rateLimit.exemptIPs setting. When a client IP matches one of the configured addresses exactly, that client bypasses the per-client rate-limit check. Other clients still use the existing rate-limit behaviour unchanged.

What is included here:

  • config schema/help text for dns.rateLimit.exemptIPs
  • validation for arrays of IPv4 and IPv6 addresses
  • rate-limit bypass for exact client IP matches
  • test config coverage for the new key
  • API spec/example coverage for the new key

Validation:

  • tested on a Raspberry Pi after deployment
  • Pi-hole remained healthy and no rate-limit recurrences were observed
  • logs showed no new rate-limit or config-related errors after deployment
  • during a restart-associated DNS spike, with the global rate limit set to 1000 queries / 60s, an exempt client reached 1663 and 1853 queries in FTL-aligned 60s buckets, with a rolling 60s peak of 2140, without being rate-limited

Related user report:

This is intentionally narrow. Matching is exact by IP address, and only applies when rate limiting is enabled.

@simpliq-marvin simpliq-marvin requested a review from a team as a code owner April 21, 2026 11:13
@simpliq-marvin simpliq-marvin force-pushed the feat/rate-limit-exempt-ips branch 2 times, most recently from 80431d9 to 756667e Compare April 21, 2026 11:33
@yubiuser
Copy link
Copy Markdown
Member

Please base on and target development branch.

Why did you include

legacy config reader support for RATE_LIMIT_EXEMPT_IPS

This would be a new option, there is no legacy to handle here.

@simpliq-marvin simpliq-marvin changed the base branch from master to development April 21, 2026 12:26
@simpliq-marvin simpliq-marvin force-pushed the feat/rate-limit-exempt-ips branch from 756667e to 4b5fd42 Compare April 21, 2026 12:26
@simpliq-marvin
Copy link
Copy Markdown
Author

Thanks for calling that out, you’re right on both points.

I opened this against the wrong base branch.

On the config side, I was adding legacy syntax for the new option, but of course that syntax never existed. My bad. I’ve removed that, rebased onto development, and updated the PR description to match the actual scope. I’ve also added the corresponding test config and API spec entries for the new key.

Copy link
Copy Markdown
Member

@DL6ER DL6ER left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are adding this into the hottest path Pi-hole has and my preliminary measurements show that this affects performance notably. Instead of iterating over the array of JSON strings, converting them twice to IPv4 and IPv6 (once for the client IP and then for every IP in the array) and doing memory comparisons, this should rather live in a dedicated function in a suitable thread outside the hot path. This can then run periodically and simply set some bool inside the struct clientsData so that it can be checked in the hot-path without any performance impact.

@simpliq-marvin
Copy link
Copy Markdown
Author

Thanks, good catch. I agree the current version is doing too much work in the query hot path.

I’m reworking this so exemption matching is resolved when client/config state is created or refreshed, and query handling only reads cached per-client state. I’ll push the revision once I’ve validated the build/tests locally.

@simpliq-marvin simpliq-marvin force-pushed the feat/rate-limit-exempt-ips branch from 4b5fd42 to 84cf6fe Compare April 21, 2026 20:38
Signed-off-by: simpliq-marvin <[email protected]>
@simpliq-marvin simpliq-marvin force-pushed the feat/rate-limit-exempt-ips branch from 84cf6fe to 0451d8e Compare April 21, 2026 21:11
@simpliq-marvin
Copy link
Copy Markdown
Author

I’ve pushed the rework. The exemption check is now cached per-client state instead of being recomputed in _FTL_new_query(), so the hot path just reads the cached flag.

I’ve also tested it on a Raspberry Pi build from this PR revision. A laptop reboot produced a burst well above the configured 1000 / 60s threshold, and the exempt client was not rate-limited (RATE_LIMIT diagnostics: none). In the same setup, non-exempt clients still followed the normal rate-limit path.

Config reload/replacement also refreshes the cached exemption state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants