├── .gitignore ├── pomerium.yaml ├── example.env ├── graph.dot ├── LICENSE ├── docker-compose.yaml ├── adguard └── conf │ └── AdGuardHome.yaml.example └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | pomerium/ 3 | adguard/work/ 4 | adguard/conf/AdGuardHome.yaml 5 | -------------------------------------------------------------------------------- /pomerium.yaml: -------------------------------------------------------------------------------- 1 | services: all 2 | pomerium_debug: true 3 | http_redirect_addr: :80 4 | grpc_client_dns_roundrobin: false 5 | autocert: true 6 | 7 | policy: 8 | - from: https://dns.example.com 9 | to: https://adguard 10 | path: /dns-query 11 | allow_public_unauthenticated_access: true 12 | tls_server_name: YOUR_DOMAIN_NAME 13 | - from: https://adguard.example.com 14 | to: http://adguard 15 | timeout: 10m 16 | allowed_users: 17 | - YOUR_GMAIL_ACCOUNT 18 | -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | TZ=America/Los_Angeles 2 | DOMAIN_NAME=example.com 3 | POMERIUM_DOMAIN_NAME=pomerium.example.com 4 | DNS_DOMAIN_NAME=dns.example.com 5 | # See https://www.pomerium.io/docs/identity-providers.html on detailed 6 | # instruction. 7 | POMERIUM_CLIENT_ID=YOUR_CLIENT_ID 8 | POMERIUM_CLIENT_SECRET=YOUR_CLIENT_SECRET 9 | # Generate two random strings using `head -c32 /dev/urandom | base64` 10 | POMERIUM_SHARED_SECRET=YOUR_RANDOM_STRING 11 | POMERIUM_COOKIE_SECRET=YOUR_RANDOM_STRING 12 | -------------------------------------------------------------------------------- /graph.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | rankdir=LR; 3 | compound=true; 4 | subgraph cluster_0 { 5 | style=filled; 6 | color=lightgrey; 7 | node [style=filled,color=white]; 8 | label = "your-dns"; 9 | subgraph cluster_2 { 10 | style=filled; 11 | color=grey; 12 | label="Adguard Home" 13 | adguard_admin [label="Admin UI"]; 14 | adguard_dns [label="Encrypted DNS"]; 15 | } 16 | } 17 | client -> adguard_dns [label="DoT or DoH"]; 18 | 19 | admin -> adguard_admin [label="(via Pomerium)"]; 20 | 21 | adguard_dns -> google_dns [label="DNS-over-HTTPS"]; 22 | adguard_dns -> cloudflare_dns [label="DNS-over-HTTPS"]; 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Yuchen Ying 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | x-ssl-service: 4 | volumes: 5 | - &dns-ssl-cert 6 | ./pomerium/certificates/acme-v02.api.letsencrypt.org-directory/${DNS_DOMAIN_NAME}/${DNS_DOMAIN_NAME}.crt:/ssl.crt:ro 7 | - &dns-ssl-key 8 | ./pomerium/certificates/acme-v02.api.letsencrypt.org-directory/${DNS_DOMAIN_NAME}/${DNS_DOMAIN_NAME}.key:/ssl.key:ro 9 | - &autocert 10 | ./pomerium:/certificates 11 | 12 | services: 13 | adguard: 14 | container_name: adguard 15 | image: docker.io/adguard/adguardhome 16 | volumes: 17 | - *dns-ssl-cert 18 | - *dns-ssl-key 19 | - ./adguard/work:/opt/adguardhome/work 20 | - ./adguard/conf:/opt/adguardhome/conf 21 | environment: 22 | - TZ=${TZ} 23 | ports: 24 | # NOTE: if you also want to access adguard via plaintext DNS query, 25 | # uncomment the lines below. 26 | # WARNING: Only enable these if you run your server in a LAN. 27 | # - 53:53/tcp 28 | # - 53:53/udp 29 | # - 67:67/udp 30 | - 853:853/tcp 31 | networks: 32 | default: 33 | ipv4_address: 172.30.1.1 34 | 35 | ouroboros: 36 | container_name: ouroboros 37 | image: docker.io/pyouroboros/ouroboros:latest 38 | restart: unless-stopped 39 | volumes: 40 | - /var/run/docker.sock:/var/run/docker.sock 41 | environment: 42 | - SELF_UPDATE=true 43 | - CLEANUP=true 44 | - INTERVAL=3600 45 | 46 | pomerium: 47 | image: docker.io/pomerium/pomerium:latest 48 | container_name: pomerium 49 | restart: unless-stopped 50 | hostname: ${POMERIUM_DOMAIN_NAME} 51 | environment: 52 | - IDP_PROVIDER=google 53 | - IDP_PROVIDER_URL=https://accounts.google.com 54 | - IDP_CLIENT_ID=${POMERIUM_CLIENT_ID} 55 | - IDP_CLIENT_SECRET=${POMERIUM_CLIENT_SECRET} 56 | - AUTHENTICATE_SERVICE_URL=https://${POMERIUM_DOMAIN_NAME} 57 | - SHARED_SECRET=${POMERIUM_SHARED_SECRET} 58 | - COOKIE_SECRET=${POMERIUM_COOKIE_SECRET} 59 | - AUTOCERT_DIR=/certificates 60 | volumes: 61 | - ./pomerium.yaml:/pomerium/config.yaml:ro 62 | - *autocert 63 | ports: 64 | - 443:443 65 | - 80:80 66 | sysctls: 67 | net.ipv6.conf.all.disable_ipv6: 0 68 | 69 | autoheal: 70 | image: docker.io/willfarrell/autoheal 71 | container_name: autoheal 72 | restart: always 73 | environment: 74 | - AUTOHEAL_CONTAINER_LABEL=all 75 | volumes: 76 | - /var/run/docker.sock:/var/run/docker.sock 77 | 78 | networks: 79 | default: 80 | external: 81 | name: infra_network 82 | -------------------------------------------------------------------------------- /adguard/conf/AdGuardHome.yaml.example: -------------------------------------------------------------------------------- 1 | bind_host: 0.0.0.0 2 | bind_port: 80 3 | users: [] 4 | http_proxy: "" 5 | language: "" 6 | rlimit_nofile: 0 7 | debug_pprof: false 8 | web_session_ttl: 720 9 | dns: 10 | bind_host: 0.0.0.0 11 | port: 53 12 | statistics_interval: 90 13 | querylog_enabled: true 14 | querylog_interval: 90 15 | querylog_size_memory: 1000 16 | anonymize_client_ip: false 17 | protection_enabled: true 18 | blocking_mode: null_ip 19 | blocking_ipv4: "" 20 | blocking_ipv6: "" 21 | blocked_response_ttl: 10 22 | parental_block_host: family-block.dns.adguard.com 23 | safebrowsing_block_host: standard-block.dns.adguard.com 24 | ratelimit: 0 25 | ratelimit_whitelist: [] 26 | refuse_any: true 27 | upstream_dns: 28 | - https://1.1.1.1/dns-query 29 | - https://8.8.8.8/dns-query 30 | bootstrap_dns: 31 | - 9.9.9.10 32 | - 149.112.112.10 33 | - 2620:fe::10 34 | - 2620:fe::fe:10 35 | all_servers: true 36 | fastest_addr: false 37 | allowed_clients: [] 38 | disallowed_clients: [] 39 | blocked_hosts: [] 40 | cache_size: 4194304 41 | cache_ttl_min: 0 42 | cache_ttl_max: 0 43 | bogus_nxdomain: [] 44 | aaaa_disabled: false 45 | enable_dnssec: false 46 | edns_client_subnet: false 47 | filtering_enabled: true 48 | filters_update_interval: 24 49 | parental_enabled: false 50 | safesearch_enabled: false 51 | safebrowsing_enabled: false 52 | safebrowsing_cache_size: 1048576 53 | safesearch_cache_size: 1048576 54 | parental_cache_size: 1048576 55 | cache_time: 30 56 | rewrites: [] 57 | blocked_services: [] 58 | tls: 59 | enabled: true 60 | server_name: "" 61 | force_https: false 62 | port_https: 443 63 | port_dns_over_tls: 853 64 | allow_unencrypted_doh: false 65 | strict_sni_check: false 66 | certificate_chain: "" 67 | private_key: "" 68 | certificate_path: /ssl.crt 69 | private_key_path: /ssl.key 70 | filters: 71 | - enabled: true 72 | url: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt 73 | name: AdGuard Simplified Domain Names filter 74 | id: 1 75 | - enabled: true 76 | url: https://adaway.org/hosts.txt 77 | name: AdAway 78 | id: 2 79 | - enabled: true 80 | url: https://hosts-file.net/ad_servers.txt 81 | name: hpHosts - Ad and Tracking servers only 82 | id: 3 83 | - enabled: true 84 | url: https://www.malwaredomainlist.com/hostslist/hosts.txt 85 | name: MalwareDomainList.com Hosts List 86 | id: 4 87 | - enabled: true 88 | url: https://raw.githubusercontent.com/yegle/pihole-list/master/hosts 89 | name: yegle's ads list 90 | id: 1590570158 91 | whitelist_filters: [] 92 | user_rules: [] 93 | dhcp: 94 | enabled: false 95 | clients: [] 96 | log_file: "" 97 | verbose: false 98 | schema_version: 6 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # your-dns 2 | A docker-compose file to provide a secure adblocking DNS server 3 | 4 | **NOTE**: if you are interested in a hosted solution, please take a look at 5 | [nextdns.io](https://nextdns.io). I'm not affiliated with nextdns.io. 6 | 7 | **NEW**: Try using `your-dns.run` as a DNS-over-TLS server. You can use this 8 | domain with "Private DNS" feature in > Android 9 (Pie). This server is set up 9 | using the `your-dns-run` branch of this repo. 10 | 11 | ## Goal 12 | 13 | Run a secure DoT (DNS-over-TLS) and DoH (DNS-over-HTTPS) DNS server that 14 | can do ad blocking and hide your DNS query from your ISP. 15 | 16 | ## Non Goal 17 | 18 | Hide your DNS query from upstream recursive DNS server. Why? Because to 19 | me hide my trail from various ISPs (Verizon, ATT, and any other ISPs 20 | behind public WiFis) is more important. 21 | 22 | ## Privacy Tradeoffs 23 | 24 | We are running a DNS forwarder instead of a DNS resolver. Running a 25 | forwarder and connect to upstream DNS over secure connection does hide 26 | your DNS queries from your ISP, but it would also leaks your web history 27 | (in the form of DNS query) to the upstream DNS. 28 | 29 | Your web history is always open to your ISP until ESNI is widely 30 | adopted. Even with ESNI, it's still easy for the ISP to learn your web 31 | history based on the IP addresses you connected. 32 | 33 | The main benefit of running a forwarder that communicate securely with 34 | upstream DNS is that your ISP won't be able to manipulate your DNS query 35 | results, e.g. hijack the `NXDOMAIN` response to show ads, force traffic 36 | to go through a transparent proxy (with more and more sites offering 37 | HTTPS, this is less of a concern) and so on. 38 | 39 | There's a trade off you need to make whether the benefit beats the 40 | reduced privacy. Personally, making it harder for the ISP to learn my web 41 | history is a good enough reason. 42 | 43 | ## All components in this stack 44 | 45 | ![overview of components](https://g.gravizo.com/source/svg?https://raw.githubusercontent.com/yegle/your-dns/master/graph.dot) 46 | 47 | 1. [Adguard Home](https://github.com/AdguardTeam/AdGuardHome): Ad 48 | blocking DNS server with native DoT/DoH support. 49 | 1. [Pomerium](https://pomerium.io): An identity-aware reverse proxy. This 50 | allows me to remote access PiHole's web UI. More importantly, 51 | Pomerium is used to get SSL certificate automatically from Let's 52 | Encrypt. 53 | ([reference](https://www.pomerium.io/reference/)) 54 | 1. Optional: [Autoheal](https://github.com/willfarrell/docker-autoheal): 55 | Auto-restart container that failed health check. 56 | 1. Optional: [Ouroboros](https://github.com/pyouroboros/ouroboros): Auto-pull 57 | latest version of each container. 58 | 59 | **NOTE**: Previously Pihole+CoreDNS was used. That setup was deprecated. If 60 | you are still looking for that, take a look at the "pihole" branch. 61 | 62 | ## Prerequisites 63 | 64 | 1. Install Docker ([how](https://docs.docker.com/v17.12/install/)) and 65 | `docker-compose` command 66 | ([how](https://docs.docker.com/compose/install/)). 67 | 1. Know how to DNAT from your public IP to the server running the stack. 68 | Or alternatively if you have IPv6, allow dport=853 access to your 69 | server. 70 | 71 | ## Run the stack 72 | 73 | The following instruction will run a list of jobs on docker to 74 | DNS-over-TLS service on port 853 and foward your request through PiHole 75 | then to Cloudflare DNS. 76 | 77 | By default the setup uses Cloudflare's 1.1.1.1 DNS server. You can 78 | modify `Corefile` and specify a different server. A list of DNS-over-TLS 79 | name server is available at 80 | https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers. 81 | 82 | 1. Create a network called `infra_network`. (Why not create the network 83 | in the compose file? Because you cannot *create* the `default` network 84 | in compose file, and can only *replace* it with `external`.) 85 | ``` 86 | docker network create --subnet 172.30.0.0/16 infra_network 87 | ``` 88 | 1. Rename `example.env` to `.env` and update the values in the file. See 89 | the comment in that file for instructions. 90 | 1. Rename `adguard/conf/AdguardHome.yaml.example` to 91 | `adguard/conf/AdguardHome.yaml`. 92 | 1. Update the `tls_server_name` in `pomerium.yaml` to match the actual 93 | domain name you will use. 94 | 1. `docker-compose up -d` and you are done :-) 95 | 96 | ## TODO 97 | 98 | None 99 | --------------------------------------------------------------------------------