Setting up multiple instances of dnscrypt-proxy with dnscrypt-proxy-multi

DNSCrypt has become the most ideal solution for having secure DNS resolving sessions. The protocol has not been submitted to IETF, but a lot of people has already started using it, and multiple client and server implementations already exist.

DNSCrypt is simply a protocol that allows authentication and encryption between two parties - the service which most of the time is the resolver, and the client which requests for domain names to be resolved. Rather than creating a local server of named or unbound that recursively resolves domain names in the open, it would be more practical to just rely on another public service that supports DNSCrypt, since traffic with a normal DNS protocol is open to eavesdropping (from unwanted parties like your ISP), and forgery. Note that even DNSSec is not encrypted, and that not all domain names support DNSSec. You also have to consider the latency when a local DNS server resolves things manually. You might also consider connecting through Tor, but Tor generally does not support UDP, and falling back to TCP may not always be allowed by DNS servers. The latency even becomes worse with Tor.

So DNSCrypt is indeed the most practical solution, but relying on free public services won’t guarantee uptime and reliability. Some services become inaccessible or unresponsive from time to time. Some also become unable to resolve domain names. This is why I came up with the idea of using multiple instances of dnscrypt-proxy to connect to those services, so one service can fill up the absence of another once they go offline.

dnscrypt-proxy-multi is a Ruby script that I created to do the task. It extracts entries from a database or resolvers list, and then checks those entries if the services they point to is available, i.e., if the port the service listens to is open.

[ dnscrypt-proxy netstat ]
Netstat output showing instances of dnscrypt-proxy

After discovering the services, dnscrypt-proxy-multi creates the instances of dnscrypt-proxy that would connect to those services, in the order of how fast the services have responded from the port check. Once an instance of dnscrypt-proxy is created, it can be optionally checked for its capability to resolve specific domain names through the --resolver-check option. If an instance fails to resolve a domain name, it gets terminated, and the service that it has connected to would be ignored.

Once all the required instances are up, dnscrypt-proxy-multi goes idle and just waits for all of them to terminate.

Installing dnscrypt-proxy-multi

dnscrypt-proxy-multi can be downloaded using the browser, or by using curl or wget:

curl -O https://raw.githubusercontent.com/konsolebox/scripts/master/dnscrypt-proxy-multi.rb
wget https://raw.githubusercontent.com/konsolebox/scripts/master/dnscrypt-proxy-multi.rb

After downloading, you can install it to /usr/local/bin:

install -m 0755 dnscrypt-proxy-multi.rb /usr/local/bin/dnscrypt-proxy-multi

If you’re using Gentoo, you can install the konsolebox overlay through layman:

layman -a konsolebox

Or by placing konsolebox.conf to /etc/portage/repos.conf/:

curl -o /etc/portage/repos.conf/konsolebox.conf \
	https://raw.githubusercontent.com/konsolebox/overlay/master/konsolebox.conf.example

The file should contain something like this:

[konsolebox]
auto-sync = yes
location = /var/local/overlays/konsolebox
masters = gentoo
sync-type = git
sync-uri = git://github.com/konsolebox/overlay.git

You can change location to refer a different directory, and also sync-uri to use https:// instead of git://.

After checking the file, you can update the repositories:

emerge --sync

And then add net-dns/dnscrypt-proxy-multi ** to /etc/portage/package.keywords. Example:

echo 'net-dns/dnscrypt-proxy-multi **' > \
		/etc/portage/package.keywords/net-dns.dnscrypt-proxy-multi

Then install the package like the usual emerge method:

emerge net-dns/dnscrypt-proxy-multi

Required version of dnscrypt-proxy

Most of dnscrypt-proxy-multi’s functions work well with the latest release version of dnscrypt-proxy which as of this writing is 1.6.1, but the --dnscrypt-proxy-syslog feature requires a new function which is currently only available in the latest source code of dnscrypt-proxy, which is in its own repository in GitHub.

Installing the newest version from source would depend on the distro, but if you’re using Gentoo, you can just follow the steps above. Just target dnscrypt-proxy, instead of dnscrypt-proxy-multi.

Also check if 1.6.2, or a newer version is already available,

