DNS Proxy

This chapter covers the configuration of service dns proxy, which is the main element for securing DNS communications in OSDx routers.

The DNS proxy (from now on, “the proxy”) has two working modes: Client and server. The client mode is mandatory, it is, the proxy will not work if not configured. Meanwhile, the server mode is optional and requires further configuration to work (i.e.: deploy certificates, calculate the stamps, …).

In this chapter, both modes will be explained, as well as the procedure to generate all the needed certificates from within OSDx.

Note

While it is possible to generate the certificates from within the router, it is not the recommended way to do so: It is preferred to use a valid CA authority and the proper PKI infrastructure.

Glossary

Here, some concepts (or wording) are explained for the reader to know them. Those concepts might be already familiar. If so, consider going to Configuration or to the Examples.

  • Stamp: the stamp is a string representing a DNS resource. It has the following form: sdns://AABBCCDDEE.... In such a stamp all the required information for the DNS client to connect to a remote server is contained.

  • Minisign key: the minisign key is an Ed25519 key used to sign, and later on verify source lists. Refer to Source Lists for more information.

  • Master certificate: the master certificate is a long-term certificate used in DNSCrypt to derive short-lived server certificates. Clients use it to verify those short-lived certificates.

Configuration

It is assumed the remote server is known and can be manageable by the reader (at least, it accepts foreign connections). To get the most out of this documentation, three DNS servers will be required:

  1. A simple DNS server: It will be used as a fallback resolver.

  2. A DNS-over-HTTPS (from now on, “DoH”) server: It will be the main DNS connection.

  3. A DNSCrypt server: It will be used as another DNS provider.

Several DNS servers on the Internet even accept the three protocols. Those should be used when possible. A comprehensive list can be found at: https://dnscrypt.info/public-servers.

However, for demonstration purposes, here it is going to be explained how to deploy a simple dnsdist server with the three required protocols.

Note

The following step is optional. If external DNS servers are going to be used, feel free to skip to either the Examples or the Load Balancing Algorithms.

Deploying a dnsdist server

dnsdist is a free, open-source utility developed and maintained by PowerDNS. For deploying the dnsdist server, there are three needed tools:

The dnsdist server that is going to be deployed will have support for the aforementioned protocols and will also spoof some domains with specific IP addresses - the idea is to replicate the same setup as the one used in the demonstrations.

The configuration of the dnsdist server will look similar to this:

dnsdist server configuration example.
-- locally defined host entries
addAction("teldat.com", SpoofAction({"19.18.17.16"}, nil, {ttl=3600}), {name="teldat.com"})
addAction("teldat.com", SpoofAction({"ff00::dead:cafe"}, nil, {ttl=3600}), {name="teldat.com"})
addAction("remote.dns", SpoofAction({"10.215.168.1"}, nil, {ttl=3600}), {name="remote.dns"})
addAction("blocked-ip.net", SpoofAction({"10.215.168.42"}, nil, {ttl=3600}), {name="blocked-ip.com"})

-- allow queries from all IP addresses
addACL("0.0.0.0/0")

-- listen to DNS queries too
setLocal("0.0.0.0")

-- add a DoH resolver globally listening on port 443
addDOHLocal("0.0.0.0:443", "doh.crt", "doh.key")

-- add a DNSCrypt resolver listening on port 8443
addDNSCryptBind("0.0.0.0:8443", "2.dnscrypt-cert.remote.dns", "dnscrypt-server.crt", "dnscrypt-server.key")

-- downstream resolver, if any
newServer({address="<downstream-address>:<downstream-port>", qps=5, name="main-resolver"})

-- disable security status polling via DNS
setSecurityPollSuffix("")

Important

This server has no security constraints, hence it is not suitable for production use.

For the configuration above, at least two certificates shall be generated: One for DoH and the other one for DNSCrypt.

Note

The dnsdist server configuration already has some values filled. If the same names are used, it can be left as it is. Else, do not forget to adjust the values.

Generating the DoH certificate

Note

OpenSSL is required for this step.

