========= DNS Proxy ========= .. sidebar:: Contents .. contents:: :depth: 2 :local: This chapter covers the configuration of :osdx:cfg:`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. - |dwrap|_, for generating the ``Ed25519`` 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: .. code-block:: lua :caption: |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=":", 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: .. code-block:: bash openssl genpkey -out CA.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096 #. [Optional] Derive the CA certificate from the CA key: .. code-block:: bash openssl req -out CA.crt -x509 \ -key CA.key -days 365 \ -subj "" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -addext "subjectKeyIdentifier=hash" #. Generate the domain key: .. code-block:: bash openssl genpkey -out doh.key -algorithm RSA -pkeyopt rsa_keygen_bits:4096 #. Generate the domain CSR: .. code-block:: bash openssl req -out doh.csr -key doh.key -new \ -subj #. Generate the signed certificate for the server: .. code-block:: bash # 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:,IP:" openssl x509 -req -out doh.crt -in \ -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:: |dwrap|_ is is required for this step. There are four steps required in this step: #. Generate the **master** DNSCrypt key-pair certificates: .. code-block:: bash 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: .. code-block:: bash dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=dnscrypt-server.key #. Generate the **temporary** DNSCrypt certificate: .. code-block:: bash 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 :osdx:cfg:`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 :osdx:cfg:`service dns static` values to cloak requests. However, this behavior can be disabled by setting the :osdx:cfg:`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: :osdx:cfg:`service dns proxy listen-address * port *` 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 :osdx:cfg:`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://:/ 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 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: .. code-block:: toml [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: .. code-block:: shell 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. .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell admin@osdx$ nslookup teldat.com Configuring DNS Forwarding and DNS Proxy at the same time --------------------------------------------------------- The :osdx:cfg:`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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: shell 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: .. code-block:: md # 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 minisign-key ``` Once everything is configured, the configuration to write at the DUTs would be: ``` set service dns proxy source url set service dns proxy source minisign-key # Optionally, configure a prefix set service dns proxy source prefix RD- # And instruct the proxy to use one of the defined servers (note the prefix) set service dns proxy server-name RD- ``` -- ## 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: .. code-block:: shell # 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 :osdx:op:`pki show certificate * tbs-hash`. #. Calculate the DNS stamp for the server: .. code-block:: shell admin@osdx$ service dns proxy stamp calculate dns-over-https host-name hash ip 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: .. code-block:: shell $ minisign -Slm .. 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: .. code-block:: shell 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: .. code-block:: shell admin@osdx# set system certificate running:// #. **[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: .. code-block:: shell admin@osdx# set service dns proxy server-name admin@osdx# set service dns proxy static protocol dns-over-https host name admin@osdx# set service dns proxy static protocol dns-over-https ip # If hash was obtained before, set it here admin@osdx# set service dns proxy static protocol dns-over-https 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: .. code-block:: shell admin@osdx$ service dns proxy dnscrypt public-key running:// 00:11:22:33:44:55:66:77:88:99:... #. Configure an OSDx device to use that DNSCrypt server: .. code-block:: shell admin@osdx# set service dns proxy server-name admin@osdx# set service dns proxy static protocol dns-crypt ip admin@osdx# set service dns proxy static protocol dns-crypt provider name admin@osdx# set service dns proxy static protocol dns-crypt provider public-key With that configuration, the proxy will connect against the DNSCrypt server. .. ######################################### ## Links with format or repeated links ## ######################################### .. |dnsdist| replace:: ``dnsdist`` .. _dnsdist: https://dnsdist.org .. |dwrap| replace:: ``dnscrypt-wrapper`` .. _dwrap: https://github.com/cofyc/dnscrypt-wrapper .. _OpenSSL: https://www.openssl.org/ .. |service| replace:: ``service dns proxy`` .. ############### ## Citations ## ############### ---- Citations ========= .. [1] DNSCrypt Public Blocklist file - https://download.dnscrypt.info/blacklists/domains/ .. [2] Automatic cipher suite ordering in crypto/tls - https://go.dev/blog/tls-cipher-suites#faqs .. [3] Uniform Resource Locators (URL) - https://www.rfc-editor.org/rfc/rfc1738 .. [4] OpenNIC servers - https://download.dnscrypt.info/dnscrypt-resolvers/v3/opennic.md .. [5] Parental control servers - https://download.dnscrypt.info/dnscrypt-resolvers/v3/parental-control.md .. [6] Public resolvers - https://download.dnscrypt.info/dnscrypt-resolvers/v3/public-resolvers.md .. [7] DNS Server Sources - https://github.com/DNSCrypt/dnscrypt-proxy/wiki/DNS-server-sources .. [8] Minisign - https://jedisct1.github.io/minisign/ .. [9] DNSCrypt Protocol Specification - https://dnscrypt.info/protocol/