Usage

Here’s the output of dnscrypt-proxy-multi --help:

dnscrypt-proxy-multi 2016-06-01
Runs multiple instances of dnscrypt-proxy.

Usage: /usr/bin/dnscrypt-proxy-multi [options]

Options:
-c, --resolver-check=FQDN[,FQDN2][/TIMEOUT[/WAIT]]
    Check instances of dnscrypt-proxy if they can resolve all specified FQDN
    and replace them with another instance that targets another resolver entry
    if they don't.  Default timeout is 5.0.  Default amount of wait-time to
    allow an instance to load and initialize before checking it is 0.1.
-C, --change-owner=USER[:GROUP]
    Change ownership of directories to specified user-group before opening files
    instantiating dnscrypt-proxy's, and dropping privilege to a user if
    configured.
-d, --dnscrypt-proxy=PATH
    Set path to dnscrypt-proxy executable.
    Default is "/usr/sbin/dnscrypt-proxy".
-D, --instance-delay=SECONDS
    Wait SECONDS seconds before creating the next instance of dnscrypt-proxy.
    Default is 0.0.
-g, --group=GROUP
    Drop priviliges to GROUP before creating instances of dnscrypt-proxy.
-G, --debug
    Show debug messages.
-i, --local-ip=RANGE
    Set range of IP addresses to listen to.  Default is "127.0.100.1-254".
    Example: "127.0.1-254.1-254,10.0.0.1"
-I, --ignore-ip-format
    Do not check if a local IP address starts or ends with 0 or 255.
-l, --log [LOG_DIR]
    Enable logging files to LOG_DIR.
    Default directory is "/var/log/dnscrypt-proxy-multi".
-L, --log-level=LEVEL
    When logging is enabled, tell dnscrypt-proxy to use log level LEVEL.
    Default level is 6.  See dnscrypt-proxy(8) for info.
-m, --max-instances=N
    Set maximum number of dnscrypt-proxy instances.  Default is 10.
-o, --log-output=FILE
    When logging is enabled, write main log output to FILE.
    Default is "<LOG_DIR>/dnscrypt-proxy-multi.log".
-O, --log-overwrite
    When logging is enabled, do not append output to main log-file.
-p, --local-port=RANGE
    Set range of ports to listen to.
    Default is "53".  Example: "2053,5300-5399"
-r, --resolvers-list=PATH
    Set resolvers list file to use.
    Default is "/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv".
-R, --resolvers-list-encoding
    Set encoding of resolvers list.  Default is "utf-8".
-s, --port-check-async=N
    Set number of port-check queries to send simultaneously.  Default is 10.
-S, --syslog [PREFIX]
    Log messages to system log.  PREFIX gets inserted at the beginning of every
    message sent to syslog if it's specified.  See also -Z.
-t, --port-check-timeout=SECONDS
    Set timeout when waiting for a port-check reply.  Default is 5.0.
-u, --user=USER
    Drop priviliges to USER before creating instances of dnscrypt-proxy.
    Note that this might prevent dnscrypt-proxy from being able to listen to
    ports lower than 1024.
-U, --dnscrypt-proxy-user=USER
    Tell dnscrypt-proxy to drop privileges as USER.
    Please consider that this may or may not work with --user.
-v, --verbose
    Show verbose messages.
-V, --version
    Show version and exit.
-w, --wait-for-connection=HOST[:PORT][,HOST2[:PORT2][,...]]
    Wait until any of the specified hosts acknowledges connection, or responds
    with an ICMP Echo reply if no port is specified.  Checking with ICMP needs
    net-ping gem, and requires root/administrative privileges.
-W, --write-pids [DIR]
    Enable writing PID's to DIR.
    Default directory is "/var/run/dnscrypt-proxy-multi".
-Z, --dnscrypt-proxy-syslog [PREFIX]
    Tell dnscrypt-proxy to log messages to system log.  It is automatically
    configured to have a prefix of '[REMOTE_IP:PORT]'. If PREFIX is specified,
    it is added to it with a space as a separator.
    Note that this disables file-logging in dnscrypt-proxy.
-h, --help
    Show this help info and exit.