There are five steps (two of them are optional):

  1. [Optional] Generate the CA key:

    openssl genpkey -out CA.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096
    
  2. [Optional] Derive the CA certificate from the CA key:

    openssl req -out CA.crt -x509 \
        -key CA.key -days 365 \
        -subj "<subject-string>" \
        -addext "basicConstraints=critical,CA:TRUE" \
        -addext "keyUsage=critical,keyCertSign,cRLSign" \
        -addext "subjectKeyIdentifier=hash"
    
  3. Generate the domain key:

    openssl genpkey -out doh.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096
    
  4. Generate the domain CSR:

    openssl req -out doh.csr -key doh.key -new \
        -subj <subject-string-with-CN>
    
  5. Generate the signed certificate for the server:

    # Store in a variable the certificate extensions
    CERT_EXTENSION="basicConstraints=CA:FALSE\n"
    CERT_EXTENSION+="extendedKeyUsage=serverAuth,clientAuth,codeSigning,emailProtection\n"
    CERT_EXTENSION+="keyUsage=critical,nonRepudiation,digitalSignature,keyEncipherment\n"
    CERT_EXTENSION+="subjectAltName=DNS:<domain>,IP:<ip>"
    
    openssl x509 -req -out doh.crt -in <path-to-DoH-CSR> \
        -CA CA.crt \
        -CAkey CA.key \
        -CAcreateserial \
        -extfile <(echo -ne "$CERT_EXTENSION") \
        -days 365
    

And it is done! Adjust the dnsdist server configuration with the correct path to the generated certificates.

Important

Alongside this step, notice that arbitrary names were used: CA.key, CA.crt, doh.key, doh.crt. One can opt-in to use those names, but it is suggested rather use appropriate unique names with the full path.

Generating the DNSCrypt certificates

Note

dnscrypt-wrapper is is required for this step.

There are four steps required in this step:

  1. Generate the master DNSCrypt key-pair certificates:

    dnscrypt-wrapper --gen-provider-keypair \
        --provider-name=2.dnscrypt-cert.remote.dns --ext-address=10.215.168.1 \
        --provider-publickey-file=dnscrypt-master.crt \
        --provider-secretkey-file=dnscrypt-master.key
    
  2. Generate the temporary DNSCrypt key-pair certificates:

    dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=dnscrypt-server.key
    
  3. Generate the temporary DNSCrypt certificate:

    dnscrypt-wrapper --gen-cert-file \
        --crypt-secretkey-file=dnscrypt-server.key \
        --provider-cert-file=dnscrypt-server.crt \
        --provider-publickey-file=dnscrypt-master.crt \
        --provider-secretkey-file=dnscrypt-master.key
    

And it is done! Adjust the dnsdist server configuration with the correct path to the generated certificates.

Important

Alongside this step, notice that arbitrary names were used: dnscrypt-master.key, dnscrypt-master.crt, dnscrypt-server.key, dnscrypt-server.crt. Moreover, a custom domain “remote.dns” and an internal IP address “10.215.168.1” were in use for customizing the scope of the certificates. It is required to change both domain and ip values with the appropriate ones. Additionally, it is suggested to use unique names for the certificates with their full path.

Load Balancing Algorithms

It is possible to configure multiple DNS servers. When the proxy starts, those servers are sorted from the quickest to the slowest, and that sorting will be used for load balancing the queries among them.

There are four strategies available:

  • first always picks the fastest server in the list.

  • p2 randomly picks a server among the two fastest.

  • ph randomly picks a server among the top fastest half of the list.

  • random just picks a server from the list.

By default, p2 strategy is used. Each time a query is made, the time it takes to complete is used to adjust the weights of the server: The faster the query is, the better the service thinks the server is.

When this operation is applied over time, each server will get compared to all the others. This way, the list is progressively kept sorted. If Source Lists are being used, very probably the servers are spread over the world. Hence, if the ph strategy is chosen, some queries will end up using slower servers. That is why p2 strategy is preferred.

Note

Before choosing a strategy, it is recommended to have a look at the response times of the servers to pick the better one. The response times can be seen in the journal by enabling the proxy logs.

Blocking Queries

The proxy can block queries based either on the domain being queried or the IP of the queried domain. Both options accept a regular expression as the value to match against.

The blocklists can be configured either by using files or by manually writing them. The first ones usually come from public blocklists, gathered from the Internet [1].

Examples of valid patterns are:

Pattern

Result

ads.*

matches anything with “ads.” prefix

*.example.com

matches example.com and all names within that zone, like www.example.com

example.com

identical to the above

=example.com

blocks only example.com

*sex*

matches any name containing that substring

ads[0-9]*

matches any name with the prefix “ads” followed by 0 or more digits

ads[a-z]*

matches any name with the prefix “ads” followed by 0 or more lowercase letters

ads*.example*

*, ? and [] can be used anywhere, but prefixes/suffixes are faster

The example.com (shorthand for *.example.com) is the most common pattern, as it will block the domain itself, but also names within that domain.

The filters are extremely fast matching prefixes (ads.*), suffixes (*.example.com) and exact matches (=example.com), even with a huge set of rules.

Important

Filters will also match aliases (CNAME pointers). Thus, www.nytimes.com does not have an IP address because it is an alias to nytimes.map.fastly.net. Therefore, blocking fastly.net will block *.fastly.net as well as all aliases pointing to it, like www.nytimes.com.

