Sign inSign up

nutshells/dnscrypt-wrapper

By nutshells

•Updated almost 7 years ago

DNSCrypt-Wrapper Docker Image

Image
0

1.2K

nutshells/dnscrypt-wrapper repository overview

⁠DNSCrypt-Wrapper Docker Image

Project Nutshells Docker Build Build Status Alpine Based MIT License dnscrypt-wrapper

DNSCrypt-Wrapper⁠ is the server-end of DNSCrypt⁠ proxy, which is a protocol to improve DNS security, now with xchacha20 cipher support. This image features certficate management & rotation.

⁠Variants:

TagDescription🐳
:latestDNSCrypt-Wrapper 0.3 on alpine:latest.Dockerfile

⁠Usage

⁠Synopsis
docker container run [OPTIONS] nutshells/dnscrypt-wrapper [COMMAND] [ARG...]
  • Learn more about docker container run and its OPTIONS here⁠;
  • List all available COMMANDs: docker container run --rm --read-only nutshells/dnscrypt-wrapper help
  • List all ARGs: docker container run --rm --read-only nutshells/dnscrypt-wrapper --help
⁠Getting Started

A DNScrypt proxy server cannot go without a so-called provider key pair. It's rather easy to generate a new pair by running the init command⁠ as below:

<keys_dir> is the host directory where you store the key pairs. The provider key pair should NEVER be changed as you may inform the world of the public key, unless the secret key is compromised.

docker container run -d -p 5353:12345/udp -p 5353:12345/tcp \
       --name=dnscrypt-server --restart=unless-stopped --read-only \
       --mount=type=bind,src=<keys_dir>,dst=/usr/local/etc/dnscrypt-wrapper \
       nutshells/dnscrypt-wrapper \
       init

Now, a server is initialized with default settings⁠ and running on port 5353 as a daemon.

Dig the public key fingerprint out of logs:

docker container logs dnscrypt-server | grep --color 'Provider public key: '

Then see if it works⁠.

⁠Using an existing key pair

If you used to run a DNScrypt proxy server and have been keeping the keys securely, make sure they are put into <keys_dir> and renamed to public.key and secret.key, then run the start command⁠ instead.

Incidentally, start is the default one which could be just omitted.

Lost the public key fingerprint but couldn't find it from logs? Try the pubkey command:

docker container run --rm --read-only \
       --mount=type=bind,src=<keys_dir>,dst=/usr/local/etc/dnscrypt-wrapper \
       nutshells/dnscrypt-wrapper \
       pubkey
⁠How to test

Get <provider_pub_key> by following the instructions above, and check this section⁠ to determin the default value of <provider_basename>.

Please install dnscrypt-proxy⁠ and dig first.

dnscrypt-proxy --local-address=127.0.0.1:53 \
               --resolver-address=127.0.0.1:5353 \
               --provider-name=2.dnscrypt-cert.<provider_basename> \
               --provider-key=<provider_pub_key>
               --loglevel=7
dig -p 53 +tcp google.com @127.0.0.1
⁠Utilities

As you can see from the examples of the previous sections: the container accepts original command-line options⁠ of ss-libev as arguments.

Here are a few more examples:

⁠Printing the version:
docker container run --rm --read-only nutshells/dnscrypt-wrapper --version
⁠Enabling verbose mode:
docker container run [OPTIONS] nutshells/dnscrypt-wrapper [COMMAND] [ARG...] -V
⁠Printing command-line options:
docker container run --rm --read-only nutshells/dnscrypt-wrapper --help

However, please be informed that some of the options are managed by the entrypoint script⁠ of the container. You will encounter an error while trying to set any of them, just use the environment variables⁠ instead; as for other exceptions, just follow the message to get rid of them.

⁠Reference

⁠Environment Variables
NameDefaultRelevant OptionDescription
RESOLVER_IP8.8.8.8-r, --resolver-addressHostname or IP address of the upstream dns resolver.
RESOLVER_PORT53-r, --resolver-addressPort number of the upstream dns resolver.
PROVIDER_BASENAMEexample.com--provider-nameBasename of the provider, which forms the whole provide name with a prefix 2.dnscrypt-cert..
CRYPT_KEYS_LIFESPAN365--cert-file-expire-daysFor how long (in days) the crypt key & certs would be valid. Refer to this topic⁠ to automate the rotation.

For instance, if you want to use OpenDNS⁠ as the upstream DNS resolver other than Google's Public DNS⁠, the default one, just set an environment variable⁠ like this:

docker container run \
       -e RESOLVER_IP=208.67.222.222 -e RESOLVER_PORT=5353 \
       [OTHER_OPTIONS] ...
⁠Data Volumes
Path in ContainerDescriptionMount as Writeable
/usr/local/etc/dnscrypt-wrapperDirectory where keys are storedY

⁠Advanced Topics

⁠Using Docker-compose

See the sample file⁠.

⁠Better Performance

It's a good idea to speed up the high-frequency queries by adding a caching upstream resolver, we choose dnsmasq⁠ for its lightweight and configurability. Though there're many other options, such as unbound⁠.

Firstly, let's create a new bridge network named dnscrypt:

Add --driver overlay if it's in swarm mode.

docker network create dnscrypt

Secondly, create a dnsmasq container (as an upstream resolver) into the network with an increased cache size:

Refer to this page⁠ for more about the nutshells/dnsmasq-fast-lookup image.

docker container run -d --network=dnscrypt --network-alias=upstream \
       --name=upstream --restart=unless-stopped --read-only \
       nutshells/dnsmasq-fast-lookup \
       --domain-needed --bogus-priv \
       --server=8.8.8.8 --no-resolv --no-hosts \
       --cache-size=10240

Then start a dnscrypt server into the same network too:

To add an existing container into the network, use docker network connect⁠ please.

docker container run -d --network=dnscrypt \
       -p 5353:12345/udp -p 5353:12345/tcp \
       --name=dnscrypt-server --restart=unless-stopped --read-only \
       --mount=type=bind,src=<keys_dir>,dst=/usr/local/etc/dnscrypt-wrapper \
       -e RESOLVER_IP=upstream -e RESOLVER_PORT=12345 \
       nutshells/dnscrypt-wrapper \
       init

Done!

A compose file⁠, which helps you to manage both of the containers concurrently, is highly recommended for this situation.

Alternatively, the --net=host option provides the best network performance, use it if you know it exactly.

⁠Backing-up the secret key

If you forgot to mount <keys_dir> into the container, and now you want to locate the secret key in the anonymous volume, just do some inspection first:

docker container inspect -f '{{json .Mounts }}' dnscrypt-server | grep --color '"Source":'

Then backup it securely.

⁠Rotating the crypt key and certs

Unlike the lifelong provider key pair, a crypt key & two certs, which are time-limited and used to encrypt and authenticate DNS queries, will be generated only if they're missing or expiring on starting. Thus the container is supposed to be restarted before certs' expiration.

Two certs are issued right after the crypt key's generation, one of them uses xchacha20 cipher.

Let's say we're planning to rotate them about once a week. Firstly, shrink the cert's lifespan⁠ to 7 days:

Actually the rotation starts when the validity remaining is under 30%, which would be on day 5 in this case.

docker container run -e CRYPT_KEYS_LIFESPAN=7 ...

Secondly, restart the container every single day by creating a daily cronjob:

0 4 * * * docker container restart dnscrypt-server
⁠Gaining a shell access

Get an interactive shell to a running container:

docker container exec -it dnscrypt-server /bin/ash
⁠Customizing the image
⁠By modifying the dockerfile

You may want to make some modifications to the image. Pull the source code from GitHub, customize it, then build one by yourself:

git clone --depth 1 https://github.com/quchao/nutshells.git
docker image build -q=false --rm=true --no-cache=true \
             -t nutshells/dnscrypt-wrapper \
             -f ./dnscrypt-wrapper/Dockerfile \
             ./dnscrypt-wrapper
⁠By committing the changes on a container

Otherwise just pull the image from the official registry, start a container and get a shell⁠ to it, commit the changes⁠ afterwards.

docker container commit --change "Commit msg" dnscrypt-server nutshells/dnscrypt-wrapper

⁠Caveats

⁠Declaring the Health Status

Status of this container-specified health check merely indicates whether the crypt certs are about to expire, you'd better restart the container to rotate the keys⁠ ASAP if it's shown as unhealthy.

To confirm the status, run this command:

docker container inspect --format='{{json .State.Health.Status}}' dnscrypt-server

And to check the logs:

docker container inspect --format='{{json .State.Health}}' dnscrypt-server | python -m json.tool

If you think this is annoying, just add the --no-healthcheck option⁠ to disable it.

⁠Contributing

Github Starts Twitter Followers

Follow GitHub's How-to⁠ guide for the basis.

Contributions are always welcome in many ways:

  • Give a star to show your fondness;
  • File an issue⁠ if you have a question or an idea;
  • Fork this repo and submit a PR⁠;
  • Improve the documentation.

⁠Todo

  • Serve with the old key & certs for another hour after the rotation.
  • Add instructions on how to speed it up by caching the upstream dns queries.
  • Add a HealthCheck instruction to indicate the expiration status of certs.
  • Add a command for checking the expire status.
  • Use another container to rotate the keys.

⁠Acknowledgments & Licenses

Unless specified, all codes of Project Nutshells are released under the MIT License⁠.

Other relevant softwares:

Tag summary

Content type

Image

Digest

Size

3.9 MB

Last updated

almost 7 years ago

docker pull nutshells/dnscrypt-wrapper