Notes:
* Directories are automatically created recursively when needed.
* Services are checked with TCP ports since TCP is a common fallback.
* Local ports are first used up before the next IP address in range is used.
* Local ports are not checked if they are currently in use.
* Names of log files are created based on the remote address, while names of
  PID files are based on the local address.
* dnscrypt-proxy creates files as the calling user; not the one specified with
  --user, so changing ownership of directories and existing files may not be
  necessary.

Simple Usage Example

Here’s a simple example of using dnscrypt-proxy-multi:

dnscrypt-proxy-multi --local-ip=127.0.0.1 --local-port=5300-5309 \
		--dnscrypt-proxy-user=dnscrypt --syslog --dnscrypt-proxy-syslog

The command specifies that port 5300 to 5309 will be used by dnscrypt-proxy to listen to for connections, and that dnscrypt-proxy will drop its privilege to user dnscrypt, and do chroot(2) to its home directory. See --user option in dnscrypt-proxy(8).

Both --syslog and --dnscrypt-proxy-syslog specify that dnscrypt-proxy-multi and dnscrypt-proxy use syslog to log messages.

Specifying a different resolvers list

Sometimes we would want to specify a custom resolvers list of our own. For example, we may want the resolvers list to exclude those from OpenDNS to prevent restrictions. This is the basic indiscriminate command to produce it:

grep -vi opendns /usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv > \
		/var/local/dnscrypt-resolvers-custom.csv

Now we add the option --resolvers-list to the command:

dnscrypt-proxy-multi --local-ip=127.0.0.1 --local-port=5300-5399 \
		--dnscrypt-proxy-user=dnscrypt --syslog --dnscrypt-proxy-syslog \
		--resolvers-list=/var/local/dnscrypt-resolvers-custom.csv

Checking services for their capability to resolve domain names

We can also configure dnscrypt-proxy-multi to check if a service - that an instance of dnscrypt-proxy is connecting to - is capable of resolving domains.

The option’s format is --resolver-check=FQDN[,FQDN2][/TIMEOUT[/WAIT]].

Example usage:

--resolver-check=kernel.org,gentoo.org,disqus.com,a.disqus.com

It would tell dnscrypt-proxy-multi to check that the service is able to resolve all the FQDN’s (Fully Qualified Domain Names) specified above. It would terminate the dnscrypt-proxy process connecting to the service if it fails to resolve any of it.

We can also customize the amount of time (in seconds) that we can allow the service to respond. Example is 10 seconds instead of 5:

--resolver-check=kernel.org,gentoo.org,disqus.com,a.disqus.com/10.0

Note that a higher value means longer time for dnscrypt-proxy-multi to initialize. On the other hand, it should also be considered that some services take longer time to respond since they need to make multiple recursive queries to other name servers when the information about an FQDN is not yet in the cache, or is no longer valid, so having a longer waiting time would also be sensible.

Making dnscrypt-proxy-multi wait for connection before starting up

We can also make dnscrypt-proxy-multi wait for a connection before it starts checking if DNS servers are available.

This is helpful during startup when Internet connection may not yet be active.

An example usage is --wait-for-connection=208.67.220.220:53,208.67.222.222:53, which would allow dnscrypt-proxy-multi to continue if a response is received from either of those addresses.

This will be the complete command so far:

dnscrypt-proxy-multi --local-ip=127.0.0.1 --local-port=5300-5399 \
		--dnscrypt-proxy-user=dnscrypt --syslog --dnscrypt-proxy-syslog \
		--resolvers-list=/var/local/dnscrypt-resolvers-custom.csv \
		--resolver-check=kernel.org,gentoo.org,disqus.com,a.disqus.com \
		--wait-for-connection=208.67.202.202:53,208.67.222.222:53

Here’s an example output after running the command:

# dnscrypt-proxy-multi --local-ip=127.0.0.1 --local-port=5300-5399 \
> --dnscrypt-proxy-user=dnscrypt --syslog --dnscrypt-proxy-syslog \
> --resolvers-list=/var/local/dnscrypt-resolvers-custom.csv \
> --resolver-check=kernel.org,gentoo.org,disqus.com,a.disqus.com \
> --wait-for-connection=208.67.202.202:53,208.67.222.222:53
----------------------------------------
Starting up.
[Warning] Ignoring entry with invalid or unsupported resolver address: Resolver address
[Warning] Using default port 443 for 51.254.115.48.
[Warning] Using default port 443 for 208.67.220.220.
[Warning] Using default port 443 for 208.67.220.123.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2620:0:ccc::2]
[Warning] Using default port 443 for 113.20.6.2.
[Warning] Using default port 443 for 113.20.8.17.
[Warning] Using default port 443 for 162.221.207.228.
[Warning] Using default port 443 for 212.83.175.31.
[Warning] Using default port 443 for 195.154.61.33.
[Warning] Using default port 443 for 185.60.147.77.
[Warning] Using default port 443 for 46.165.222.246.
[Warning] Using default port 443 for 212.129.46.86.
[Warning] Using default port 443 for 212.129.46.32.
[Warning] Using default port 443 for 95.141.47.58.
[Warning] Using default port 443 for 173.234.159.235.
[Warning] Using default port 443 for 173.234.56.115.
[Warning] Using default port 443 for 70.32.38.67.
[Warning] Using default port 443 for 108.62.19.131.
[Warning] Using default port 443 for 76.164.234.11.
[Warning] Using default port 443 for 104.238.194.235.
[Warning] Using default port 443 for 217.12.203.133.
[Warning] Using default port 443 for 81.2.237.32.
[Warning] Using default port 443 for 82.211.31.248.
[Warning] Using default port 443 for 185.137.15.105.
[Warning] Using default port 443 for 77.66.108.93.
[Warning] Using default port 443 for 85.25.105.193.
[Warning] Using default port 443 for 37.235.49.61.
[Warning] Using default port 443 for 31.14.133.188.
[Warning] Using default port 443 for 89.111.13.60.
[Warning] Using default port 443 for 178.17.170.133.
[Warning] Using default port 443 for 185.14.29.140.
[Warning] Using default port 443 for 77.81.104.121.
[Warning] Using default port 443 for 41.79.69.13.
[Warning] Using default port 443 for 217.12.210.54.
[Warning] Using default port 443 for 77.66.84.233.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2001:1448:243::dc2]
[Warning] Using default port 443 for 176.56.237.171.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2a00:d880:3:1::a6c1:2e89]
[Warning] Using default port 443 for 212.47.228.136.
[Warning] Using default port 443 for 185.121.177.177.
[Warning] Using default port 443 for 185.121.177.53.
[Warning] Using default port 443 for 79.133.43.124.
[Warning] Using default port 443 for 172.81.176.146.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2602:ffb6:2:0:f816:3eff:fe23:ae28]
[Warning] Using default port 443 for 185.97.7.7.
[Warning] Using default port 443 for 185.115.125.185.
[Warning] Using default port 443 for 93.170.96.119.
[Warning] Using default port 443 for 95.154.236.104.
[Warning] Using default port 443 for 103.53.199.71.
[Warning] Using default port 443 for 217.78.6.191.
[Warning] Using default port 443 for 91.247.228.155.
[Warning] Using default port 443 for 185.99.132.5.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2a06:1280:bee1::dea:5]
[Warning] Using default port 443 for 173.44.61.182.
[Warning] Using default port 443 for 194.132.32.32.
[Warning] Using default port 443 for 93.95.228.87.
[Warning] Using default port 443 for 23.226.227.93.
[Warning] Ignoring entry with invalid or unsupported resolver address: [2a02:7aa0:1619::c434:714c]:5353
[Warning] Ignoring entry with invalid or unsupported resolver address: [2001:470:70:4ff::2]:2053
[Warning] Using default port 443 for 40.68.125.220.
Waiting for connection.
Checking resolver address 51.254.115.48:443.
Checking resolver address 208.67.220.220:443.
Checking resolver address 208.67.220.123:443.
Checking resolver address 208.67.220.220:53.
Checking resolver address 113.20.6.2:443.
Checking resolver address 113.20.8.17:443.
Checking resolver address 162.221.207.228:443.
Checking resolver address 212.83.175.31:443.
Checking resolver address 195.154.61.33:443.
Checking resolver address 185.60.147.77:443.
Checking resolver address 46.165.222.246:443.
Checking resolver address 212.129.46.86:443.
Checking resolver address 212.129.46.32:443.
Checking resolver address 95.141.47.58:443.
Checking resolver address 173.234.159.235:443.
Checking resolver address 173.234.56.115:443.
Checking resolver address 70.32.38.67:443.
Checking resolver address 108.62.19.131:443.
Checking resolver address 76.164.234.11:443.
Checking resolver address 104.238.194.235:443.
Checking resolver address 217.12.203.133:443.
Checking resolver address 81.2.237.32:443.
Checking resolver address 82.211.31.248:443.
Checking resolver address 185.137.15.105:443.
Checking resolver address 77.66.108.93:443.
Checking resolver address 37.187.0.40:54.
Checking resolver address 85.25.105.193:443.
Checking resolver address 37.235.49.61:443.
Checking resolver address 31.14.133.188:443.
Checking resolver address 89.111.13.60:443.
Checking resolver address 178.17.170.67:54.
Checking resolver address 95.85.9.86:80.
Checking resolver address 185.83.217.248:5353.
Checking resolver address 178.17.170.133:443.
Checking resolver address 185.14.29.140:443.
Checking resolver address 77.81.104.121:443.
Checking resolver address 91.214.71.181:80.
Checking resolver address 128.199.248.105:54.
Checking resolver address 41.79.69.13:443.
Checking resolver address 217.12.210.54:443.
Checking resolver address 77.66.84.233:443.
Checking resolver address 176.56.237.171:443.
Checking resolver address 212.47.228.136:443.
Checking resolver address 185.121.177.177:443.
Checking resolver address 185.121.177.53:443.
Checking resolver address 79.133.43.124:443.
Checking resolver address 172.81.176.146:443.
Checking resolver address 185.97.7.7:443.
Checking resolver address 185.115.125.185:443.
Checking resolver address 93.170.96.119:443.
Checking resolver address 95.154.236.104:443.
Checking resolver address 103.53.199.71:443.
Checking resolver address 217.78.6.191:443.
Checking resolver address 91.247.228.155:443.
Checking resolver address 185.99.132.5:443.
Checking resolver address 173.44.61.182:443.
Checking resolver address 194.132.32.32:443.
Checking resolver address 93.95.228.87:443.
Checking resolver address 23.226.227.93:443.
Checking resolver address 130.255.73.90:5353.
Checking resolver address 46.227.67.134:55.
Checking resolver address 93.115.92.252:5353.
Checking resolver address 95.215.46.195:5353.
Checking resolver address 45.55.117.60:8080.
Checking resolver address 45.55.113.239:8080.
Checking resolver address 178.216.201.222:2053.
Checking resolver address 40.68.125.220:443.
Checking resolver address 77.88.8.78:15353.
Starting dnscrypt-proxy instance for 208.67.220.123:443 (127.0.0.1:5300).
Checking if 208.67.220.123:443 (127.0.0.1:5300) can resolve kernel.org.
Success: 194.44.204.199
Checking if 208.67.220.123:443 (127.0.0.1:5300) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 208.67.220.123:443 (127.0.0.1:5300) can resolve disqus.com.
Success: 134.33.235.23
Checking if 208.67.220.123:443 (127.0.0.1:5300) can resolve a.disqus.com.
Success: 134.52.101.151
Starting dnscrypt-proxy instance for 208.67.220.220:53 (127.0.0.1:5301).
Checking if 208.67.220.220:53 (127.0.0.1:5301) can resolve kernel.org.
Success: 194.44.204.199
Checking if 208.67.220.220:53 (127.0.0.1:5301) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 208.67.220.220:53 (127.0.0.1:5301) can resolve disqus.com.
Success: 134.33.235.23
Checking if 208.67.220.220:53 (127.0.0.1:5301) can resolve a.disqus.com.
Success: 134.52.101.151
Starting dnscrypt-proxy instance for 128.199.248.105:54 (127.0.0.1:5302).
Checking if 128.199.248.105:54 (127.0.0.1:5302) can resolve kernel.org.
Success: 194.44.204.199
Checking if 128.199.248.105:54 (127.0.0.1:5302) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 128.199.248.105:54 (127.0.0.1:5302) can resolve disqus.com.
Success: 134.85.156.104
Checking if 128.199.248.105:54 (127.0.0.1:5302) can resolve a.disqus.com.
Success: 134.40.101.151
Starting dnscrypt-proxy instance for 208.67.220.220:443 (127.0.0.1:5303).
Checking if 208.67.220.220:443 (127.0.0.1:5303) can resolve kernel.org.
Success: 194.44.204.199
Checking if 208.67.220.220:443 (127.0.0.1:5303) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 208.67.220.220:443 (127.0.0.1:5303) can resolve disqus.com.
Success: 134.81.156.104
Checking if 208.67.220.220:443 (127.0.0.1:5303) can resolve a.disqus.com.
Success: 134.24.101.151
Starting dnscrypt-proxy instance for 185.121.177.53:443 (127.0.0.1:5304).
Checking if 185.121.177.53:443 (127.0.0.1:5304) can resolve kernel.org.
Success: 194.44.204.199
Checking if 185.121.177.53:443 (127.0.0.1:5304) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 185.121.177.53:443 (127.0.0.1:5304) can resolve disqus.com.
Success: 134.81.156.104
Checking if 185.121.177.53:443 (127.0.0.1:5304) can resolve a.disqus.com.
Success: 134.24.101.151
Starting dnscrypt-proxy instance for 185.121.177.177:443 (127.0.0.1:5305).
Checking if 185.121.177.177:443 (127.0.0.1:5305) can resolve kernel.org.
Success: 194.44.204.199
Checking if 185.121.177.177:443 (127.0.0.1:5305) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 185.121.177.177:443 (127.0.0.1:5305) can resolve disqus.com.
Success: 134.81.156.104
Checking if 185.121.177.177:443 (127.0.0.1:5305) can resolve a.disqus.com.
Success: 134.24.101.151
Starting dnscrypt-proxy instance for 104.238.194.235:443 (127.0.0.1:5306).
Checking if 104.238.194.235:443 (127.0.0.1:5306) can resolve kernel.org.
Success: 69.4.20.149
Checking if 104.238.194.235:443 (127.0.0.1:5306) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 104.238.194.235:443 (127.0.0.1:5306) can resolve disqus.com.
Success: 134.37.235.23
Checking if 104.238.194.235:443 (127.0.0.1:5306) can resolve a.disqus.com.
Success: 134.24.101.151
Starting dnscrypt-proxy instance for 103.53.199.71:443 (127.0.0.1:5307).
Checking if 103.53.199.71:443 (127.0.0.1:5307) can resolve kernel.org.
Success: 194.44.204.199
Checking if 103.53.199.71:443 (127.0.0.1:5307) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 103.53.199.71:443 (127.0.0.1:5307) can resolve disqus.com.
Success: 134.37.235.23
Checking if 103.53.199.71:443 (127.0.0.1:5307) can resolve a.disqus.com.
Success: 134.40.101.151
Starting dnscrypt-proxy instance for 45.55.113.239:8080 (127.0.0.1:5308).
Checking if 45.55.113.239:8080 (127.0.0.1:5308) can resolve kernel.org.
Success: 194.44.204.199
Checking if 45.55.113.239:8080 (127.0.0.1:5308) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 45.55.113.239:8080 (127.0.0.1:5308) can resolve disqus.com.
Success: 134.85.156.104
Checking if 45.55.113.239:8080 (127.0.0.1:5308) can resolve a.disqus.com.
Success: 134.40.101.151
Starting dnscrypt-proxy instance for 23.226.227.93:443 (127.0.0.1:5309).
Checking if 23.226.227.93:443 (127.0.0.1:5309) can resolve kernel.org.
Success: 140.20.145.198
Checking if 23.226.227.93:443 (127.0.0.1:5309) can resolve gentoo.org.
Success: 134.167.16.89
Checking if 23.226.227.93:443 (127.0.0.1:5309) can resolve disqus.com.
Success: 134.81.156.104
Checking if 23.226.227.93:443 (127.0.0.1:5309) can resolve a.disqus.com.
Success: 134.52.101.151

