From: Vladimír Čunát Date: Thu, 21 Aug 2025 12:26:09 +0000 (+0200) Subject: fallback: add YAML config + docs X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c145e98c2e3a20378ec5dad6ac7d1d1a96bc8183;p=thirdparty%2Fknot-resolver.git fallback: add YAML config + docs --- diff --git a/NEWS b/NEWS index 4b0fa072b..429a5413b 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ Improvements - reduce validation strictness for domain names (#934, !1727) - manager: force a configuration reload via management HTTP API 'api/reload/force' (#939, !1748) - kresctl: reload: added '--force' flag +- /fallback: add this feature/module (!1733) Bugfixes -------- diff --git a/doc/_static/config.schema.json b/doc/_static/config.schema.json index 88e11901d..171ce40b1 100644 --- a/doc/_static/config.schema.json +++ b/doc/_static/config.schema.json @@ -650,12 +650,18 @@ "minimum": 0.0, "description": "Multiplies rate-limiting and defer prices of operations, use 0 to whitelist.", "default": 1.0 + }, + "fallback": { + "type": "boolean", + "description": "Enable/disable fallback on resolution failure.", + "default": true } }, "default": { "minimize": true, "dns64": true, - "price_factor": 1.0 + "price_factor": 1.0, + "fallback": true } } } @@ -1014,16 +1020,13 @@ "description": "Subtree(s) to forward." }, "servers": { - "anyOf": [ - { - "type": "array", - "items": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" - } - }, - { - "type": "array", - "items": { + }, + { "description": "Forward server configuration.", "type": "object", "properties": { @@ -1095,8 +1098,8 @@ } } } - } - ], + ] + }, "description": "Forward servers configuration." }, "options": { @@ -1124,6 +1127,108 @@ "description": "List of Forward Zones and its configuration.", "default": null }, + "fallback": { + "description": "Config for fallback on resolution failure.", + "type": "object", + "properties": { + "enable": { + "type": "boolean", + "description": "Enable/disable the fallback.", + "default": false + }, + "servers": { + "type": [ + "array", + "null" + ], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "description": "Forward server configuration.", + "type": "object", + "properties": { + "address": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ], + "description": "IP address(es) of a forward server." + }, + "transport": { + "type": [ + "string", + "null" + ], + "enum": [ + "tls" + ], + "description": "Transport protocol for a forward server.", + "default": null + }, + "pin-sha256": { + "anyOf": [ + { + "type": "null" + }, + { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Za-z\\d+/]{43}=$" + } + }, + { + "type": "string", + "pattern": "^[A-Za-z\\d+/]{43}=$" + } + ] + } + ], + "description": "Hash of accepted CA certificate.", + "default": null + }, + "hostname": { + "type": [ + "string", + "null" + ], + "pattern": "(?=^.{,253}\\.?$)(^(?!-)[^.]{,62}[^.-](\\.(?!-)[^.]{,62}[^.-])*\\.?$)|^\\.$", + "description": "Hostname of the Forward server.", + "default": null + }, + "ca-file": { + "type": [ + "string", + "null" + ], + "description": "Path to CA certificate file.", + "default": null + } + } + } + ] + }, + "description": "Forward servers configuration for fallback.", + "default": null + } + }, + "default": { + "enable": false, + "servers": null + } + }, "cache": { "description": "DNS resolver cache configuration.", "type": "object", 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..77ab0ee8d 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 FallbackSchema, ForwardSchema 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..80b94f3f0 100644 --- a/python/knot_resolver/datamodel/forward_schema.py +++ b/python/knot_resolver/datamodel/forward_schema.py @@ -51,7 +51,7 @@ class ForwardSchema(ConfigSchema): """ subtree: ListOrItem[DomainName] - servers: Union[List[IPAddressOptionalPort], List[ForwardServerSchema]] + servers: List[Union[IPAddressOptionalPort, ForwardServerSchema]] options: ForwardOptionsSchema = ForwardOptionsSchema() def _validate(self) -> None: @@ -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: Optional[List[Union[IPAddressOptionalPort, ForwardServerSchema]]] = 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 %}