Blocking by IP will block query answers. They accept regular expressions too, but it is less common to use them. For example, matching 192.168.* will block all domains that return an IP address within that range.

Caching

The proxy has a built-in cache that can be enabled to keep the recent queries in memory: The first time a query is made, a network transaction is required. The second time, if the response is still valid, the query is immediately sent from memory.

Among other advantages, enabling the cache improves resiliency against network downtimes, drastically reduces latency, and enhances privacy as fewer queries are sent to third-party servers.

Several options can be configured:

  • max-negated-ttl: How long - at most in seconds - a not found entry is kept in memory.

  • max-ttl: How long, at most in seconds, a valid answer is kept in memory.

  • min-negated-ttl: How long, at minimum in seconds, a not found entry is kept in memory.

  • min-ttl: How long, at minimum in seconds, a valid answer is kept in memory.

  • size: How many entries are kept in memory.

Preferred Ciphers

If not configured, the proxy automatically chooses the best cipher based on the hardware characteristics of the device. Moreover, the cipher is chosen looking forward to the best security cipher. It is, devices with hardware support for an AES algorithm shall use AES encryption, whereas those that do not should use ChaCha20-Poly1305.

The ciphers are chosen in a particular order, defined through the CLI. Ideally, hardware-capable algorithms should appear first. The set of configured ciphers will define the set of preferred ciphers. However, if no of the ciphers are available the proxy service will start: It will only complain about none of the configured ciphers being suitable for the connectivity.

Warning

Configuring the ciphers will only take effect when the connection is secured, at most, with TLS v1.2. By default, the proxy tries to establish a TLS v1.3 connection with the remote host. In such a case, adjusting the ciphers will produce no effect.

The explanation is available in Golang’s TLS FAQs [2]. Citing:

Why leave enabled TLS 1.0-1.2 cipher suites configurable? There is a meaningful tradeoff between baseline security and legacy compatibility to make in choosing which cipher suites to enable, and that’s a choice we can’t make ourselves without either cutting out an unacceptable slice of the ecosystem, or reducing the security guarantees of modern users.

Related to the impossibility of choosing TLS v1.3 cipher suites, citing [2]:

Why not make TLS 1.3 cipher suites configurable? Conversely, there is no tradeoff to make with TLS 1.3, as all its cipher suites provide strong security. This lets us leave them all enabled and pick the fastest based on the specifics of the connection without requiring the developer’s involvement.

Cloaking

Cloaking is a more advanced version of service dns static. For example:

example.com 192.168.1.2

The above rule will make the proxy return 192.168.1.2 as a response to a query for example.com or even www.example.com.

Maybe the real IP address is totally different. However, the proxy will override it, and not even forward the query to the DNS resolver. The domain name does not even have to exist.

The cloaking feature accepts regular expressions for matching names. Those regular expressions follow the same rules as explained in Blocking Queries. The destination can be an IP address or another domain name.

Note

By using cloaking, it is possible to chain domains: Domain A points to B, B points to C and C resolves to an IP address. Therefore, A and B resolve to the same IP address.

When not configured, the cloaking feature will use service dns static values to cloak requests. However, this behavior can be disabled by setting the service dns proxy cloaking ignore-hosts path.

Disable Protocols

When using Source Lists, it is usual that there are a lot of servers, each of them even having multiple protocols. By default, plain DNS, DNS-over-TLS, and DNS-over-QUIC are disabled (because the proxy does not support them). Disabling protocols allows skipping servers that make use of such a protocol. It can be interesting in situations in which one protocol is blocked, when some operation is done with specific traffic, or when a specific protocol must be used.

Two protocols can be disabled: DoH and DNSCrypt. Therefore, servers that use a disabled protocol will not be used.

Note

It is possible to disable both protocols. However, no server will be usable

Fallback Resolver

The fallback resolver is a standard, non-encrypted DNS server that is used for resolving the configured DNS servers. They are useful in situations in which the remote server name is known but is not system-resolvable.

The proxy will try to resolve the remote address by iterating over these steps:

  1. If the IP address of the server is directly configured, it will be used (thus no query is needed).

  2. If there is no IP address, the domain name is resolved using locally configured DNS resolvers, if any.

  3. If there are no locally configured DNS resolvers, then the fallback resolvers are used.

Force TCP

The proxy will use automatically the most suitable protocol for the connection, prioritizing speed. Hence, UDP is usually the chosen one. However, by enabling this option all upstream connections will be done using TCP, and it will be impossible to use a UDP-only server.

This can be useful in situations in which the remote server is known to handle only TCP connections, to avoid sending it via UDP communications.

IPv6 Blocking Options