Running dnscrypt-proxy-multi from an init script

Creating a service for dnscrypt-proxy-multi is pretty straightforward, and is about the same as any other service. The only difference is that we have to explicitly specify the Ruby program that would directly run dnscrypt-proxy-multi.

An example to this is this OpenRC script for Gentoo provided by net-dns/dnscrypt-proxy-multi:

#!/sbin/openrc-run
# Copyright 1999-2016 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

RUBY_EXEC=${RUBY_EXEC:-/usr/bin/ruby}
PID_FILE=${PID_FILE:-/run/dnscrypt-proxy-multi.pid}

description="Runs multiple instances of dnscrypt-proxy"

depend() {
	use net
	before dns
	after logger
}

start() {
	ebegin "Starting dnscrypt-proxy-multi"

	start-stop-daemon --start --quiet --background --pidfile "${PID_FILE}" --make-pidfile --exec "${RUBY_EXEC}" -- \
			/usr/bin/dnscrypt-proxy-multi ${DNSCRYPT_PROXY_MULTI_OPTIONS}

	eend "$?"
}

stop() {
	ebegin "Stopping dnscrypt-proxy-multi"
	start-stop-daemon --stop --quiet --pidfile "${PID_FILE}"
	eend "$?"
}

Setting up named or unbound to forward requests to the instances of dnscrypt-proxy

