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:
A simple DNS server: It will be used as a fallback resolver.
A DNS-over-HTTPS (from now on, “DoH”) server: It will be the main DNS connection.
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:
OpenSSL, for generating the certificates for DoH.
dnscrypt-wrapper
, for generating theEd25519
certificates required by DNSCrypt.dnsdist
itself.
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:
-- 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):
[Optional] Generate the CA key:
openssl genpkey -out CA.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096
[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"
Generate the domain key:
openssl genpkey -out doh.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096
Generate the domain CSR:
openssl req -out doh.csr -key doh.key -new \ -subj <subject-string-with-CN>
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:
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
Generate the temporary DNSCrypt key-pair certificates:
dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=dnscrypt-server.key
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 |
---|---|
|
matches anything with “ads.” prefix |
|
matches example.com and all names within that zone, like www.example.com |
|
identical to the above |
|
blocks only example.com |
|
matches any name containing that substring |
|
matches any name with the prefix “ads” followed by 0 or more digits |
|
matches any name with the prefix “ads” followed by 0 or more lowercase letters |
|
|
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:
If the IP address of the server is directly configured, it will be used (thus no query is needed).
If there is no IP address, the domain name is resolved using locally configured DNS resolvers, if any.
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:
block
IPv6 queries. Those queries will return a message indicating that the requests have been locally blocked.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:
Create the CA key, if required.
Derive the CA certificate, if required.
Generate the server key.
Generate the Certificate Signing Request (CSR) from the server key.
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:
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 to72h
(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-to720h
- 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:
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.
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:
Obtain the hash for the server(s): After downloading the certificate, issue the command
pki show certificate <file> tbs-hash
.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:
Grab the CA certificate from the upstream server.
Add the CA certificate to the trusted storage. This can be easily done by running:
admin@osdx# set system certificate running://<path-to-ca>
[Optional] Grab the upstream certificate from the DoH server.
[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).
[Optional] Get a valid IP server address to resolve the hostname when there are no resolvers available.
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:
Grab the master DNSCrypt public certificate (not the temporary one).
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:...
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.