By default, IPv6 queries are handled properly: They are forwarded to the upstream server. However, there might be no IPv6 connection available. In that case, IPv6 communications can be blocked in two ways:

  1. block IPv6 queries. Those queries will return a message indicating that the requests have been locally blocked.

  2. do-not-query IPv6-only servers. The servers that are accessible only through IPv6 will be skipped.

HTTP Keep-Alive

Some of the DNS encryption methods are using HTTP underneath. This setting configures the keep-alive for the HTTP connections (by default, 30s).

Listen Address

The proxy will listen by default to all queries made on port 53. However, sometimes it is interesting to listen to queries on a specific address or a different port.

This setting allows configuring from one to N listen addresses for the service. Valid values are IPv4 and IPv6 addresses. When address :: is in use, the proxy will listen to all addresses (in both IPv4 and IPv6). If the address is 0.0.0.0 the proxy will listen to all IPv4 addresses.

Afterward, each address can have its port to listen to.

If nothing is configured, the proxy will listen to all IPv4/IPv6 communications done on port 53 (equivalent to having set: service dns proxy listen-address <ipv4|ipv6> port <u32> with port 53).

Important

There may be some conflicting services, like service dns forwarding, as both of them by default listen on all IPv4/IPv6 addresses on port 53. Both services have a check that ensures no collision exists between them. However, keep this consideration in mind when configuring both services.

Logging

By default, the proxy logs are disabled to keep the journal clear. However, they can be enabled at any time with the command service dns proxy log.

There are 6 levels available:

  • 0 contains all kinds of debug information, which is not usually useful.

  • 1 contains messages with “informational” severity or higher.

  • 2 contains messages with “notice” severity or higher.

  • 3 contains messages with “warning” severity or higher.

  • 4 contains messages with “error” severity or higher.

  • 5 contains messages with “critical” severity or higher.

  • 6 contains messages with “fatal” severity.

By default, 2 (notice) is the default log level.

Server Requirements

When working with Source Lists, there are some servers that perform arbitrary operations over the traffic. Some of those operations imply storing logs of the ongoing queries; Others imply blocking traffic to certain sites, such as parental control or ad blocking.

It is possible to instruct the proxy to avoid such a server when certain conditions are not met. In particular:

  • dnssec: The remote DNS servers must support DNS Security Extensions.

  • no-filter: The remote DNS server must not enforce its own blocklist.

  • no-logs: The remote DNS server must not log user queries.

Danger

The remote servers do not need to declare what they do with the data or tell the truth. Always use trusted and verified sources.

Built-in Server

The built-in server is one of the top features of the DNS proxy service. It adds the ability not only to connect to a remote DNS server but to host a DoH server on the router.

To deploy a DoH server on the router, server certificates are needed. For that, there are five steps to follow:

  1. Create the CA key, if required.

  2. Derive the CA certificate, if required.

  3. Generate the server key.

  4. Generate the Certificate Signing Request (CSR) from the server key.

  5. Sign the server certificate using the CSR, the server key, the CA, and the CA key.

Tip

For a full example of how to deploy a DoH server on the router, please refer to Statically defining a DNS server.

Notice that the first two steps are optional: They depend directly if there already are both CA key and CA certificate (for example, from a certificate authority).

On the other hand, the server is somewhat customizable. By default, it will listen to all incoming connections on port 3000. However, this can be changed to any specific address and port. It follows the same format as the one explained in Listen address. Please, refer to such a section for more information.

Besides, the path is also customizable. By convention, /dns-query is used. However, it can be changed to any other value conforming the RFC1738 [3].

Once everything is configured, the URL will look as follows:

https://<listen-address>:<port>/<path>

Remote Servers

The remote servers refer to any upstream server the proxy connects to forward the queries using a secure connection. Currently, remote servers are the ones that support at least DoH or DNSCrypt protocols.

The remote servers can be built from two sources:

  • A source list.

  • A static source.

For the first one, a lookup over the source is required. Notice that lists can have prefixes, and that prefix must be prepended to the server name.

For the second one, the entries defined at Static Sources will be suggested and are the only acceptable ones.

Note

It is possible to configure a server that exists neither on the list nor on the static sources. However, the service will complain during the start, and the commit will fail.

Source Lists

The source lists are a useful resource when dealing with multiple remote servers, as they concentrate in a standardized way up to N servers, each of them with its characteristics.

When using a list, the proxy will take care of keeping it synchronized with the remote provider, therefore updating the servers when needed.

There are many source lists available on the Internet that are free to be used, updated regularly, and created using trusted sources. Some of them are:

  • OpenNIC servers [4].

  • Parental control servers [5].

  • Public resolvers [6].

Note

Those sources have been extracted from the “DNS server sources” wiki page [7]. Consider having a look at it for obtaining the minisign_key as well as to gather more information or check for new sources.