Now that we have instances of dnscrypt-proxy set up, we can now use a local DNS server to catch all requests from the system and forward them to the instances.

Here’s an example for named (/etc/bind/named.conf). The most important directives are forward and forwarders.

/*
 * Refer to the named.conf(5) and named(8) man pages, and the documentation
 * in /usr/share/doc/bind-9 for more details.
 * Online versions of the documentation can be found here:
 * http://www.isc.org/software/bind/documentation
 */

acl "xfer" {
	none;
};

acl "trusted" {
	127.0.0.0/8;
};

options {
	directory "/var/bind";
	pid-file "/run/named/named.pid";

	listen-on { 127.0.0.1; };

	allow-query {
		trusted;
	};

	allow-query-cache {
		trusted;
	};

	allow-recursion {
		trusted;
	};

	allow-transfer {
		none;
	};

	allow-update {
		none;
	};

	forward only;

	forwarders {
		127.0.0.1 port 5300;  // dnscrypt-proxy-multi
		127.0.0.1 port 5301;  // dnscrypt-proxy-multi
		127.0.0.1 port 5302;  // dnscrypt-proxy-multi
		127.0.0.1 port 5303;  // dnscrypt-proxy-multi
		127.0.0.1 port 5304;  // dnscrypt-proxy-multi
		127.0.0.1 port 5305;  // dnscrypt-proxy-multi
		127.0.0.1 port 5306;  // dnscrypt-proxy-multi
		127.0.0.1 port 5307;  // dnscrypt-proxy-multi
		127.0.0.1 port 5308;  // dnscrypt-proxy-multi
		127.0.0.1 port 5309;  // dnscrypt-proxy-multi
	};

	recursion yes;
};

