]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
fallback: add YAML config + docs 1733/head
authorVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 21 Aug 2025 12:26:09 +0000 (14:26 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 15 Oct 2025 14:02:54 +0000 (16:02 +0200)
NEWS
doc/_static/config.schema.json
doc/user/config-fallback.rst [new file with mode: 0644]
doc/user/config-performance.rst
python/knot_resolver/datamodel/config_schema.py
python/knot_resolver/datamodel/forward_schema.py
python/knot_resolver/datamodel/templates/forward.lua.j2

diff --git a/NEWS b/NEWS
index 4b0fa072b255d2bfb9e014549c076a6c0974a27e..429a5413b7a1785a5752c473709db7a67c3ea155 100644 (file)
--- 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
 --------
index 88e11901d818a2a2a999cb15a8cf32294196da20..171ce40b145c8f57c88c594c333e72675c70670f 100644 (file)
                                 "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
                         }
                     }
                 }
                         "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": {
                                         }
                                     }
                                 }
-                            }
-                        ],
+                            ]
+                        },
                         "description": "Forward servers configuration."
                     },
                     "options": {
             "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 (file)
index 0000000..7a45abb
--- /dev/null
@@ -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 <config-forward>`.
+
+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 <config-views>`.
index 1d3604d8cec7dc730d42caa73ab310c27ecaf6f7..481d357d32988d083c84e0c46dc8bfb7b0a3b22d 100644 (file)
@@ -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
index 2cc2680c9203cc15d6dad21f0633f0a1ac7ecdf8..77ab0ee8d22922cdc8d7ff0cb62f89abbaa2df1a 100644 (file)
@@ -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]
index 6b693e6be475c4dccdca895ab3c640bbcc75153f..80b94f3f01df45c446876bef3f9d79bb8d0dfbf5 100644 (file)
@@ -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.")
index 24311da183ade67b39ba0f451b7348777688d624..bc4d73ef6b57743ab0f149917b8176d88156bf5a 100644 (file)
@@ -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 %}