There may be noticed that every list has the so-called “minisign key”. Such a key conforms the security standard for every user to know whether a source is correct or if it has been compromised: Every source is signed with the proper key, and the minisign key is used by clients to verify its integrity.

The main point of source lists is to avoid placing the same stamp on multiple clients. Even if there is only one usable, internal DNS server, it is better to deploy a source list containing up-to-date information about such a server, and let the clients use that source list to configure the upstream server: As the clients check regularly for updates on the lists if there are any changes on the upstream server, they will be propagated automatically to all clients.

Tip

For an example of how to create your source list, refer to How to create your source list.

There are other customization options for source lists:

  • prefix, which prepends the given value to every source in the list. For example, if the prefix is “RD-”, for a server with the name “teldat” the name to set at Remote Servers would be: RD-teldat. It is used to avoid collisions between multiple lists that may contain the same server names.

  • refresh-delay, which configures the hours a list is cached before being refreshed. By default, it is set to 72h (3 days). This setting should be adjusted accordingly to the updated frequency of the in-use lists. If a list refers to servers that change with a very low frequency (long time periods), this value might be set higher (up-to 720h - 30 days).

Static Sources

The proxy’s static sources add the ability to manually configure remote servers without the need of having a source list. Such a remote server can be configured in two ways:

  • If there is a stamp, by configuring the remote server’s stamp.

  • If there is no such a stamp, by configuring manually the protocol for the remote server.

Tip

Stamps can be calculated directly using the CLI. The operational command service dns proxy stamp does everything required.

The supported protocols are dns-crypt and dns-over-https. The first one requires the remote IP address and the provider data - consisting of the provider’s name and public key.

Tip

The public key information can be extracted by using the CLI operational command service dns proxy dnscrypt.

The latter requires only the host name information. However, other fields should be filled to provide better security, like the hash or the ip. The hash consists of a set of SHA256 hashes of one or more certificates in the TBS chain. It is used to ensure the server information is correct and there is no man-in-the-middle attack. The ip field is used to access the host name without needing to resolve it using a plain DNS query.

Tip

The hash of a certificate can be extracted by using the CLI operational command pki show certificate, with the tbs-hash flag enabled (like pki show certificate <path> tbs-hash).

Once all the static sources are configured, they can be used in Remote Servers. Notice that both static sources and source lists can be used together - just keep in mind the naming of the servers.

Warning

If an unsupported protocol is used - when using a stamp -, the validation for the field will fail.

Query Timeout

The query timeout defines the time to wait for a DNS query response (in milliseconds). This setting is usually adjusted if the network has a lot of latency, where usually the value is increased. However, take into account that a higher value implies a greater service startup time, so tweak it carefully.

By default, it is set to 5000 milliseconds.

Whitelisting Queries

The whitelist option does exactly the opposite of Blocking Queries command: Allowing names or IPs. Nevertheless, whitelisting only makes sense when blocklists are configured, as they are not an inverse list on their own: If a domain is whitelisted but there is no blocklist, then all domains are whitelisted.

Whitelists are extremely useful when working with remote blocklists, in which thousands of domains are blocked. Whitelists allow permitting a domain or an IP without needing to adjust the blocklist.

Note

Whitelists follow the same regular expressions as the ones explained at Blocking Queries. Please, refer to such a section for more information.

Examples

Configuring a remote source list

There are on the Internet several source lists that expose different providers offering DNS resolution. In this example, the list “public resolvers” [6] is going to be used, and the proxy is going to be configured to use cloudflare as a resolver.

The first thing to locate when working with the source list is the minisign-key. Such a key will be used to ensure the source list is correct and that it was not modified by an attacker.

Usually, the source lists contain a field at the top of it where the configuration is placed. In this case, for the public resolvers list [6], the configuration looks as follows:

[sources.'public-resolvers']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
cache_file = 'public-resolvers.md'

Analyzing such a configuration, the interesting fields are:

  • urls, containing a list of URLs where the list is available at.

  • minisign_key, containing the representation of the key used to sign the list.

For instance, the configuration to set at an OSDx device would be:

admin@osdx# set service dns proxy source public-resolvers url https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md
admin@osdx# set service dns proxy source public-resolvers url https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md
admin@osdx# set service dns proxy source public-resolvers minisign-key RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3

Tip

While it is not necessary, it is suggested to define a prefix per source list. This way, the in-use servers can be easily identified by such a prefix.

Additionally, the refresh delay as well as a prefix are going to be set. Having a look at the list source, it is updated with a frequency of once a week. Therefore, the refresh delay is going to be set to such a value - in this case, 168 hours.

admin@osdx# set service dns proxy source public-resolvers refresh-delay 168
admin@osdx# set service dns proxy source public-resolvers prefix pr-