include "/etc/bind/logging.conf";

zone "." in {
	type hint;
	file "/var/bind/named.cache";
};

zone "localhost" IN {
	type master;
	file "pri/localhost.zone";
	notify no;
};

zone "127.in-addr.arpa" IN {
	type master;
	file "pri/127.zone";
	notify no;
};

And here’s an example configuration for unbound:

server:
	verbosity: 1
	interface: 127.0.0.1
	chroot: "/chroot/unbound"
	root-hints: "/etc/root.hints"
	do-not-query-localhost: no
forward-zone:
	name: "."
	forward-addr: 127.0.0.1@5300
	forward-addr: 127.0.0.1@5301
	forward-addr: 127.0.0.1@5302
	forward-addr: 127.0.0.1@5303
	forward-addr: 127.0.0.1@5304
	forward-addr: 127.0.0.1@5305
	forward-addr: 127.0.0.1@5306
	forward-addr: 127.0.0.1@5307
	forward-addr: 127.0.0.1@5308
	forward-addr: 127.0.0.1@5309
	forward-first: no

Notice that we had to use do-not-query-localhost: no since it is yes by default, and queries sent to 127.0.0.1/8 and ::1 are not allowed. If you think it’s a security risk, you can make dnscrypt-proxy listen to local addresses besides 127.0.0.1, like 127.1.0.1, and add custom do-not-query-address values that would target localhost addresses besides it, like 127.0.0.1/16.

