diff --git a/Dockerfile.m4 b/Dockerfile.m4 index 26a5ac7..a0d2f96 100644 --- a/Dockerfile.m4 +++ b/Dockerfile.m4 @@ -232,11 +232,12 @@ ENV KRESD_DNS1_IP=1.1.1.1@853 ENV KRESD_DNS1_HOSTNAME=cloudflare-dns.com ENV KRESD_DNS2_IP=1.0.0.1@853 ENV KRESD_DNS2_HOSTNAME=cloudflare-dns.com +ENV KRESD_INSTANCE_NUMBER=1 +ENV KRESD_RECENTLY_BLOCKED_NUMBER=100 +ENV KRESD_CERT_MANAGED=true ENV KRESD_WATCHDOG_QNAME=cloudflare.com. ENV KRESD_WATCHDOG_QTYPE=A ENV KRESD_WATCHDOG_INTERVAL=10 -ENV KRESD_STATS_BLOCKED_COUNT=100 -ENV KRESD_CERT_MANAGED=true ENV KRESD_NIC= ENV KRESD_LOG_LEVEL=notice @@ -319,6 +320,8 @@ FROM base AS test # Perform a test run RUN printf '%s\n' 'Starting services...' \ + && export TINI_SUBREAPER=1 \ + && export KRESD_INSTANCE_NUMBER=2 \ && (nohup container-init &) \ && TIMEOUT_DURATION=240s \ && TIMEOUT_COMMAND='until container-healthcheck; do sleep 1; done' \ diff --git a/README.md b/README.md index 2795076..9215a73 100644 --- a/README.md +++ b/README.md @@ -44,17 +44,12 @@ Hostname of the DNS-over-TLS server to which the queries will be forwarded Certificate hash of the DNS-over-TLS server to which the queries will be forwarded ([key-pinned authentication docs](https://knot-resolver.readthedocs.io/en/stable/modules-policy.html#key-pinned-authentication)). -#### `KRESD_WATCHDOG_QNAME` (default: `cloudflare.com.`) -Query name to check the health status of kresd. +#### `KRESD_INSTANCE_NUMBER` (default: `1`) +Number of instances to launch. -#### `KRESD_WATCHDOG_QTYPE` (default: `A`) -Query type to check the health status of kresd. - -#### `KRESD_WATCHDOG_INTERVAL` (default: `10`) -Interval in seconds to check the health status of kresd. - -#### `KRESD_STATS_BLOCKED_COUNT` (default: `100`) -Number of recently blocked domains to expose in stats. +#### `KRESD_RECENTLY_BLOCKED_NUMBER` (default: `100`) +Number of recently blocked domains to store in memory for each instance. +The `/recently_blocked` endpoint returns an aggregated list of all instances. #### `KRESD_CERT_MANAGED` (default: `true`) If equals `true`, a self-signed certificate will be generated. You can provide your own certificate with these options: @@ -66,6 +61,15 @@ If equals `true`, a self-signed certificate will be generated. You can provide y > **Note:** for a more advanced setup, look at the [following example](examples/caddy) with [Let's Encrypt](https://letsencrypt.org) and [Caddy](https://caddyserver.com/). +#### `KRESD_WATCHDOG_QNAME` (default: `cloudflare.com.`) +Query name to check the health status of kresd. + +#### `KRESD_WATCHDOG_QTYPE` (default: `A`) +Query type to check the health status of kresd. + +#### `KRESD_WATCHDOG_INTERVAL` (default: `10`) +Interval in seconds to check the health status of kresd. + #### `KRESD_NIC` (default: empty) If defined, kresd will only listen on the specified interface. Some users observed a considerable, close to 100%, performance gain in Docker containers when they bound the daemon to a single interface:ip address pair diff --git a/config/knot-resolver/kresd.conf.d/080-policy-blocklist.conf b/config/knot-resolver/kresd.conf.d/080-policy-blocklist.conf index 601dd5c..e3c69dd 100644 --- a/config/knot-resolver/kresd.conf.d/080-policy-blocklist.conf +++ b/config/knot-resolver/kresd.conf.d/080-policy-blocklist.conf @@ -4,7 +4,7 @@ if not stats then modules.load('stats') end if not http then modules.load('http') end local lru = require('lru') -local recently_blocked = lru.new(tonumber(env.KRESD_STATS_BLOCKED_COUNT)) +local recently_blocked = lru.new(tonumber(env.KRESD_RECENTLY_BLOCKED_NUMBER)) local blocked_metric = 'answer.blocked' stats[blocked_metric] = 0 @@ -26,24 +26,31 @@ local function deny_and_count(msg) end end -local function get_recently_blocked() - local out = {} - for qname, count in recently_blocked:pairs() do - table.insert(out, { qname = qname, count = count }) - end - if out[1] == nil then return '[]' - else return tojson(out) end -end - policy.add(policy.rpz( deny_and_count('Blocked domain'), env.KRESD_DATA_DIR .. '/hblock/blocklist.rpz', true )) -http.configs._builtin.webmgmt.endpoints['/blocked'] = { +function get_recently_blocked() + local rb = {} + for qname, count in recently_blocked:pairs() do + rb[qname] = count + end + return rb +end + +http.configs._builtin.webmgmt.endpoints['/recently_blocked'] = { 'application/json', function () - return get_recently_blocked() + local out = {} + for _, result in pairs(map('get_recently_blocked()')) do + if type(result) == 'table' then + for qname, count in pairs(result) do + out[qname] = (out[qname] or 0) + count + end + end + end + return tojson(out) end } diff --git a/run.sh b/run.sh index f5d9916..a54ee89 100755 --- a/run.sh +++ b/run.sh @@ -45,6 +45,7 @@ printf '%s\n' "Creating \"${CONTAINER_NAME:?}\" container..." --publish '127.0.0.153:8453:8453/tcp' \ --mount type=volume,src="${CONTAINER_NAME:?}-data",dst='/var/lib/knot-resolver/' \ --mount type=volume,src="${CONTAINER_NAME:?}-cache",dst='/var/cache/knot-resolver/' \ + --env KRESD_INSTANCE_NUMBER=4 \ "${IMAGE_NAME:?}" "$@" >/dev/null printf '%s\n\n' 'Done!' diff --git a/scripts/bin/container-healthcheck b/scripts/bin/container-healthcheck index 39a4d29..f9365a3 100755 --- a/scripts/bin/container-healthcheck +++ b/scripts/bin/container-healthcheck @@ -3,11 +3,14 @@ set -eu umask 0002 -# kresd service must be running -if ! is-sv-status kresd run; then - >&2 printf '%s\n' 'kresd service is not running' - exit 1 -fi +# kresd services must be running +id=0; while [ "${id:?}" -lt "${KRESD_INSTANCE_NUMBER:?}" ]; do + if ! is-sv-status "kresd${id:?}" run; then + >&2 printf '%s\n' "kresd${id:?} service is not running" + exit 1 + fi + id=$((id + 1)) +done # kres-cache-gc service must be running if ! is-sv-status kres-cache-gc run; then diff --git a/scripts/bin/container-init b/scripts/bin/container-init index d38b999..e238475 100755 --- a/scripts/bin/container-init +++ b/scripts/bin/container-init @@ -21,5 +21,15 @@ if [ ! -e "${KRESD_DATA_DIR:?}"/hblock/blocklist.rpz ]; then hblock fi +# Create a service for each kresd instance +if [ "${KRESD_INSTANCE_NUMBER:?}" -gt 1 ]; then + id=1; while [ "${id:?}" -lt "${KRESD_INSTANCE_NUMBER:?}" ]; do + if [ ! -d "${SVDIR:?}"/kresd"${id:?}"/ ]; then + cp -a "${SVDIR:?}"/kresd0/ "${SVDIR:?}"/kresd"${id:?}"/ + fi + id=$((id + 1)) + done +fi + # Start all services exec tini -- runsvdir -P "${SVDIR:?}" diff --git a/scripts/bin/kres-console b/scripts/bin/kres-console index 6d9cd1a..5419f57 100755 --- a/scripts/bin/kres-console +++ b/scripts/bin/kres-console @@ -3,11 +3,14 @@ set -eu umask 0002 -if is-sv-status kresd run; then - KRESD_PID=$(cat "${SVDIR:?}"/kresd/supervise/pid) - KRESD_SOCKET=${KRESD_CACHE_DIR:?}/control/${KRESD_PID:?} - kresc "${KRESD_SOCKET:?}" -else - >&2 printf '%s\n' 'kresd is not running' - exit 1 -fi +id=0; while [ "${id:?}" -lt "${KRESD_INSTANCE_NUMBER:?}" ]; do + if is-sv-status "kresd${id:?}" run; then + KRESD_PID=$(cat "${SVDIR:?}"/"kresd${id:?}"/supervise/pid) + KRESD_SOCKET=${KRESD_CACHE_DIR:?}/control/${KRESD_PID:?} + exec kresc "${KRESD_SOCKET:?}" + fi + id=$((id + 1)) +done + +>&2 printf '%s\n' 'kresd is not running' +exit 1 diff --git a/scripts/service/kresd/run b/scripts/service/kresd0/run similarity index 100% rename from scripts/service/kresd/run rename to scripts/service/kresd0/run