Modules

dnsproxy – Tiny DNS proxy

The module forwards all queries, or all specific zone queries if configured per zone, to the indicated server for resolution. If configured in the fallback mode, only localy unsatisfied queries are forwarded. I.e. a tiny DNS proxy. There are several uses of this feature:

  • A substitute public-facing server in front of the real one
  • Local zones (poor man’s “views”), rest is forwarded to the public-facing server
  • etc.

Note

The module does not alter the query/response as the resolver would, and the original transport protocol is kept as well.

Example

The configuration is straightforward and just a single remote server is required:

remote:
  - id: hidden
    address: 10.0.1.1

mod-dnsproxy:
  - id: default
    remote: hidden
    fallback: on

template:
  - id: default
    global-module: mod-dnsproxy/default

zone:
  - domain: local.zone

When clients query for anything in the local.zone, they will be responded to locally. The rest of the requests will be forwarded to the specified server (10.0.1.1 in this case).

Module reference

mod-dnsproxy:
  - id: STR
    remote: remote_id
    timeout: INT
    fallback: BOOL
    catch-nxdomain: BOOL

id

A module identifier.

remote

A reference to a remote server where the queries are forwarded to.

Required

timeout

A remote response timeout in milliseconds.

Default: 500

fallback

If enabled, localy unsatisfied queries leading to REFUSED (no zone) are forwarded. If disabled, all queries are directly forwarded without any local attempts to resolve them.

Default: on

catch-nxdomain

If enabled, localy unsatisfied queries leading to NXDOMAIN are forwarded. This option is only relevant in the fallback mode.

Default: off

dnstap – Dnstap traffic logging

A module for query and response logging based on the dnstap library. You can capture either all or zone-specific queries and responses; usually you want to do the former.

Example

The configuration comprises only a sink path parameter, which can be either a file or a UNIX socket:

mod-dnstap:
  - id: capture_all
    sink: /tmp/capture.tap

template:
  - id: default
    global-module: mod-dnstap/capture_all

Note

To be able to use a Unix socket you need an external program to create it. Knot DNS connects to it as a client using the libfstrm library. It operates exactly like syslog. See here for more details.

Note

Dnstap log files can also be created or read using kdig.

Module reference

For all queries logging, use this module in the default template. For zone-specific logging, use this module in the proper zone configuration.

mod-dnstap:
  - id: STR
    sink: STR
    identity: STR
    version: STR
    log-queries: BOOL
    log-responses: BOOL

id

A module identifier.

sink

A sink path, which can be either a file or a UNIX socket when prefixed with unix:.

Required

Warning

File is overwritten on server startup or reload.

identity

A DNS server identity. Set empty value to disable.

Default: FQDN hostname

version

A DNS server version. Set empty value to disable.

Default: server version

log-queries

If enabled, query messages will be logged.

Default: on

log-responses

If enabled, response messages will be logged.

Default: on

noudp — No UDP response

The module sends empty truncated response to any UDP query. TCP queries are not affected.

Example

To enable this module globally, you need to add something like the following to the configuration file:

template:
  - id: default
    global-module: mod-noudp

Note

This module is not configurable.

onlinesign — Online DNSSEC signing

The module provides online DNSSEC signing. Instead of pre-computing the zone signatures when the zone is loaded into the server or instead of loading an externally signed zone, the signatures are computed on-the-fly during answering.

The main purpose of the module is to enable authenticated responses with zones which use other dynamic module (e.g., automatic reverse record synthesis) because these zones cannot be pre-signed. However, it can be also used as a simple signing solution for zones with low traffic and also as a protection against zone content enumeration (zone walking).

In order to minimize the number of computed signatures per query, the module produces a bit different responses from the responses that would be sent if the zone was pre-signed. Still, the responses should be perfectly valid for a DNSSEC validating resolver.