Once configured, start the name server with your service manager. For example:

/etc/init.d/named start
/etc/init.d/unbound start

After that, running dig kernel.org @127.0.0.1 should give us something like this:

; <<>> DiG 9.10.3-P2 <<>> kernel.org @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35985
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;kernel.org.                    IN      A

;; ANSWER SECTION:
kernel.org.             571     IN      A       199.204.44.194
kernel.org.             571     IN      A       198.145.20.140
kernel.org.             571     IN      A       149.20.4.69

Seeing that everything’s ok, we can now add 127.0.0.1 to /etc/resolv.conf:

# echo 'nameserver 127.0.0.1' > /etc/resolv.conf

You can make it permanent by configuring your network interface with the network manager you use, just like this one:

[ connection edit ]
Editing a network connection using Network Manager

Side note: We actually can just make instances of dnscrypt-proxy listen to multiple IP addresses like 127.53.0.1 to 127.53.0.10 where each would listen to port 53, and just add those IP addresses to /etc/resolv.conf; no longer needing a local name server in between, but I still prefer the idea of having everything managed by a single server, where the server may also cache the DNS data by itself.

Filtering syslog messages

If we’re using syslog to all instances of dnscrypt-proxy and dnscrypt-proxy-multi, we can filter them and send them to another file by configuring syslog-ng (granting syslog-ng is our system logger application).

Here’s an example configuration in /etc/syslog-ng/syslog-ng.conf. Note that I have excluded the options directive.

...
source s_system { system(); internal(); };

filter f_dnscrypt_proxy { program(^dnscrypt-proxy$); };
filter f_dnscrypt_proxy_multi { program(^dnscrypt-proxy-multi$); };
filter f_messages { not program(^unbound$) and not program(^dnscrypt-proxy$) and not program(^dnscrypt-proxy-multi$); };
filter f_unbound { program(^unbound$); };

destination d_console_all { file("/dev/tty12"); };
destination d_dnscrypt_proxy { file("/var/log/dnscrypt-proxy.log"); };
destination d_dnscrypt_proxy_multi { file("/var/log/dnscrypt-proxy-multi.log"); };
destination d_messages { file("/var/log/messages"); };
destination d_unbound { file("/var/log/unbound.log"); };

log { source(s_system); filter(f_dnscrypt_proxy); destination(d_dnscrypt_proxy); };
log { source(s_system); filter(f_dnscrypt_proxy_multi); destination(d_dnscrypt_proxy_multi); };
log { source(s_system); destination(d_console_all); };
log { source(s_system); filter(f_messages); destination(d_messages); };
log { source(s_system); filter(f_unbound); destination(d_unbound); };

For more informtion on how those directives work, please see syslog-ng.conf(5).

After making the changes, restart the syslog-ng to make it work. In Gentoo, you can do it with /etc/init.d/syslog-ng restart.

Running dnscrypt-proxy-multi in a chroot environment

I’ll be updating this article for that information, but if you’re using Gentoo and OpenRC, you can install the konsolebox overlay and use the chroot flag when you install net-dns/dnscrypt-proxy-multi. After installing the package, configure /etc/conf.d/dnscrypt-proxy-multi-chroot and run /etc/conf.d/dnscrypt-proxy-multi-chroot setup. Run /etc/conf.d/dnscrypt-proxy-multi-chroot start afterwards. Don’t forget to add the init script at boot time with rc-config add dnscrypt-proxy-multi-chroot if you need to.

It should also be noted that dnscrypt-proxy is already capable of doing chroot by itself when specified with a user, which can be configured through dnscrypt-proxy-multi’s --dnscrypt-proxy-user option. See dnscrypt-proxy(8) for more info about the --user option, which is called by dnscrypt-proxy-multi when --dnscrypt-proxy-user is used.

Back to top