Finally, for configuring the wanted server (in this example, cloudflare), the following lines must be set:

admin@osdx# set service dns proxy server-name pr-cloudflare
admin@osdx# commit

Important

Notice how the prefix pr- was prepended to the server name (cloudflare). If no prefix would have been set, the server name would simply be cloudflare.

Once everything is configured, it can be checked the correct behavior by issuing the next command:

admin@osdx$ nslookup teldat.com

Configuring DNS Forwarding and DNS Proxy at the same time

The service dns forwarding can be used alongside the proxy. However, depending on the configuration, it is usually necessary to specify different listen addresses for them to not collide.

Note

While the proxy can do most of the operations of the DNS forwarding, it can be interesting to have them work together to have a single caching system, to have a non-encrypted DNS resolver, to define DNS records on an OSDx device, to encrypt Internet DNS queries, etc. Both are powerful tools that together make the most out of the entire DNS system.

The usual operation basis is to adjust the proxy to listen on a specific port and configure the DNS forwarding to forward DNS queries to such an address. In this example, the proxy is going to be configured on port 5353, and will listen on a dummy local interface so it will not receive traffic from external services.

Note

Having the proxy listening on a dummy interface is not required: The only purpose is to isolate the service from external traffic.

The configuration to be set on the OSDx device would be:

admin@osdx# set interfaces dummy dum0 address 10.0.0.1/24
admin@osdx# set service dns proxy listen-address 10.0.0.1 port 5353
# The rest of the configuration for the proxy would be here - refer to
# the example "Configuring a remote source list" on a basic, working
# configuration.
admin@osdx# set service dns forwarding name-server 10.0.0.1 port 5353
# Prefer this server, when available.
admin@osdx# set service dns forwarding name-server 10.0.0.1 priority 0
# Backup plain DNS server, when DNS proxy is not available.
admin@osdx# set service dns forwarding name-server 1.1.1.1 priority 1
# Configure the system to locally resolve DNS queries. See
# "Locally resolving DNS queries" for more information.
admin@osdx# set service dns resolver local
admin@osdx# commit

Once everything is configured, do a query to any address (like teldat.com). That would dump stats like the following:

admin@osdx$ service dns forwarding show statistics

----------------------------------------------------
            Cache Statistics                 Value
----------------------------------------------------
Cache size:                                    150
Queries forwarded:                             5
Queries answered locally:                      0
Total DNS entries inserted into cache:         18
DNS entries removed from cache before expiry:  0


-----------------------------------------
Nameserver Statistics         Value
-----------------------------------------
Server:                     1.1.1.1
Queries sent:               4
Queries retried or failed:  0
Server:                     10.0.0.1
Queries sent:               4
Queries retried or failed:  0

As it can be seen, the queries are forwarded to 1.1.1.1 when the proxy is not ready yet, but then forwarded to 10.0.0.1 when the proxy is up and running.

Deploying a DoH Server locally

The proxy has the ability to deploy a DoH server at the same time it is acting as a client, thus other clients can connect to such a server.

The configuration of the DoH server can be slightly tricky because several certificates are needed. Two of them can be optional, which are the CA certificates when a trusted CA provider is in use. Nevertheless, in this example, the full process is going to be explained.

First, it is needed to generate the keys: CA.key and server.key. Those keys will be the ones used to sign the certificates:

admin@osdx$ pki generate private-key running://CA.key rsa bits 4096
Generated private key at 'running://CA.key'
admin@osdx$ pki generate private-key running://server.key rsa bits 4096
Generated private key at 'running://server.key'

Second, the CA.crt should be generated. That certificate must contain all the needed extensions to be considered a CA by clients. Moreover, some parameters should be adjusted to fit the company requirements, like the subject. In this example, a simulated Teldat CA is going to be generated:

admin@osdx$ pki generate certificate running://CA.crt x509 days 3600 subject "/C=ES/ST=Madrid/L=Tres Cantos/O=Teldat S.A./OU=RD/CN=Teldat Root CA" private-key running://CA.key extension basicConstraints=critical,CA:TRUE extension keyUsage=critical,keyCertSign,cRLSign extension subjectKeyIdentifier=hash
Generated X.509 certificate at 'running://CA.crt'

The extensions given to the command instruct the underlying PKI structure to prepare a certificate that is a CA that can be used for signing.

Third, it is necessary to generate the certificate for the server. In this case, an intermediate CSR is needed, containing all the basic information. Then, generating the final certificate requires adjusting the subject and the extensions to reflect the domain name and IP address the certificate is going to be deployed at:

admin@osdx$ pki generate csr running://server.csr subject "/C=ES/ST=Madrid/L=Tres Cantos/O=Teldat S.A./OU=RD/CN=dns.dut0" private-key running://server.key
Generated certificate signing request at 'running://server.csr'
admin@osdx$ pki sign running://server.crt certificate running://CA.crt csr running://dns.dut0.csr private-key running://CA.key days 3600 extension basicConstraints=CA:FALSE extension extendedKeyUsage=serverAuth,clientAuth,codeSigning,emailProtection extension keyUsage=critical,nonRepudiation,digitalSignature,keyEncipherment extension subjectAltName=DNS:dns.dut0,IP:10.215.168.10
Generated signed certificate at 'running://dns.dut0.crt'

Once all the certificates have been generated, the configuration for the proxy is straightforward:

admin@osdx# set service dns proxy server cert file 'running://dns.dut0.crt'
admin@osdx# set service dns proxy server cert key 'running://server.key'

Note

The above configuration represents the minimal settings for deploying a DoH server on a device. The remaining proxy configuration must be set for it to work properly.

For configuring another OSDx host to connect against the just configured one, first it is necessary to grab the hash for the server key:

admin@osdx$ pki show certificate running://dns.dut0.crt tbs-hash
aa:bb:cc:dd:ee:ff:...

Then, on the other DUT (let’s say, DUT1), the configuration for the remote server can be done using statically defined entries:

admin@DUT1# set service dns proxy server-name INTERNAL_DOH
admin@DUT1# set service dns proxy static INTERNAL_DOH protocol dns-over-https host name dns.dut0
admin@DUT1# set service dns proxy static INTERNAL_DOH protocol dns-over-https host port 3000
admin@DUT1# set service dns proxy static INTERNAL_DOH protocol dns-over-https host ip 10.215.168.10
admin@DUT1# set service dns proxy static INTERNAL_DOH protocol dns-over-https hash aa:bb:cc:dd:ee:ff:...

Warning

When working with self-signed certificates, it is necessary to add the CA certificate to the system trusted storage. This can be done by copying such a certificate into the destination host and issuing the following command:

admin@DUT1# set system certificate trust running://CA.crt

This will allow the client to connect to a remote host that ciphers the communication with a certificate signed by that CA.

How to create your source list

When deploying a custom set of upstream servers, it is really useful to have a source list in which all the resources are placed. The source lists offer an automatic synchronization mechanism for all available clients. Moreover, they are both easy to create and to update.

Note

For more information about source lists, please refer to Source Lists section.

In this example, the process of creating a source list is explained. The example will use the following template as the base:

# RD-resolver

This file may contain several servers available (there is no limit at all). The format to
be followed is:

1. A **title** containing the name identifying the server, like:

    `## server-name`

2. A set of comments, if needed, with no prefix at all ending with two newlines:

    ```
    First comment

    Second comment

    ...

    'N' comment
    ```

