From: Aleš Mrázek Date: Tue, 15 Apr 2025 14:16:50 +0000 (+0200) Subject: datamodel: stabilize rate-limiting schema X-Git-Tag: v6.0.17~9^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c7399b28773d92e2ff375b94203b9726a156de4;p=thirdparty%2Fknot-resolver.git datamodel: stabilize rate-limiting schema --- diff --git a/NEWS b/NEWS index d1bb25c62..dd74d4210 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,7 @@ Incompatible changes - /dnssec/trust-anchor-signal-query -> /dnssec/signal-query - /logging/dnssec-bogus -> /dnssec/log-bogus - /network/tls/files-watchdog -> /network/tls/watchdog + - /rate-limiting -> /rate-limiting/enabled Knot Resolver 6.0.16 (2025-10-30) diff --git a/doc/_static/config.schema.json b/doc/_static/config.schema.json index bad48d6c0..55561d1db 100644 --- a/doc/_static/config.schema.json +++ b/doc/_static/config.schema.json @@ -1687,21 +1687,21 @@ }, "rate-limiting": { "description": "Configuration of rate limiting.", - "type": [ - "object", - "null" - ], + "type": "object", "properties": { - "capacity": { - "type": "integer", - "minimum": 1, - "description": "Expected maximal number of blocked networks/hosts at the same time.", - "default": 524288 + "enabled": { + "type": "boolean", + "description": "Enable/disable rate limiting", + "default": false }, "rate-limit": { - "type": "integer", + "type": [ + "integer", + "null" + ], "minimum": 1, - "description": "Maximal number of allowed queries per second from a single host." + "description": "Maximal number of allowed queries per second from a single host.", + "default": null }, "instant-limit": { "type": "integer", @@ -1709,6 +1709,12 @@ "description": "Maximal number of allowed queries at a single point in time from a single host.", "default": 50 }, + "capacity": { + "type": "integer", + "minimum": 1, + "description": "Expected maximal number of blocked networks/hosts at the same time.", + "default": 524288 + }, "slip": { "type": "integer", "minimum": 0, @@ -1728,7 +1734,15 @@ "default": false } }, - "default": null + "default": { + "enabled": false, + "rate_limit": null, + "instant_limit": 50, + "capacity": 524288, + "slip": 2, + "log_period": "0s", + "dry_run": false + } }, "prioritization": { "description": "Configuration of request prioritization (defer).", diff --git a/doc/user/config-rate-limiting.rst b/doc/user/config-rate-limiting.rst index 41984e40a..a0798d2d7 100644 --- a/doc/user/config-rate-limiting.rst +++ b/doc/user/config-rate-limiting.rst @@ -21,6 +21,15 @@ that's common in other DNS servers. The limits can be adjusted for different packet origins using :option:`price-factor ` in :ref:`views `. +.. option:: rate-limiting/enabled: true|false + + :default: false + + Enable rate limiting. + + To start using rate limiting, you need to set at least the :option: `rate-limit >` which has no default value. + + .. option:: rate-limiting/rate-limit: Maximal allowed number of UDP queries per second from a single IPv6 or IPv4 address. diff --git a/python/knot_resolver/datamodel/config_schema.py b/python/knot_resolver/datamodel/config_schema.py index 8eebf8b17..3ff945a9f 100644 --- a/python/knot_resolver/datamodel/config_schema.py +++ b/python/knot_resolver/datamodel/config_schema.py @@ -127,7 +127,7 @@ class KresConfig(ConfigSchema): dns64: Dns64Schema = Dns64Schema() logging: LoggingSchema = LoggingSchema() monitoring: MonitoringSchema = MonitoringSchema() - rate_limiting: Optional[RateLimitingSchema] = None + rate_limiting: RateLimitingSchema = RateLimitingSchema() prioritization: PrioritizationSchema = PrioritizationSchema() lua: LuaSchema = LuaSchema() @@ -149,7 +149,7 @@ class KresConfig(ConfigSchema): dns64: Dns64Schema logging: LoggingSchema monitoring: MonitoringSchema - rate_limiting: Optional[RateLimitingSchema] + rate_limiting: RateLimitingSchema prioritization: PrioritizationSchema lua: LuaSchema diff --git a/python/knot_resolver/datamodel/rate_limiting_schema.py b/python/knot_resolver/datamodel/rate_limiting_schema.py index 01771713c..3a8116dab 100644 --- a/python/knot_resolver/datamodel/rate_limiting_schema.py +++ b/python/knot_resolver/datamodel/rate_limiting_schema.py @@ -1,3 +1,5 @@ +from typing import Optional + from knot_resolver.datamodel.types import ( Int0_32, IntPositive, @@ -11,24 +13,29 @@ class RateLimitingSchema(ConfigSchema): Configuration of rate limiting. --- - capacity: Expected maximal number of blocked networks/hosts at the same time. + enabled: Enable/disable rate limiting rate_limit: Maximal number of allowed queries per second from a single host. instant_limit: Maximal number of allowed queries at a single point in time from a single host. + capacity: Expected maximal number of blocked networks/hosts at the same time. slip: Number of restricted responses out of which one is sent as truncated, the others are dropped. log_period: Minimal time between two log messages, or '0s' to disable. dry_run: Perform only classification and logging but no restrictions. """ - capacity: IntPositive = IntPositive(524288) - rate_limit: IntPositive + enabled: bool = False + rate_limit: Optional[IntPositive] = None instant_limit: IntPositive = IntPositive(50) + capacity: IntPositive = IntPositive(524288) slip: Int0_32 = Int0_32(2) log_period: TimeUnit = TimeUnit("0s") dry_run: bool = False def _validate(self) -> None: + if self.enabled and not self.rate_limit: + raise ValueError("'rate-limit' has to be configured to enable rate limiting") + max_instant_limit = int(2**32 // 768 - 1) if not int(self.instant_limit) <= max_instant_limit: raise ValueError(f"'instant-limit' has to be in range 1..{max_instant_limit}") - if not int(self.rate_limit) <= 1000 * int(self.instant_limit): + if self.rate_limit and not int(self.rate_limit) <= 1000 * int(self.instant_limit): raise ValueError("'rate-limit' has to be in range 1..(1000 * instant-limit)") diff --git a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 index 1f2a546d0..0b6bd8142 100644 --- a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 +++ b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 @@ -1,12 +1,15 @@ {% from 'macros/common_macros.lua.j2' import boolean %} -{% if cfg.rate_limiting.rate_limit is defined and cfg.rate_limiting.rate_limit -%} -assert(C.ratelimiting_init( - '{{ cfg.rundir }}/ratelimiting', - {{ cfg.rate_limiting.capacity }}, - {{ cfg.rate_limiting.instant_limit }}, - {{ cfg.rate_limiting.rate_limit }}, - {{ cfg.rate_limiting.slip }}, - {{ cfg.rate_limiting.log_period.millis() }}, - {{ boolean(cfg.rate_limiting.dry_run) }}) == 0) -{%- endif %} +{% if cfg.rate_limiting.enabled %} +assert( + C.ratelimiting_init( + '{{ cfg.rundir }}/ratelimiting', + {{ cfg.rate_limiting.capacity }}, + {{ cfg.rate_limiting.instant_limit }}, + {{ cfg.rate_limiting.rate_limit }}, + {{ cfg.rate_limiting.slip }}, + {{ cfg.rate_limiting.log_period.millis() }}, + {{ boolean(cfg.rate_limiting.dry_run) }} + ) == 0 +) +{% endif %}