Differences from statically signed zones:

  • The NSEC records are constructed as Minimally Covering NSEC Records (RFC 7129#appendix-A). Therefore the generated domain names cover the complete domain name space in the zone’s authority.
  • NXDOMAIN responses are promoted to NODATA responses. The module proves that the query type does not exist rather than that the domain name does not exist.
  • Domain names matching a wildcard are expanded. The module pretends and proves that the domain name exists rather than proving a presence of the wildcard.

Records synthesized by the module:

  • DNSKEY record is synthesized in the zone apex and includes public key material for the active signing key.
  • NSEC records are synthesized as needed.
  • RRSIG records are synthesized for authoritative content of the zone.

Known issues:

  • The delegations are not signed correctly.
  • Some CNAME records are not signed correctly.
  • The automatic policy-based key rotation does not work. The rotation events are invoked just at server (re)load.

Limitations:

  • Online-sign module always enforces Single-Type Signing scheme.
  • Only one active signing key can be used.
  • Key rollover is not possible.
  • The NSEC records may differ for one domain name if queried for different types. This is an implementation shortcoming as the dynamic modules cooperate loosely. Possible synthesis of a type by other module cannot be predicted. This dissimilarity should not affect response validation, even with validators performing aggressive negative caching.
  • The NSEC proofs will work well with other dynamic modules only if the modules synthesize only A and AAAA records. If synthesis of other type is required, please, report this information to Knot DNS developers.

Example

  • Enable the module in the zone configuration with the default signing policy:

    zone:
      - domain: example.com
        module: mod-onlinesign
    

    Or with an explicit signing policy:

    policy:
      - id: rsa
        algorithm: RSASHA256
        zsk-size: 2048
    
    mod-onlinesign:
      - id: explicit
        policy: rsa
    
    zone:
      - domain: example.com
        module: mod-onlinesign/explicit
    

    Or use manual policy in an analogous manner, see Manual key management.

    Note

    Only id, manual, keystore, algorithm, ksk-size, and rrsig-lifetime policy items are relevant to this module. If no rrsig-lifetime is configured, the default value is 25 hours.

  • Make sure the zone is not signed and also that the automatic signing is disabled. All is set, you are good to go. Reload (or start) the server:

    $ knotc reload
    

The following example stacks the online signing with reverse record synthesis module:

mod-synthrecord:
  - id: lan-forward
    type: forward
    prefix: ip-
    ttl: 1200
    network: 192.168.100.0/24

zone:
  - domain: corp.example.net
    module: [mod-synthrecord/lan-forward, mod-onlinesign]

Module reference

mod-onlinesign:
  - id: STR
    policy: STR

id

A module identifier.

policy

A reference to DNSSEC signing policy. A special default value can be used for the default policy settings.

rosedb – Static resource records

The module provides a mean to override responses for certain queries before the record is searched in the available zones. The module comes with the rosedb_tool tool used to manipulate the database of static records.

For example, let’s suppose we have a database of following records:

myrecord.com.      3600 IN A 127.0.0.1
www.myrecord.com.  3600 IN A 127.0.0.2
ipv6.myrecord.com. 3600 IN AAAA ::1

And we query the nameserver with the following:

$ kdig IN A myrecord.com
  ... returns NOERROR, 127.0.0.1
$ kdig IN A www.myrecord.com
  ... returns NOERROR, 127.0.0.2
$ kdig IN A stuff.myrecord.com
  ... returns NOERROR, 127.0.0.1
$ kdig IN AAAA myrecord.com
  ... returns NOERROR, NODATA
$ kdig IN AAAA ipv6.myrecord.com
  ... returns NOERROR, ::1

An entry in the database matches anything at the same or a lower domain level, i.e. ‘myrecord.com’ matches ‘a.a.myrecord.com’ as well. This can be utilized to create catch-all entries.

You can also add authority information for the entries, provided you create SOA + NS records for a name, like so:

myrecord.com.     3600 IN SOA master host 1 3600 60 3600 3600
myrecord.com.     3600 IN NS ns1.myrecord.com.
myrecord.com.     3600 IN NS ns2.myrecord.com.
ns1.myrecord.com. 3600 IN A 127.0.0.1
ns2.myrecord.com. 3600 IN A 127.0.0.2

In this case, the responses will:

  1. Be authoritative (AA flag set)
  2. Provide an authority section (SOA + NS)
  3. Be NXDOMAIN if the name is found (i.e. the ‘IN AAAA myrecord.com’ from the example), but not the RR type (this is to allow the synthesis of negative responses)

The SOA record applies only to the ‘myrecord.com.’, not to any other record (not even those of its subdomains). From this point of view, all records in the database are unrelated and not hierarchical. The idea is to provide subtree isolation for each entry.

In addition, the module is able to log matching queries via remote syslog if you specify a syslog address endpoint and an optional string code.

Example

  • Create the entries in the database:

    $ mkdir /tmp/static_rrdb
    $ # No logging
    $ rosedb_tool /tmp/static_rrdb add myrecord.com. A 3600 "127.0.0.1" "-" "-"
    $ # Logging as 'www_query' to Syslog at 10.0.0.1
    $ rosedb_tool /tmp/static_rrdb add www.myrecord.com. A 3600 "127.0.0.1" \
                                                     "www_query" "10.0.0.1"
    $ # Logging as 'ipv6_query' to Syslog at 10.0.0.1
    $ rosedb_tool /tmp/static_rrdb add ipv6.myrecord.com. AAAA 3600 "::1" \
                                                  "ipv6_query" "10.0.0.1"
    $ # Verify settings
    $ rosedb_tool /tmp/static_rrdb list
    www.myrecord.com.       A RDATA=10B     www_query       10.0.0.1
    ipv6.myrecord.com.      AAAA RDATA=22B  ipv6_query      10.0.0.1
    myrecord.com.           A RDATA=10B     -               -
    

Note

The database may be modified later on while the server is running.

  • Configure the query module:

    mod-rosedb:
      - id: default
        dbdir: /tmp/static_rrdb
    
    template:
      - id: default
        global-module: mod-rosedb/default
    

The module accepts just one parameter – the path to the directory where the database will be stored.

  • Start the server:

    $ knotd -c knot.conf
    
  • Verify the running instance:

    $ kdig @127.0.0.1#6667 A myrecord.com
    

Module reference

mod-rosedb:
  - id: STR
    dbdir: STR

id

A module identifier.

dbdir

A path to the directory where the database is stored.

Required

rrl — Response rate limiting

Response rate limiting (RRL) is a method to combat DNS reflection amplification attacks. These attacks rely on the fact that 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 and can flood a victim with a large number of unsolicited DNS responses. The RRL lowers the amplification factor of these attacks by sending some of the responses as truncated or by dropping them altogether.

Note

The module introduces two statistics counters. The number of slipped and dropped responses.

Example

You can enable RRL by setting the module globally or per zone.

mod-rrl:
  - id: default
    rate-limit: 200   # Allow 200 resp/s for each flow
    slip: 2           # Every other response slips

template:
  - id: default
    global-module: mod-rrl/default   # Enable RRL globally

Module reference

mod-rrl:
  - id: STR
    rate-limit: INT
    slip: INT
    table-size: INT
    whitelist: ADDR[/INT] | ADDR-ADDR ...

id

A module identifier.

rate-limit

Rate limiting is based on the token bucket scheme. A rate basically represents a number of tokens available each second. Each response is processed and classified (based on several discriminators, e.g. source netblock, query type, zone name, rcode, etc.). Classified responses are then hashed and assigned to a bucket containing number of available tokens, timestamp and metadata. When available tokens are exhausted, response is dropped or sent as truncated (see slip). Number of available tokens is recalculated each second.

Required

table-size

Size of the hash table in a number of buckets. The larger the hash table, the lesser the probability of a hash collision, but at the expense of additional memory costs. Each bucket is estimated roughly to 32 bytes. The size should be selected as a reasonably large prime due to better hash function distribution properties. Hash table is internally chained and works well up to a fill rate of 90 %, general rule of thumb is to select a prime near 1.2 * maximum_qps.

Default: 393241

slip

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 Nth 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 that all rate-limited responses will be dropped. The outbound bandwidth and packet rate will be strictly capped by the 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 that all rate-limited responses will 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 RRL.
  • Setting the value to 2 will cause that half of the rate-limited responses will be dropped, 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.

Default: 1

whitelist

A list of IP addresses, network subnets, or network ranges to exempt from rate limiting. Empty list means that no incoming connection will be white-listed.

Default: not set

stats — Query statistics

The module extends server statistics with incoming DNS request and corresponding response counters, such as used network protocol, total number of responded bytes, etc (see module reference for full list of supported counters). This module should be configured as the last module.

Note

Server initiated communication (outgoing NOTIFY, incoming *XFR,...) is not counted by this module.

Note

Leading 16-bit message size over TCP is not considered.

Example

Common statistics with default module configuration:

template:
  - id: default
    global-module: mod-stats

Per zone statistics with explicit module configuration:

mod-stats:
  - id: custom
    edns-presence: on
    query-type: on

template:
  - id: default
    module: mod-stats/custom

Module reference

mod-stats:
  - id: STR
    request-protocol: BOOL
    server-operation: BOOL
    request-bytes: BOOL
    response-bytes: BOOL
    edns-presence: BOOL
    flag-presence: BOOL
    response-code: BOOL
    request-edns-option: BOOL
    response-edns-option: BOOL
    reply-nodata: BOOL
    query-type: BOOL
    query-size: BOOL
    reply-size: BOOL

id

A module identifier.

request-protocol

If enabled, all incoming requests are counted by the network protocol:

  • udp4 - UDP over IPv4
  • tcp4 - TCP over IPv4
  • udp6 - UDP over IPv6
  • tcp6 - TCP over IPv6

Default: on

server-operation

If enabled, all incoming requests are counted by the server operation. The server operation is based on message header OpCode and message query (meta) type:

  • query - Normal query operation
  • update - Dynamic update operation
  • notify - NOTIFY request operation
  • axfr - Full zone transfer operation
  • ixfr - Incremental zone transfer operation
  • invalid - Invalid server operation

Default: on

request-bytes

If enabled, all incoming request bytes are counted by the server operation:

  • query - Normal query bytes
  • update - Dynamic update bytes
  • other - Other request bytes

Default: on

response-bytes

If enabled, outgoing response bytes are counted by the server operation:

  • reply - Normal response bytes
  • transfer - Zone transfer bytes
  • other - Other response bytes

Warning

Dynamic update response bytes are not counted by this module.

Default: on

edns-presence

If enabled, EDNS pseudo section presence is counted by the message direction:

  • request - EDNS present in request
  • response - EDNS present in response

Default: off

flag-presence

If enabled, some message header flags are counted:

  • TC - Truncated Answer in response
  • DO - DNSSEC OK in request

Default: off

response-code

If enabled, outgoing response code is counted:

  • NOERROR
  • ...
  • NOTZONE
  • BADVERS
  • ...
  • BADCOOKIE
  • other - All other codes

Note

In the case of multi-message zone transfer response, just one counter is incremented.

Warning

Dynamic update response code is not counted by this module.

Default: on

request-edns-option

If enabled, EDNS options in requests are counted by their code:

  • CODE0
  • ...
  • EDNS-KEY-TAG (CODE14)
  • other - All other codes

Default: off

response-edns-option

If enabled, EDNS options in responses are counted by their code. See request-edns-option.

Default: off

reply-nodata

If enabled, NODATA pseudo RCODE (RFC 2308#section-2.2) is counted by the query type:

  • A
  • AAAA
  • other - All other types

Default: off

query-type

If enabled, normal query type is counted:

  • A (TYPE1)
  • ...
  • TYPE65
  • SPF (TYPE99)
  • ...
  • TYPE110
  • ANY (TYPE255)
  • ...
  • TYPE260
  • other - All other types

Note

Not all assigned meta types (IXFR, AXFR,...) have their own counters, because such types are not processed as normal query.

Default: off

query-size

If enabled, normal query message size distribution is counted by the size range in bytes:

  • 0-15
  • 16-31
  • ...
  • 272-287
  • 288-65535

Default: off

reply-size

If enabled, normal reply message size distribution is counted by the size range in bytes:

  • 0-15
  • 16-31
  • ...
  • 4080-4095
  • 4096-65535

Default: off

synthrecord – Automatic forward/reverse records

This module is able to synthesize either forward or reverse records for a given prefix and subnet.

Records are synthesized only if the query can’t be satisfied from the zone. Both IPv4 and IPv6 are supported.

Example

Automatic forward records

mod-synthrecord:
  - id: test1
    type: forward
    prefix: dynamic-
    ttl: 400
    network: 2620:0:b61::/52

zone:
  - domain: test.
    file: test.zone # Must exist
    module: mod-synthrecord/test1

Result:

$ kdig AAAA dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test.
...
;; QUESTION SECTION:
;; dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. IN AAAA

;; ANSWER SECTION:
dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. 400 IN AAAA 2620:0:b61:100::1

You can also have CNAME aliases to the dynamic records, which are going to be further resolved:

$ kdig AAAA alias.test.
...
;; QUESTION SECTION:
;; alias.test. IN AAAA

;; ANSWER SECTION:
alias.test. 3600 IN CNAME dynamic-2620-0000-0b61-0100-0000-0000-0000-0002.test.
dynamic-2620-0000-0b61-0100-0000-0000-0000-0002.test. 400 IN AAAA 2620:0:b61:100::2

Automatic reverse records

mod-synthrecord:
  - id: test2
    type: reverse
    prefix: dynamic-
    origin: test
    ttl: 400
    network: 2620:0:b61::/52

zone:
  - domain: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa.
    file: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa.zone # Must exist
    module: mod-synthrecord/test2

Result:

$ kdig -x 2620:0:b61::1
...
;; QUESTION SECTION:
;; 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. IN PTR

;; ANSWER SECTION:
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. 400 IN PTR
                               dynamic-2620-0000-0b61-0000-0000-0000-0000-0001.test.

Module reference

mod-synthrecord:
  - id: STR
    type: forward | reverse
    prefix: STR
    origin: DNAME
    ttl: INT
    network: ADDR[/INT] | ADDR-ADDR ...

id

A module identifier.

type

The type of generated records.

Possible values:

  • forward – Forward records
  • reverse – Reverse records

Required

prefix

A record owner prefix.

Note

The value doesn’t allow dots, address parts in the synthetic names are separated with a dash.

Default: empty

origin

A zone origin (only valid for the reverse type).

Required

ttl

Time to live of the generated records.

Default: 3600

network

An IP address, a network subnet, or a network range the query must match.

Required

whoami — Whoami response

The module synthesizes an A or AAAA record containing the query source IP address, at the apex of the zone being served. It makes sure to allow Knot DNS to generate cacheable negative responses, and to allow fallback to extra records defined in the underlying zone file. The TTL of the synthesized record is copied from the TTL of the SOA record in the zone file.

Because a DNS query for type A or AAAA has nothing to do with whether the query occurs over IPv4 or IPv6, this module requires a special zone configuration to support both address families. For A queries, the underlying zone must have a set of nameservers that only have IPv4 addresses, and for AAAA queries, the underlying zone must have a set of nameservers that only have IPv6 addresses.

Example

To enable this module, you need to add something like the following to the Knot DNS configuration file:

zone:
  - domain: whoami.domain.example
    file: "/path/to/whoami.domain.example"
    module: mod-whoami

zone:
  - domain: whoami6.domain.example
    file: "/path/to/whoami6.domain.example"
    module: mod-whoami

The whoami.domain.example zone file example:

$TTL 1

@       SOA     (
                        whoami.domain.example.          ; MNAME
                        hostmaster.domain.example.      ; RNAME
                        2016051300                      ; SERIAL
                        86400                           ; REFRESH
                        86400                           ; RETRY
                        86400                           ; EXPIRE
                        1                               ; MINIMUM
                )

$TTL 86400

@       NS      ns1.whoami.domain.example.
@       NS      ns2.whoami.domain.example.
@       NS      ns3.whoami.domain.example.
@       NS      ns4.whoami.domain.example.

ns1     A       198.51.100.53
ns2     A       192.0.2.53
ns3     A       203.0.113.53
ns4     A       198.19.123.53

The whoami6.domain.example zone file example:

$TTL 1

@       SOA     (
                        whoami6.domain.example.         ; MNAME
                        hostmaster.domain.example.      ; RNAME
                        2016051300                      ; SERIAL
                        86400                           ; REFRESH
                        86400                           ; RETRY
                        86400                           ; EXPIRE
                        1                               ; MINIMUM
                )

$TTL 86400

@       NS      ns1.whoami6.domain.example.
@       NS      ns2.whoami6.domain.example.
@       NS      ns3.whoami6.domain.example.
@       NS      ns4.whoami6.domain.example.

ns1     AAAA    2001:db8:100::53
ns2     AAAA    2001:db8:200::53
ns3     AAAA    2001:db8:300::53
ns4     AAAA    2001:db8:400::53

The parent domain would then delegate whoami.domain.example to ns[1-4].whoami.domain.example and whoami6.domain.example to ns[1-4].whoami6.domain.example, and include the corresponding A-only or AAAA-only glue records.

Note

This module is not configurable.