3. A **final line** containing the DNS Stamp for the just described server:

    `sdns://AABBCCDDEEFFGG-...`

    DNS Stamps can be generated either by using the
    [online tool](https://dnscrypt.info/stamps/) or by running a
    client-available application.


Once the servers are configured, it is time to sign the resolver. A tool
called `minisign` is needed. Such a tool is available on the Internet,
accessible through: https://jedisct1.github.io/minisign/

The general idea involves three steps:

1. Create a pair of keys (`minisign -G`).
2. Note the generated **public key** value (something like:
    `RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3`).
3. Create a **signature file** for the resolvers list, with extension
    `.minisig` (`minisign -Sm`). Optionally, some comments may be added.

The signature file **must** be accessible** from the outside, located at
the same level as the resolvers list itself: The DNS proxy application
will fetch the resources and verify them when configuring.

**Note**: use the **already noted** public key when configuring the
source as the `minisign-key` parameter:

```
set service dns proxy source <name> minisign-key <noted-key>
```

Once everything is configured, the configuration to write at the DUTs would
be:

```
set service dns proxy source <name> url <public-location(s)>
set service dns proxy source <name> minisign-key <noted-key>
# Optionally, configure a prefix
set service dns proxy source <name> prefix RD-
# And instruct the proxy to use one of the defined servers (note the prefix)
set service dns proxy server-name RD-<server-name>
```

--

## rd-server

Private, LAN available, DoH server forwarding DNS requests.

Does block nothing. No persistent logs. No DNSSEC. No EDNS Client-Subnet.

{{STAMP}}

That Markdown file (md) contains all the basic structures for source lists. Note the two sections of it:

  1. A “prelude”, where some general instructions regarding the source list are given. There, in the example, how to structure source lists, generate the keys and more are explained.

  2. The current set of servers belonging to such a list, separated by -- from the prelude. There, in the example, the servers are listed (prepended by ##) and some extra information about them - which is optional. The required line is the latest one - {{STAMP}}, the stamp containing all the information on how to connect to the server.

First, the Minisign keys must be created [8]. For doing so, the following command must be run:

# The key is created without a password - remove "-W" for encrypting it
$ minisign -WG

The generated keys are placed at minisign.pub and ~/.minisign/minisign.key. The location can be changed with options -p and -s, respectively.

Second, the stamp for the server must be calculated. It can be done using a standard Linux CLI or with an OSDx device. The latter one is going to be demonstrated:

  1. Obtain the hash for the server(s): After downloading the certificate, issue the command pki show certificate <file> tbs-hash.

  2. Calculate the DNS stamp for the server:

    admin@osdx$ service dns proxy stamp calculate dns-over-https host-name <domain> hash <hash> ip <server-address> <options>
    sdns://AABBCCDDEE...
    

    Several options allow configuring the stamp (like DNSSEC, filtering, etc.) which can be tweaked based on the server configuration.

Third, replace the placeholder {{STAMP}} with the just calculated one.

Finally, sign the source list with the minisign key:

$ minisign -Slm <source-list.md>

Note

If the key was moved to another location, use the -s option to specify key location.

The source list and the signature must be placed at a location accessible from the routers that are going to use such a list. Follow the example Configuring a remote source list for instructions on how to configure the proxy to use remote lists.

Locally resolving DNS queries

Configuring the proxy does not imply the DNS requests made locally are going to be resolved against such a proxy: It is necessary to ask the system to do so.

The process is pretty straightforward: Configure the proxy as usual and configure the resolver to work locally, by issuing:

admin@osdx# set service dns resolver local

After committing the configuration, every request that needs to resolve a domain name will be routed to the proxy. This setting will work also if DNS forwarding was configured.

Note

The local resolver will make sure that at least the proxy or the forwarder is configured. If not, it will be impossible to commit the configuration.

Statically defining a DNS server

The static entries allow configuring different upstream servers to forward the queries to. The proxy can handle both DoH and DNSCrypt servers - the other protocols are not compatible yet.

In this example, how to configure each of those is about to be explained.

Upstream DoH Server

The DoH protocol works against a standard HTTPS server, usually listening on port 443. The protocol is encrypted using TLS and requires multiple certificates to be deployed for encrypting the communication.

In this example, a self-signed certificate is going to be used. Therefore, the system will need to trust the CA certificate that signed it.

The steps to follow are:

  1. Grab the CA certificate from the upstream server.

  2. Add the CA certificate to the trusted storage. This can be easily done by running:

    admin@osdx# set system certificate running://<path-to-ca>
    
  3. [Optional] Grab the upstream certificate from the DoH server.

  4. [Optional] Obtain the hash for such a certificate. Later on, it will be used to validate the upstream server’s identity (there is no MiM).

  5. [Optional] Get a valid IP server address to resolve the hostname when there are no resolvers available.

  6. Configure an OSDx device to use those settings:

    admin@osdx# set service dns proxy server-name <name>
    admin@osdx# set service dns proxy static <name> protocol dns-over-https host name <domain>
    admin@osdx# set service dns proxy static <name> protocol dns-over-https ip <server-address>
    # If hash was obtained before, set it here
    admin@osdx# set service dns proxy static <name> protocol dns-over-https hash <hash>
    

This way, OSDx is ready to use that DoH server. In the next section, how to configure DNSCrypt is explained.

Upstream DNSCrypt Server

The DNSCrypt protocol works using standard HTTPS, usually listening on port 443. The protocol is encrypted using TLS and requires Ed25519 keys for securing the communication. There are several key differences with DoH [9]:

  • It was designed to secure DNS communications, whereas DoH was a “patch” over HTTPS communications for protecting the traffic.

  • It is designed to work over TCP or UDP.

  • It works on standard port 443, which is hardly blocked.

  • It uses strong encryption standards, using elliptic curves such as Ed25519.

  • It has long-lived certificates that are used to generate short-lived ones, safer to use because they cannot be compromised.

Once the server is configured (see Configuration for more details), the steps to follow are:

  1. Grab the master DNSCrypt public certificate (not the temporary one).

  2. Obtain the public key from such a certificate, by issuing:

    admin@osdx$ service dns proxy dnscrypt public-key running://<master-cert>
    00:11:22:33:44:55:66:77:88:99:...
    
  3. Configure an OSDx device to use that DNSCrypt server:

    admin@osdx# set service dns proxy server-name <name>
    admin@osdx# set service dns proxy static <name> protocol dns-crypt ip <server-ip>
    admin@osdx# set service dns proxy static <name> protocol dns-crypt provider name <server-provider>
    admin@osdx# set service dns proxy static <name> protocol dns-crypt provider public-key <public-key>
    

With that configuration, the proxy will connect against the DNSCrypt server.


Citations