{"id":13548719,"url":"https://github.com/qdm12/dns","last_synced_at":"2025-05-15T17:09:29.584Z","repository":{"id":38254948,"uuid":"128698848","full_name":"qdm12/dns","owner":"qdm12","description":"Docker DNS server on steroids to access DNS-over-TLS from Cloudflare, Google, Quad9, Quadrant or CleanBrowsing","archived":false,"fork":false,"pushed_at":"2024-11-05T09:05:13.000Z","size":1918,"stargazers_count":257,"open_issues_count":31,"forks_count":39,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-29T18:02:27.708Z","etag":null,"topics":["cleanbrowsing","cloudflare-dns","dns-over-tls","dns-server","docker","google-dns","quad9","quadrant","unbound"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/qmcgaw/cloudflare-dns-server","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qdm12.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["qdm12"]}},"created_at":"2018-04-09T01:40:58.000Z","updated_at":"2025-04-29T16:33:24.000Z","dependencies_parsed_at":"2023-11-15T09:29:52.091Z","dependency_job_id":"c69f7048-84b6-4a67-a7b7-ce2fb3d57614","html_url":"https://github.com/qdm12/dns","commit_stats":null,"previous_names":["qdm12/cloudflare-dns-server"],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fdns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fdns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fdns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qdm12%2Fdns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qdm12","download_url":"https://codeload.github.com/qdm12/dns/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253736097,"owners_count":21955782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cleanbrowsing","cloudflare-dns","dns-over-tls","dns-server","docker","google-dns","quad9","quadrant","unbound"],"created_at":"2024-08-01T12:01:13.604Z","updated_at":"2025-05-15T17:09:24.570Z","avatar_url":"https://github.com/qdm12.png","language":"Go","funding_links":["https://github.com/sponsors/qdm12"],"categories":["Go"],"sub_categories":[],"readme":"# DNS over TLS upstream server Docker container\r\n\r\nDNS over TLS upstream server connected to DNS over TLS (IPv4 and IPv6) servers with DNSSEC, DNS rebinding protection, built-in Docker healthcheck and fine grain IPs + hostnames blocking\r\n\r\n**Announcement**: *You can now try `:v2.0.0-beta` with [this documentation](https://github.com/qdm12/dns/tree/v2.0.0-beta).\r\n\r\n**The `:latest` Docker image might break compatibility in the coming days/weeks**\r\n\r\n[![Title](https://github.com/qdm12/dns/raw/master/readme/title.png)](https://hub.docker.com/r/qmcgaw/dns)\r\n\r\n[![Build status](https://github.com/qdm12/dns/actions/workflows/build.yml/badge.svg)](https://github.com/qdm12/dns/actions/workflows/build.yml)\r\n\r\n[![dockeri.co](https://dockeri.co/image/qmcgaw/dns)](https://hub.docker.com/r/qmcgaw/dns)\r\n[![dockeri.co](https://dockeri.co/image/qmcgaw/cloudflare-dns-server)](https://hub.docker.com/r/qmcgaw/cloudflare-dns-server)\r\n\r\n![Last release](https://img.shields.io/github/release/qdm12/dns?label=Last%20release)\r\n![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/dns?sort=semver\u0026label=Last%20Docker%20tag)\r\n[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/dns?sort=semver\u0026label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/dns/tags?page=1\u0026ordering=last_updated)\r\n![GitHub last release date](https://img.shields.io/github/release-date/qdm12/dns?label=Last%20release%20date)\r\n![Commits since release](https://img.shields.io/github/commits-since/qdm12/dns/latest?sort=semver)\r\n\r\n[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/dns/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/dns/tags)\r\n\r\n[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/dns.svg)](https://github.com/qdm12/dns/commits/main)\r\n[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/dns.svg)](https://github.com/qdm12/dns/graphs/contributors)\r\n[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/dns.svg)](https://github.com/qdm12/dns/pulls?q=is%3Apr+is%3Aclosed)\r\n[![GitHub issues](https://img.shields.io/github/issues/qdm12/dns.svg)](https://github.com/qdm12/dns/issues)\r\n[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/dns.svg)](https://github.com/qdm12/dns/issues?q=is%3Aissue+is%3Aclosed)\r\n\r\n[![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/dns)](https://github.com/qdm12/dns)\r\n![Code size](https://img.shields.io/github/languages/code-size/qdm12/dns)\r\n![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/dns)\r\n![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/dns)\r\n\r\n[![MIT](https://img.shields.io/github/license/qdm12/dns)](https://github.com/qdm12/dns/master/LICENSE)\r\n![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=dns.readme)\r\n\r\n## Features\r\n\r\n- It can be connected to one or more of the following DNS-over-TLS providers:\r\n  - [Cloudflare](https://developers.cloudflare.com/1.1.1.1/dns-over-tls/)\r\n  - [Google](https://developers.google.com/speed/public-dns/docs/dns-over-tls)\r\n  - [Quad9](https://www.quad9.net/faq/#Does_Quad9_support_DNS_over_TLS)\r\n  - [LibreDNS](https://libredns.gr)\r\n  - [Quadrant](https://quadrantsec.com/about/blog/quadrants_public_dns_resolver_with_tls_https_support/)\r\n  - [CleanBrowsing](https://cleanbrowsing.org/guides/dnsovertls)\r\n  - [CIRA Canadian Shield](https://www.cira.ca/cybersecurity-services/canadian-shield)\r\n- Split-horizon DNS (randomly pick one of the DoT providers specified for each request)\r\n- Block hostnames and IP addresses for 3 categories: malicious, surveillance and ads\r\n- Block custom hostnames and IP addresses using environment variables\r\n- **One line setup**\r\n- Runs without root\r\n- Small 41.1MB Docker image (uncompressed, amd64)\r\n  - [Alpine 3.14](https://alpinelinux.org)\r\n  - [Unbound 1.13.1](https://nlnetlabs.nl/downloads/unbound) from Alpine packages\r\n  - [Files and lists built periodically](https://github.com/qdm12/updated/tree/master/files)\r\n  - Go static binary entrypoint built from this source\r\n- Resolves using IPv4 and IPv6 when available\r\n- Auto updates block lists and cryptographic files every 24h and restarts Unbound (\u003c 1 second downtime)\r\n- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v7 and ppc64le 🎆\r\n- DNS rebinding protection\r\n- DNSSEC Validation\r\n\r\n    [![DNSSEC Validation](https://github.com/qdm12/dns/blob/master/readme/rootcanary.org.png?raw=true)](https://www.rootcanary.org/test.html)\r\n\r\nDiagrams are shown for router and client-by-client configurations in the [**Connect clients to it**](#connect-clients-to-it) section.\r\n\r\n## Setup\r\n\r\n1. ⚠️ Raspberry Pi users running 32 bit systems, you need to do [this](https://github.com/alpinelinux/docker-alpine/issues/135#issuecomment-812287338) on your host to run the container.\r\n1. Launch the container with\r\n\r\n    ```sh\r\n    docker run -d -p 53:53/udp qmcgaw/dns\r\n    ```\r\n\r\n    You can also use [docker-compose.yml](https://github.com/qdm12/dns/blob/master/docker-compose.yml) with:\r\n\r\n    ```sh\r\n    docker-compose up -d\r\n    ```\r\n\r\n    More environment variables are described in the [environment variables](#environment-variables) section.\r\n\r\n1. See the [Connect clients to it](#connect-clients-to-it) section, you can also refer to the [Verify DNS connection](#verify-dns-connection) section if you want.\r\n\r\nIf you run an old Docker version or Kernel, you might want to run the container as root with `--user=\"0\"` (see [this issue](https://github.com/qdm12/dns/issues/79) for context).\r\n\r\n## Docker tags 🐳\r\n\r\n| Docker image | Github release |\r\n| --- | --- |\r\n| `qmcgaw/dns:latest` | [Master branch](https://github.com/qdm12/dns/commits/master) |\r\n| `qmcgaw/dns:v1.5.1` | [v1.5.1](https://github.com/qdm12/dns/releases/tag/v1.5.1) |\r\n| `qmcgaw/dns:v1.4.1` | [v1.4.1](https://github.com/qdm12/dns/releases/tag/v1.4.1) |\r\n| `qmcgaw/dns:v1.2.1` | [v1.2.1](https://github.com/qdm12/dns/releases/tag/v1.2.1) |\r\n| `qmcgaw/dns:v1.1.1` | [v1.1.1](https://github.com/qdm12/dns/releases/tag/v1.1.1) |\r\n| `qmcgaw/cloudflare-dns-server:latest` | [Master branch](https://github.com/qdm12/dns/commits/master) |\r\n| `qmcgaw/cloudflare-dns-server:v1.0.0` | [v1.0.0](https://github.com/qdm12/dns/releases/tag/v1.0.0) |\r\n\r\n💁 `qmcgaw/cloudflare-dns-server:latest` mirrors `qmcgaw/dns:latest`\r\n\r\n## Environment variables\r\n\r\n| Environment variable | Default | Description |\r\n| --- | --- | --- |\r\n| `PROVIDERS` | `cloudflare` | Comma separated list of DNS-over-TLS providers from `cira family`, `cira private`, `cira protected`, `cleanbrowsing adult`, `cleanbrowsing family`, `cleanbrowsing security`, `cloudflare`, `cloudflare family`, `cloudflare security`, `google`, `libredns`, `quad9`, `quad9 secured`, `quad9 unsecured` and `quadrant` |\r\n| `VERBOSITY` | `1` | From 0 (no log) to 5 (full debug log) |\r\n| `VERBOSITY_DETAILS` | `0` | From 0 to 4 (higher means more details) |\r\n| `BLOCK_MALICIOUS` | `on` | `on` or `off`, to block malicious IP addresses and malicious hostnames from being resolved |\r\n| `BLOCK_SURVEILLANCE` | `off` | `on` or `off`, to block surveillance IP addresses and hostnames from being resolved |\r\n| `BLOCK_ADS` | `off` | `on` or `off`, to block ads IP addresses and hostnames from being resolved |\r\n| `BLOCK_HOSTNAMES` |  | comma separated list of hostnames to block from being resolved |\r\n| `BLOCK_IPS` |  | comma separated list of IPs to block from being returned to clients |\r\n| `UNBLOCK` | | comma separated list of hostnames to leave unblocked |\r\n| `LISTENINGPORT` | `53` | UDP port on which the Unbound DNS server should listen to (internally) |\r\n| `CACHING` | `on` | `on` or `off`. It can be useful if you have another DNS (i.e. Pihole) doing the caching as well on top of this container |\r\n| `PRIVATE_ADDRESS` | All IPv4 and IPv6 CIDRs private ranges | Comma separated list of CIDRs or single IP addresses. Note that the default setting prevents DNS rebinding |\r\n| `CHECK_DNS` | `on` | `on` or `off`. Check resolving github.com using `127.0.0.1:53` at start |\r\n| `IPV4` | `on` | `on` or `off`. Uses DNS resolution for IPV4 |\r\n| `IPV6` | `off` | `on` or `off`. Uses DNS resolution for IPV6. **Do not enable if you don't have IPV6** |\r\n| `UPDATE_PERIOD` | `24h` | Period to update block lists and restart Unbound. Set to `0` to disable. |\r\n\r\n## Extra configuration\r\n\r\nYou can bind mount an Unbound configuration file *include.conf* to be included in the Unbound server section with\r\n`-v $(pwd)/include.conf:/unbound/include.conf:ro`, see [Unbound configuration documentation](https://nlnetlabs.nl/documentation/unbound/unbound.conf/)\r\n\r\n## Golang API\r\n\r\nIf you want to use the Go code I wrote, you can see tiny [examples](examples) of DoT and DoH resolvers and servers using the API developed.\r\n\r\n## Connect clients to it\r\n\r\n### Option 1: Router (recommended)\r\n\r\nAll machines connected to your router will use the 1.1.1.1 encrypted DNS by default\r\n\r\nConfigure your router to use the LAN IP address of your Docker host as its primary DNS address.\r\n\r\n- Access your router page, usually at [http://192.168.1.1](http://192.168.1.1) and login with your credentials\r\n- Change the DNS settings, which are usually located in *Connection settings / Advanced / DNS server*\r\n- If a secondary fallback DNS address is required, use a dull ip address such as the router's IP 192.168.1.1 to force traffic to only go through this container\r\n\r\n![Diagram router](https://github.com/qdm12/dns/blob/master/readme/diagram-router.png?raw=true)\r\n\r\nTo ensure network clients cannot use another DNS, you might want to\r\n\r\n- Block the outbound UDP 53 port on your router firewall\r\n- Block the outbound TCP 853 port on your router firewall, **except from your Docker host**\r\n- If you have *Deep packet inspection* on your router, block DNS over HTTPs on port TCP 443\r\n\r\n### Option 2: Client, one by one\r\n\r\nYou have to configure each machine connected to your router to use the Docker host as their DNS server.\r\n\r\n![Diagram clients](https://github.com/qdm12/dns/blob/master/readme/diagram-clients.png?raw=true)\r\n\r\n#### Docker containers\r\n\r\nConnect other Docker containers by specifying the DNS to be the host IP address `127.0.0.1`:\r\n\r\n```bash\r\ndocker run -it --rm --dns=127.0.0.1 alpine\r\n```\r\n\r\nFor *docker-compose.yml*:\r\n\r\n```yml\r\nversion: '3'\r\nservices:\r\n  test:\r\n    image: alpine:3.11\r\n    network_mode: bridge\r\n    dns:\r\n      - 127.0.0.1\r\n```\r\n\r\nIf the containers are in the same Docker network, you can simply set the `dns` to the LAN IP address of the DNS container (i.e. `10.0.0.5`)\r\n\r\n#### Windows\r\n\r\n1. Open the control panel and follow the instructions shown on the screenshots below.\r\n\r\n![Windows screenshot 1](https://github.com/qdm12/dns/blob/master/readme/windows1.png?raw=true)\r\n\r\n![Windows screenshot 2](https://github.com/qdm12/dns/blob/master/readme/windows2.png?raw=true)\r\n\r\n![Windows screenshot 3](https://github.com/qdm12/dns/blob/master/readme/windows3.png?raw=true)\r\n\r\n![Windows screenshot 4](https://github.com/qdm12/dns/blob/master/readme/windows4.png?raw=true)\r\n\r\n![Windows screenshot 5](https://github.com/qdm12/dns/blob/master/readme/windows5.png?raw=true)\r\n\r\nEnter the IP Address of your Docker host as the **Preferred DNS server** (`192.168.1.210` in my case)\r\nYou can set the Cloudflare DNS server address 1.1.1.1 as an alternate DNS server although you might want to\r\nleave this blank so that no domain name request is in plaintext.\r\n\r\n![Windows screenshot 6](https://github.com/qdm12/dns/blob/master/readme/windows6.png?raw=true)\r\n\r\n![Windows screenshot 7](https://github.com/qdm12/dns/blob/master/readme/windows7.png?raw=true)\r\n\r\nWhen closing, Windows should try to identify any potential problems.\r\nIf everything is fine, you should see the following message:\r\n\r\n![Windows screenshot 8](https://github.com/qdm12/dns/blob/master/readme/windows8.png?raw=true)\r\n\r\n#### Mac OS\r\n\r\nFollow the instructions at [https://support.apple.com/kb/PH25577](https://support.apple.com/kb/PH25577)\r\n\r\n#### Linux\r\n\r\nYou probably know how to do that. Otherwise you can usually modify the first line of */etc/resolv.conf* by changing the IP address of your DNS server.\r\n\r\n#### Android\r\n\r\nSee [this](http://xslab.com/2013/08/how-to-change-dns-settings-on-android/)\r\n\r\n#### iOS\r\n\r\nSee [this](http://www.macinstruct.com/node/558)\r\n\r\n### Firewall considerations\r\n\r\nThis container requires the following connections:\r\n\r\n- UDP 53 Inbound (only if used externally)\r\n- TCP 853 Outbound to 1.1.1.1 and 1.0.0.1\r\n\r\n### Verify DNS connection\r\n\r\n1. Verify that you use Cloudflare DNS servers: [https://www.dnsleaktest.com](https://www.dnsleaktest.com) with the Standard or Extended test\r\n1. Verify that DNS SEC is enabled: [https://en.internet.nl/connection](https://en.internet.nl/connection)\r\n\r\nNote that [https://1.1.1.1/help](https://1.1.1.1/help) does not work as the container is not a client to Cloudflare servers but a forwarder intermediary. Hence [https://1.1.1.1/help](https://1.1.1.1/help) does not detect a direct connection to them.\r\n\r\n## Go API\r\n\r\nSome packages are exposed publicly through the [pkg](pkg) directory.\r\n\r\nThe API is at v1.x.x but (shame on me) is not stable and subject to change without changing major version. If you need it to be stable, please [create an issue](https://github.com/qdm12/dns/issues/new) and I'll see what I can do.\r\n\r\nFor now, it is used by the [gluetun](https://github.com/qdm12/gluetun) project for its DNS over TLS usage.\r\n\r\n## Development\r\n\r\n### Development setup\r\n\r\n#### Using VSCode and Docker\r\n\r\n1. Install [Docker](https://docs.docker.com/install/)\r\n    - On Windows, share a drive with Docker Desktop and have the project on that partition\r\n    - On OSX, share your project directory with Docker Desktop\r\n1. With [Visual Studio Code](https://code.visualstudio.com/download), install the [remote containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)\r\n1. In Visual Studio Code, press on `F1` and select `Remote-Containers: Open Folder in Container...`\r\n1. Your dev environment is ready to go!... and it's running in a container :+1:\r\n\r\n#### Locally\r\n\r\n1. Install [Go](https://golang.org/dl/), [Docker](https://www.docker.com/products/docker-desktop) and [Git](https://git-scm.com/downloads)\r\n1. Install dependencies\r\n\r\n    ```sh\r\n    go mod download\r\n    ```\r\n\r\n1. Install [golangci-lint](https://github.com/golangci/golangci-lint#install)\r\n\r\n### Commands available\r\n\r\n```sh\r\n# Build the binary\r\ngo build cmd/main.go\r\n# Test the code\r\ngo test ./...\r\n# Lint the code\r\ngolangci-lint run\r\n# Build the Docker image\r\ndocker build -t qmcgaw/dns .\r\n```\r\n\r\nSee [Contributing](.github/CONTRIBUTING.md) for more information on how to contribute to this repository.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqdm12%2Fdns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqdm12%2Fdns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqdm12%2Fdns/lists"}