From 1bd05d7cd7f13ef346136dfa42d9976b851ff572 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Thu, 21 Aug 2025 14:26:09 +0200 Subject: [PATCH] fallback: add YAML config + docs --- doc/user/config-fallback.rst | 23 +++++++++++++++++++ doc/user/config-performance.rst | 1 + .../knot_resolver/datamodel/config_schema.py | 5 +++- .../knot_resolver/datamodel/forward_schema.py | 17 ++++++++++++++ .../datamodel/templates/forward.lua.j2 | 11 ++++++++- 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 doc/user/config-fallback.rst diff --git a/doc/user/config-fallback.rst b/doc/user/config-fallback.rst new file mode 100644 index 000000000..7a45abb90 --- /dev/null +++ b/doc/user/config-fallback.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _config-serve-stale: + +Fallback on resolution failure +============================== + +This allows switching to a fallback forwarding configuration on queries where the resolver is unable to contact upstream servers. + +.. code-block:: yaml + + fallback: + enable: true + servers: + - address: [ 2001:148f:fffe::1, 193.17.47.1 ] + transport: tls + hostname: odvr.nic.cz + +The ``servers:`` has the same schema as in :ref:`forwarding `. + +If you use fallback within a fleet of servers, +you will probably want to avoid queries cycling in there, +i.e. disable the fallback option for them in :ref:`views `. diff --git a/doc/user/config-performance.rst b/doc/user/config-performance.rst index 1d3604d8c..481d357d3 100644 --- a/doc/user/config-performance.rst +++ b/doc/user/config-performance.rst @@ -29,6 +29,7 @@ impact than cache settings and number of workers. config-cache-predict config-cache-prefill config-serve-stale + config-fallback config-rfc7706 config-priming config-edns-keepalive diff --git a/python/knot_resolver/datamodel/config_schema.py b/python/knot_resolver/datamodel/config_schema.py index 2cc2680c9..54155001b 100644 --- a/python/knot_resolver/datamodel/config_schema.py +++ b/python/knot_resolver/datamodel/config_schema.py @@ -8,7 +8,7 @@ from knot_resolver.datamodel.cache_schema import CacheSchema from knot_resolver.datamodel.defer_schema import DeferSchema from knot_resolver.datamodel.dns64_schema import Dns64Schema from knot_resolver.datamodel.dnssec_schema import DnssecSchema -from knot_resolver.datamodel.forward_schema import ForwardSchema +from knot_resolver.datamodel.forward_schema import ForwardSchema, FallbackSchema from knot_resolver.datamodel.globals import Context, get_global_validation_context, set_global_validation_context from knot_resolver.datamodel.local_data_schema import LocalDataSchema, RPZSchema, RuleSchema from knot_resolver.datamodel.logging_schema import LoggingSchema @@ -102,6 +102,7 @@ class KresConfig(ConfigSchema): views: List of views and its configuration. local_data: Local data for forward records (A/AAAA) and reverse records (PTR). forward: List of Forward Zones and its configuration. + fallback: Config for fallback on resolution failure. cache: DNS resolver cache configuration. dnssec: Disable DNSSEC, enable with defaults or set new configuration. dns64: Disable DNS64 (RFC 6147), enable with defaults or set new configuration. @@ -125,6 +126,7 @@ class KresConfig(ConfigSchema): views: Optional[List[ViewSchema]] = None local_data: LocalDataSchema = LocalDataSchema() forward: Optional[List[ForwardSchema]] = None + fallback: FallbackSchema = FallbackSchema() cache: CacheSchema = lazy_default(CacheSchema, {}) dnssec: Union[bool, DnssecSchema] = True dns64: Union[bool, Dns64Schema] = False @@ -148,6 +150,7 @@ class KresConfig(ConfigSchema): views: Optional[List[ViewSchema]] local_data: LocalDataSchema forward: Optional[List[ForwardSchema]] + fallback: FallbackSchema cache: CacheSchema dnssec: Union[Literal[False], DnssecSchema] dns64: Union[Literal[False], Dns64Schema] diff --git a/python/knot_resolver/datamodel/forward_schema.py b/python/knot_resolver/datamodel/forward_schema.py index 6b693e6be..79064fe8c 100644 --- a/python/knot_resolver/datamodel/forward_schema.py +++ b/python/knot_resolver/datamodel/forward_schema.py @@ -74,3 +74,20 @@ class ForwardSchema(ConfigSchema): if self.options.authoritative and is_transport_tls(self.servers): raise ValueError("Forwarding to authoritative servers using TLS protocol is not supported.") + + +class FallbackSchema(ConfigSchema): + """ + Configuration for fallback after resolution failure. + + --- + enable: Enable/disable the fallback. + servers: Forward servers configuration for fallback. + """ + + enable: bool = False + servers: Union[List[IPAddressOptionalPort], List[ForwardServerSchema], None] = None + + def _validate(self) -> None: + if self.enable and self.servers is None: + raise ValueError("Fallback enabled without configuring servers.") diff --git a/python/knot_resolver/datamodel/templates/forward.lua.j2 b/python/knot_resolver/datamodel/templates/forward.lua.j2 index 24311da18..bc4d73ef6 100644 --- a/python/knot_resolver/datamodel/templates/forward.lua.j2 +++ b/python/knot_resolver/datamodel/templates/forward.lua.j2 @@ -1,4 +1,4 @@ -{% from 'macros/forward_macros.lua.j2' import policy_rule_forward_add %} +{% from 'macros/forward_macros.lua.j2' import policy_rule_forward_add, forward_servers %} {% if cfg.forward %} {% for fwd in cfg.forward %} @@ -7,3 +7,12 @@ {% endfor %} {% endfor %} {% endif %} + + +{% if cfg.fallback and cfg.fallback.enable %} +modules.load('fallback') +fallback.config({ + targets = {{ forward_servers(cfg.fallback.servers) }}, + options = {}, +}) +{% endif %} -- 2.47.2