diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 32a1c7c58..e1b6718b9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -197,25 +197,111 @@ build:macOS: - pip3 install -U requests - python3 ./ci/gh_actions.py ${CI_COMMIT_REF_NAME} ${CI_COMMIT_SHA} -docker: +.docker: &docker <<: *nodep - stage: build + except: null image: docker:latest + variables: + DOCKER_HUB_REGISTRY: cznic/knot-resolver + GITLAB_REGISTRY: ${CI_REGISTRY}/knot/knot-resolver/cross-platform + tags: + - amd64 + - dind + +docker:build: + <<: *docker <<: *multi_platform - only: - refs: - - branches@knot/knot-resolver + stage: build + except: + - tags + script: + - docker buildx build --no-cache -t knot-resolver:${PLATFORM} . + after_script: + - docker rmi --force knot-resolver:${PLATFORM} + - docker rmi $(docker images -f "dangling=true" -q) tags: - ${PLATFORM} - dind - variables: - DOCKER_IMAGE_NAME: knot-resolver-test:${CI_COMMIT_SHA} + +docker:build:cross-platform: + <<: *docker + stage: build + only: + - master@knot/knot-resolver + - tags + before_script: + - > + docker buildx create + --name kres-builder + --driver docker-container + --bootstrap --use + - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin script: - - docker build --no-cache -t ${DOCKER_IMAGE_NAME} . - # TODO: perhaps try if the running image answers queries - after_script: # remove dangling images to avoid running out of disk space - - docker rmi ${DOCKER_IMAGE_NAME} - - docker rmi $(docker images -f "dangling=true" -q) + - > + docker buildx build + --no-cache + --platform linux/amd64,linux/arm64/v8,linux/arm/v7 + --provenance=false + --pull + --push + --tag ${GITLAB_REGISTRY}:${CI_COMMIT_REF_NAME} + . + +docker:test:cross-platform: + <<: *docker + <<: *multi_platform + stage: test + only: + - tags + - master@knot/knot-resolver + needs: + - docker:build:cross-platform + image: + name: ${GITLAB_REGISTRY}:${CI_COMMIT_REF_NAME} + entrypoint: [""] + before_script: + - apt-get update + - apt-get -y install knot-dnsutils curl git + - /usr/bin/knot-resolver -c /etc/knot-resolver/config.yaml > knot-resolver.log & + script: + # check that the resolver responds to queries + - kdig nic.cz @localhost#53 + - kdig +tcp nic.cz @localhost#53 + - kdig +tls nic.cz @localhost#853 + - kdig +https nic.cz @localhost#443 + # run some packaging tests + - tests/packaging/kresctl.sh + - tests/packaging/interactive/etag.sh + - tests/packaging/interactive/schema.sh + - tests/packaging/interactive/reload.sh + - tests/packaging/interactive/metrics.sh + - tests/packaging/interactive/cache-clear.sh + - tests/packaging/interactive/workers.sh + - kresctl stop + artifacts: + when: always + paths: + - knot-resolver.log + tags: + - docker + - ${PLATFORM} + +dockerhub:deploy: + <<: *docker + stage: deploy + when: manual + only: + - tags + needs: + - docker:test:cross-platform + before_script: + - echo "$DOCKER_HUB_TOKEN" | docker login -u $DOCKER_HUB_USER --password-stdin + script: + - > + docker buildx imagetools create + -t ${DOCKER_HUB_REGISTRY}:${CI_COMMIT_REF_NAME} + -t ${DOCKER_HUB_REGISTRY}:6 + ${GITLAB_REGISTRY}:${CI_COMMIT_REF_NAME} # }}} # sanity {{{ @@ -576,7 +662,7 @@ obs:trigger: &obs_trigger - source ./venv/bin/activate - pip install --upgrade pip - pip install apkg - - scripts/make-obs.sh + - scripts/ci/make-obs.sh - echo y | scripts/ci/build-in-obs.sh $OBS_REPO obs:release: @@ -618,6 +704,8 @@ obs:odvr: - apkg info cache | grep archive/dev - apkg install --build-dep - apkg test --test-dep + after_script: + - journalctl -u knot-resolver.service artifacts: expire_in: 1 week paths: @@ -668,6 +756,10 @@ pkg:debian-11: <<: *enable_repo_build image: $CI_REGISTRY/packaging/apkg/full/debian-11 +pkg:ubuntu-24.10: + <<: *pkg_test_deb + image: $CI_REGISTRY/packaging/apkg/full/ubuntu-24.10 + pkg:ubuntu-24.04: <<: *pkg_test_deb image: $CI_REGISTRY/packaging/apkg/full/ubuntu-24.04 @@ -682,28 +774,28 @@ pkg:ubuntu-20.04: <<: *enable_repo_build image: $CI_REGISTRY/packaging/apkg/full/ubuntu-20.04 -pkg:fedora-40: +pkg:fedora-41: <<: *pkg_test - image: $CI_REGISTRY/packaging/apkg/full/fedora-40 + image: $CI_REGISTRY/packaging/apkg/full/fedora-41 -pkg:fedora-39: +pkg:fedora-40: <<: *pkg_test - image: $CI_REGISTRY/packaging/apkg/full/fedora-39 + image: $CI_REGISTRY/packaging/apkg/full/fedora-40 pkg:alma-9: <<: *pkg_test image: $CI_REGISTRY/packaging/apkg/full/alma-9 + before_script: + # python-watchdog is not included in the official Alma 9 packages + # install it using PyPi just for testing + - pip3 install watchdog pkg:arch: <<: *pkg_test_user - image: $CI_REGISTRY/packaging/apkg/test/arch - tags: - - docker - - linux - - amd64 + image: $CI_REGISTRY/packaging/apkg/full/arch before_script: - - pacman -Syy - - pip install apkg + # prometheus and watchdog are optional dependencies, but our `apkg test` needs them + - pacman -Syu --noconfirm python-prometheus_client python-watchdog # RHEL 8 derivatives would need more work due to *default* python being old #pkg:rocky-8: diff --git a/AUTHORS b/AUTHORS index 3f8f226e2..d511deec3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Daniel Salzman daurnimator David Beitey Felix Yan +Frantisek Tobias Grigorii Demidov Hasnat Héctor Molinero Fernández @@ -45,6 +46,7 @@ Lukáš Ježek Lukáš Ondráček Manu Bretelle Marek Vavruša +menakite <29005531+menakite@users.noreply.github.com> Michal Karm Babáček Michal Lupečka Ondřej Surý diff --git a/Dockerfile b/Dockerfile index 613636e3d..cc5e5ad96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ FROM debian:12 AS build ENV OBS_REPO=knot-resolver-latest ENV DISTROTEST_REPO=Debian_12 - RUN apt-get update -qq && \ apt-get -qqq -y install \ apt-transport-https ca-certificates wget \ @@ -25,10 +24,19 @@ RUN cd /source && \ git submodule update --init --recursive && \ git config --global user.name "Docker Build" && \ git config --global user.email docker-build@knot-resolver && \ + \ + # Replace 'knot-resolver' user and group with 'root' + # in meson_options.tx and python/knot_resolver/constants.py. + # This is needed for the file/directory permissions validation + # and then for the proper functioning of the resolver. + sed s/knot-resolver/root/g -i meson_options.txt && \ + sed 's/USER.*/USER = "root"/g' -i python/knot_resolver/constants.py && \ + sed 's/GROUP.*/GROUP = "root"/g' -i python/knot_resolver/constants.py && \ + git commit -a -m TMP && \ + \ /root/.local/bin/apkg build-dep -y && \ /root/.local/bin/apkg build - # Real container FROM debian:12-slim AS runtime @@ -56,10 +64,9 @@ RUN apt-get install -y /pkg/*/*.deb && \ apt-get remove -y -qq curl gnupg2 && \ apt-get autoremove -y && \ apt-get clean && \ - rm -rf /var/lib/apt/lists/* && \ - mkdir /config + rm -rf /var/lib/apt/lists/* -COPY etc/config/config.example.docker.yaml /config/config.yaml +COPY etc/config/config.example.docker.yaml /etc/knot-resolver/config.yaml LABEL cz.knot-resolver.vendor="CZ.NIC" LABEL maintainer="knot-resolver-users@lists.nic.cz" @@ -67,5 +74,10 @@ LABEL maintainer="knot-resolver-users@lists.nic.cz" # Export plain DNS, DoT, DoH and management interface EXPOSE 53/UDP 53/TCP 443/TCP 853/TCP 5000/TCP +# Prepare shared config +VOLUME /etc/knot-resolver +# Prepare shared cache +VOLUME /var/cache/knot-resolver + ENTRYPOINT ["/usr/bin/knot-resolver"] -CMD ["-c", "/config/config.yaml"] +CMD ["-c", "/etc/knot-resolver/config.yaml"] diff --git a/NEWS b/NEWS index 8d19f9373..050612214 100644 --- a/NEWS +++ b/NEWS @@ -1,16 +1,23 @@ -Knot Resolver 6.0.9 (2024-mm-dd) +Knot Resolver 6.0.10 (202y-mm-dd) ================================ -Incompatible changes --------------------- -- -f/--forks is removed (#631, !1602) +Improvements +------------ +- avoid multiple log lines when IPv6 isn't available (!1633) +- manager: fix startup on Linux without libsystemd (!1608) +- auto-reload TLS certificate files (!1626) + + +Knot Resolver 6.0.9 (2024-11-11) +================================ Improvements ------------ +- rate-limiting: add these options, mechanism, docs (!1624) - manager: secret for TLS session resumption via ticket (RFC5077) (!1567) - The manager creates and sets the secret for all running 'kresd' workers. + The manager creates and sets the secret for all running ``kresd`` workers. The secret is created automatically if the user does not configure their own secret in the configuration. This means that the workers will be able to resume each other's TLS sessions, regardless of whether the user has configured it to do so. @@ -20,6 +27,10 @@ Improvements - extended_errors: answer with EDE in more cases (!1585, !1588, !1590, !1592) - local-data: make DNAMEs work, i.e. generate CNAMEs (!1609) - daemon: use connected UDP sockets by default (#326, !1618) +- docker: multiplatform builds (#922, !1623) +- docker: shared VOLUMEs are prepared for configuration and cache (!1625, !1627) + + Configuration path was changed to standard ``/etc/knot-resolver/config.yaml``. Bugfixes -------- @@ -29,6 +40,7 @@ Bugfixes Incompatible changes -------------------- +- -f/--forks is removed (#631, !1602) - gnutls < 3.4 support is dropped, released over 9 years ago (!1601) - libuv < 1.27 support is dropped, released over 5 years ago (!1618) @@ -40,8 +52,8 @@ Security -------- - reduce buffering of transmitted data, especially TCP-based in userspace Also expose some of the new tweaks in lua: - (require 'ffi').C.the_worker.engine.net.tcp.user_timeout = 1000 - (require 'ffi').C.the_worker.engine.net.listen_{tcp,udp}_buflens.{snd,rcv} + - (require 'ffi').C.the_worker.engine.net.tcp.user_timeout = 1000 + - (require 'ffi').C.the_worker.engine.net.listen_{tcp,udp}_buflens.{snd,rcv} Packaging --------- @@ -69,12 +81,11 @@ Improvements ------------ - TLS (DoT, DoH): respect crypto policy overrides in OS (!1526) - manager: export metrics to JSON via management HTTP API (!1527) - * JSON is the new default metrics output format - * the ``prometheus-client`` Python package is now an optional dependency, - required only for Prometheus export to work + - JSON is the new default metrics output format + - the ``prometheus-client`` Python package is now an optional dependency, required only for Prometheus export to work - cache: prefetching records - * predict module: prefetching expiring records moved to prefetch module - * prefetch module: new module to prefetch expiring records + - predict module: prefetching expiring records moved to prefetch module + - prefetch module: new module to prefetch expiring records - stats: add separate metrics for IPv6 and IPv4 (!1545) - add the fresh DNSSEC root key "KSK-2024" already, Key ID 38696 (!1556) - manager: policy-loader: new component for separate loading of policy rules (!1540) diff --git a/daemon/main.c b/daemon/main.c index 142cdbe5c..1600f59a2 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -38,6 +38,8 @@ #include #if ENABLE_LIBSYSTEMD #include +#else +static int notify_ready(const char *state); #endif #include @@ -193,6 +195,8 @@ static int run_worker(uv_loop_t *loop, struct args *args) /* Notify supervisor. */ #if ENABLE_LIBSYSTEMD sd_notify(0, "READY=1"); +#else + notify_ready("READY=1"); #endif /* Run event loop */ uv_run(loop, UV_RUN_DEFAULT); @@ -382,6 +386,47 @@ static int start_listening(flagged_fd_array_t *fds) { return some_bad_ret; } +#if !ENABLE_LIBSYSTEMD +/* Notify supervisord about successful inicialization + * @note tested only on an abstract address in $NOTIFY_SOCKET*/ +static int notify_ready(const char *state) +{ + int sockfd; + struct sockaddr_un addr; + char *socket_path = getenv("NOTIFY_SOCKET"); + if (!socket_path) { + kr_log_error(WORKER, "Failed retrieving env variable $NOTIFY_SOCKET\n"); + return EXIT_FAILURE; + } + if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { + kr_log_error(WORKER, "Failed to create unix socket at $NOTIFY_SOCKET ('%s'): %s\n", + socket_path, strerror(errno)); + return EXIT_FAILURE; + } + + addr.sun_family = AF_UNIX; + + int addrlen; + if (socket_path[0] == '@') { + addr.sun_path[0] = '\0'; + strncpy(&addr.sun_path[1], socket_path + 1, sizeof(addr.sun_path) - 2); + addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path + 1) + 1; + } else { + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) + 1; + } + if (sendto(sockfd, state, strlen(state), 0, &addr, addrlen) == -1) { + kr_log_error(WORKER, "Failed to send notify message to '%s': %s\n", + socket_path, strerror(errno)); + close(sockfd); + return EXIT_FAILURE; + } + + close(sockfd); + return kr_ok(); +} +#endif /* if !ENABLE_LIBSYSTEMD */ + /* Drop POSIX 1003.1e capabilities. */ static void drop_capabilities(void) { diff --git a/daemon/worker.c b/daemon/worker.c index 500eeb26a..55c916904 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -845,8 +845,10 @@ static int transmit(struct qr_task *task) do { ret = uv_udp_connect(udp, out_comm.comm_addr); } while (ret == UV_EADDRINUSE && --connect_tries > 0); - if (ret < 0) - kr_log_error(IO, "Failed to establish udp connection: %s\n", uv_strerror(ret)); + if (ret < 0) { + kr_log_info(IO, "Failed to establish udp connection to %s: %s\n", + kr_straddr(out_comm.comm_addr), uv_strerror(ret)); + } } ret = qr_task_send(task, session, &out_comm, task->pktbuf); if (ret) { diff --git a/distro/pkg/arch/PKGBUILD b/distro/pkg/arch/PKGBUILD index ebbd164d1..b01352c8e 100644 --- a/distro/pkg/arch/PKGBUILD +++ b/distro/pkg/arch/PKGBUILD @@ -46,6 +46,7 @@ optdepends=( 'lua51-http: http and prefill modules, trust_anchors bootstrap' 'lua51-psl: policy.slice_randomize_psl() function' 'python-prometheus_client: stats and metrics in Prometheus format' + 'python-watchdog: files monitoring and reload on changes' ) backup=('etc/knot-resolver/config.yaml') options=(debug strip) diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index 5661e9739..907c9ffa9 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -56,6 +56,7 @@ Recommends: lua-http, lua-psl, python3-prometheus-client, + python3-watchdog, Suggests: knot-resolver6-module-http, Description: caching, DNSSEC-validating DNS resolver - core binaries diff --git a/distro/pkg/rpm/knot-resolver.spec b/distro/pkg/rpm/knot-resolver.spec index ca8602ffd..91c1a148b 100644 --- a/distro/pkg/rpm/knot-resolver.spec +++ b/distro/pkg/rpm/knot-resolver.spec @@ -65,6 +65,7 @@ Requires: python3-pyyaml Requires: python3-typing-extensions %endif Recommends: python3-prometheus_client +Recommends: python3-watchdog # dnstap module dependencies # SUSE is missing protoc-c protobuf compiler diff --git a/doc/_static/config.schema.json b/doc/_static/config.schema.json index b9397dc92..2532dc4fe 100644 --- a/doc/_static/config.schema.json +++ b/doc/_static/config.schema.json @@ -1,8 +1,8 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://www.knot-resolver.cz/documentation/v6.0.8/_static/config.schema.json", + "$id": "https://www.knot-resolver.cz/documentation/v6.0.9/_static/config.schema.json", "title": "Knot Resolver configuration JSON schema", - "description": "Version Knot Resolver 6.0.8", + "description": "Version Knot Resolver 6.0.9", "type": "object", "properties": { "version": { @@ -1677,27 +1677,33 @@ "properties": { "capacity": { "type": "integer", + "minimum": 1, "description": "Expected maximal number of blocked networks/hosts at the same time.", "default": 524288 }, "rate-limit": { "type": "integer", + "minimum": 1, "description": "Maximal number of allowed queries per second from a single host." }, "instant-limit": { "type": "integer", + "minimum": 1, "description": "Maximal number of allowed queries at a single point in time from a single host.", "default": 50 }, "slip": { "type": "integer", + "minimum": 0, + "maximum": 32, "description": "Number of restricted responses out of which one is sent as truncated, the others are dropped.", "default": 2 }, "log-period": { - "type": "integer", - "description": "Minimal time in msec between two log messages, or zero to disable.", - "default": 0 + "type": "string", + "pattern": "^(\\d+)(us|ms|s|m|h|d)$", + "description": "Minimal time between two log messages, or '0s' to disable.", + "default": "0s" }, "dry-run": { "type": "boolean", diff --git a/doc/dev/build.rst b/doc/dev/build.rst index f65122146..d3d87dce9 100644 --- a/doc/dev/build.rst +++ b/doc/dev/build.rst @@ -306,6 +306,7 @@ All dependencies are also listed in `pyproject.toml ` for more info. + + +.. option:: debug + + Executes a GDB-compatible debugger and attaches it to the Manager's + subprocesses. By default, the debugger is ``gdb`` and the subprocesses are + only the ``kresd`` workers. + + .. warning:: + + The ``debug`` command is a utility for Knot Resolver developers and is + not intended to be used by end-users. Running this command **will** make + your resolver unresponsive. + + .. note:: + + Modern kernels will prevent debuggers from tracing processes that are + not their descendants, which is exactly the scenario that happens with + ``kresctl debug``. There are three ways to work around this, listed in + the order in which they are preferred in terms of security: + + 1. Grant the debugger the ``cap_sys_ptrace`` capability + (**recommended**) + + * For ``gdb``, this may be achieved by using the ``setcap`` + command like so: + + .. code-block:: bash + + sudo setcap cap_sys_ptrace=eip /usr/bin/gdb + + 2. Run the debugger as root + + * You may use the ``--sudo`` option to achieve this + + 3. Set ``/proc/sys/kernel/yama/ptrace_scope`` to ``0`` + + * This will allow **all** programs in your current session to + trace each other. Handle with care! + + .. note:: + + This command will only work if executed on the same machine where Knot + Resolver is running. Remote debugging is currently not supported. + + .. option:: [proc_type] + + :default: kresd + + Optional. The type of process to debug. See :ref:`Subprocess types + ` for more info. + + .. option:: --sudo + + Run the debugger with sudo. + + .. option:: --gdb + + Use a custom GDB executable. This may be a command on ``PATH``, or an + absolute path to an executable. + + .. option:: --print-only + + Prints the GDB command line into ``stderr`` as a Python array, does not + execute GDB. + + +.. _debugging-with-kresctl-subprocess-types: + +Subprocess types +---------------- + +Some of ``kresctl``'s commands (like :option:`pids` and :option:`debug`) take a subprocess +type value determining which subprocesses will be affected by them. The possible +values are as follows: + +* ``kresd`` -- the worker daemons +* ``gc`` -- the cache garbage collector +* ``all`` -- all of the Manager's subprocesses diff --git a/doc/dev/index.rst b/doc/dev/index.rst index a13e3d616..f0d557630 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -30,6 +30,13 @@ Welcome to Knot Resolver's documentation for developers and advanced users! manager-dev-code layered-protocols +.. toctree:: + :caption: Debugging + :name: debugging-chapter + :maxdepth: 1 + + debugging-with-kresctl + .. toctree:: :caption: Lua configuration :name: configuration-lua-chapter diff --git a/doc/kresctl.8.in b/doc/kresctl.8.in index 538356afc..d8770ca07 100644 --- a/doc/kresctl.8.in +++ b/doc/kresctl.8.in @@ -32,7 +32,7 @@ The available options are: .B \-s\fI \fR, \fB\-\-socket \fI Specify how to connect to a running Knot Resolver. Accepts path to Unix-domain -socket or \fIhost:port\fR. Defaults to \fI/var/run/knot-resolver/manager.sock\fR +socket or \fIhost:port\fR. Defaults to \fI/var/run/knot-resolver/kres-api.sock\fR Some commands do not require communication with the running resolver. In such cases, the value of this option is ignored and the command may succeed even diff --git a/doc/user/config-network-server-tls.rst b/doc/user/config-network-server-tls.rst index 420de59ca..8fc848786 100644 --- a/doc/user/config-network-server-tls.rst +++ b/doc/user/config-network-server-tls.rst @@ -130,9 +130,12 @@ policies. .. tip:: - The certificate files aren't automatically reloaded on change. - If you update the certificate files, e.g. using ACME, you have to either - reload or restart the service(s). + If you have ``python-watchdog`` installed on your system, + the certificate files are automatically reloaded on change. + If you update the certificate files, e.g. using ACME, + the manager is notified about changes and commands all workers + to reload their certificate files. If you don't have ``python-watchdog``, + you have to restart the ``knot-resolver`` service manually. .. option:: sticket-secret: diff --git a/doc/user/config-overview.rst b/doc/user/config-overview.rst index 15cb9f629..b42ee0a39 100644 --- a/doc/user/config-overview.rst +++ b/doc/user/config-overview.rst @@ -54,7 +54,7 @@ Getting the JSON Schema * The JSON schema can be `downloaded here <_static/config.schema.json>`_ (valid only for the version of the resolver this documentation was generated for). * The :ref:`kresctl schema ` command outputs the JSON schema of the currently installed version as well. It does not require a running resolver. -* The JSON schema can also be obtained from a running resolver by sending a HTTP GET request to the path ``/schema`` on the :ref:`management API ` (by default a Unix socket at ``/var/run/knot-resolver/manager.sock``). +* The JSON schema can also be obtained from a running resolver by sending a HTTP GET request to the path ``/schema`` on the :ref:`management API ` (by default a Unix socket at ``/run/knot-resolver/kres-api.sock``). .. tip:: diff --git a/doc/user/config-performance.rst b/doc/user/config-performance.rst index dbd8d12d2..b09b4a92b 100644 --- a/doc/user/config-performance.rst +++ b/doc/user/config-performance.rst @@ -32,3 +32,4 @@ impact than cache settings and number of instances. config-rfc7706 config-priming config-edns-keepalive + config-rate-limiting diff --git a/doc/user/config-rate-limiting.rst b/doc/user/config-rate-limiting.rst new file mode 100644 index 000000000..35342bf9e --- /dev/null +++ b/doc/user/config-rate-limiting.rst @@ -0,0 +1,147 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _config-rate-limiting: + +Rate limiting +============= + +Rate limiting is a method to combat DNS reflection amplification +attacks. These attacks rely on the fact that the source address of a UDP query +can be forged, and without a worldwide deployment of `BCP38 +`_, such a forgery cannot be prevented. +An attacker can use a DNS server (or multiple servers) as an amplification +source to flood a victim with a large number of unsolicited DNS responses. +Rate limiting lowers the amplification factor of these attacks by sending some +responses as truncated or by dropping them altogether. + +See the `operator's overview blogpost `_ +for more in depth introduction to this section, +but beware that the *soft limit* was dropped in favor of the *slip* mechanism +that's common in other DNS servers. + + +.. option:: rate-limiting/rate-limit: + + Maximal allowed number of UDP queries per second from a single IPv6 or IPv4 address. + To be set according to the server performance. + Setting the value enables rate limiting as the rest of the configuration is optional. + + Rate limiting is performed for the whole address and several chosen prefixes. + The limits of prefixes are constant multiples of :option:`rate-limit `. + The specific prefixes and multipliers, which might be adjusted in the future, are as follows: + + .. table:: + + +------+------+------+------+------+------+ + | IPv6 | /128 | /64 | /56 | /48 | /32 | + +======+======+======+======+======+======+ + | | 1 | 2 | 3 | 4 | 64 | + +------+------+------+------+------+------+ + + .. table:: + + +------+------+------+------+------+ + | IPv4 | /32 | /24 | /20 | /18 | + +======+======+======+======+======+ + | | 1 | 32 | 256 | 768 | + +------+------+------+------+------+ + + With each host/network, a counter of unrestricted responses is associated; + if the counter would exceed its capacity, it is not incremented and the response is restricted. + Counters use exponential decay for lowering their values, + i.e. they are lowered by a constant fraction of their value each millisecond. + The specified rate limit is reached, when the number of queries is the same every millisecond; + sending many queries once a second or even a larger timespan leads to a more strict limiting. + + +.. option:: rate-limiting/instant-limit: + + :default: 50 + + Maximal allowed number of queries at a single point in time from a single IPv6 or IPv4 address. + To be set according to the expected normal behaviour of clients; likely not needed to be alterered. + The limits for prefixes use the same multipliers as for :option:`rate-limit `. + + This limit is relevant for bursts of queries, + e.g. when a recently inactive host/network suddenly starts sending many queries. + + The :option:`instant-limit ` + sets the actual capacity of each counter of responses, + and together with the :option:`rate-limit ` + they set the fraction by which the counter is periodically lowered. + The :option:`instant-limit ` may be at least + :option:`rate-limit ` **/ 1000**, at which point the + counters are zeroed each millisecond. + + +.. option:: rate-limiting/slip: + + :default: 2 + + Number of restricted responses out of which one is sent as truncated, the others are dropped. + + As attacks using DNS/UDP are usually based on a forged source address, + an attacker could deny services to the victim's netblock if all + responses would be completely blocked. The idea behind SLIP mechanism + is to send each N\ :sup:`th` response as truncated, thus allowing client to + reconnect via TCP for at least some degree of service. It is worth + noting, that some responses can't be truncated (e.g. SERVFAIL). + + - Setting the value to **0** will cause all rate-limited responses to + be dropped. The outbound bandwidth and packet rate will be strictly capped + by the :option:`rate-limit ` option. + All legitimate requestors affected + by the limit will face denial of service and will observe excessive timeouts. + Therefore this setting is not recommended. + + - Setting the value to **1** will cause all rate-limited responses to + be sent as truncated. The amplification factor of the attack will be reduced, + but the outbound data bandwidth won't be lower than the incoming bandwidth. + Also the outbound packet rate will be the same as without rate limiting. + + - Setting the value to **2** will cause approximately half of the rate-limited responses + to be dropped, and the other half will be sent as truncated. With this + configuration, both outbound bandwidth and packet rate will be lower than the + inbound. On the other hand, the dropped responses enlarge the time window + for possible cache poisoning attack on the resolver. + + - Setting the value to anything **larger than 2** will keep on decreasing + the outgoing rate-limited bandwidth, packet rate, and chances to notify + legitimate requestors to reconnect using TCP. These attributes are inversely + proportional to the configured value. Setting the value high is not advisable. + + +.. option:: rate-limiting/capacity: + + :default: 524288 + + Maximal number of stored hosts/networks with their counters. + The data structure tries to store only the most frequent sources, + so it is safe to set it according to the expected maximal number of limited ones. + + Use **1.4 *** ``maximum-qps`` **/** :option:`rate-limit `, + where ``maximum-qps`` is the number of queries which can be handled by the server per second. + There is at most ``maximum-qps`` **/** :option:`rate-limit ` limited hosts; + larger networks have higher limits, so they require only a fraction of the value (handled by the **1.4** multiplier). + The value will be rounded up to the nearest power of two. + + The memory occupied by one table structure is **8 *** :option:`capacity ` Bytes. + + +.. option:: rate-limiting/log-period: