DNS over TLS (DoT) on mac with stubby and dnsforge

This is a step-by-step guide on how to setup DNS over TLS (DoT) on mac with stubby

1. Install Stubby

Install stubby on your mac with homebrew

brew install stubby

2. Configure stubby

write the following config to /opt/homebrew/etc/stubby/stubby.yml

This config uses dnsforge as the encrypted DNS resolver, though you may use any encrypted DNS resolver of your choice.


################################################################################
######################## STUBBY YAML CONFIG FILE ###############################
################################################################################

################################### LOGGING ####################################
log_level: GETDNS_LOG_NOTICE

########################## BASIC & PRIVACY SETTINGS ############################
resolution_type: GETDNS_RESOLUTION_STUB

dns_transport_list:
  - GETDNS_TRANSPORT_TLS

# Strict mode - TLS auth REQUIRED, no plaintext fallback
tls_authentication: GETDNS_AUTHENTICATION_REQUIRED

# Pad queries to 128 bytes to prevent size-based traffic analysis
tls_query_padding_blocksize: 128

# Hide client subnet from upstream resolvers
edns_client_subnet_private: 1

############################# CONNECTION SETTINGS ##############################
# Distribute queries across all upstreams
round_robin_upstreams: 1

# Keep TLS connections alive for 10s to reduce handshake overhead
idle_timeout: 10000

# Retry/backoff settings
tls_connection_retries: 3
tls_backoff_time: 300

# Per-query timeout (ms)
timeout: 5000

# Force TLS 1.3 minimum
tls_min_version: GETDNS_TLS1_3

################################ LISTEN ADDRESS ################################
# Stubby listens locally on port 53
# Point your system DNS to 127.0.0.1
listen_addresses:
  - 127.0.0.1
  - 0::1

############################### DNSSEC SETTINGS ################################
# dnsforge.de performs DNSSEC validation upstream (ad flag confirmed)
# Uncomment below to enforce DNSSEC locally as well
# dnssec: GETDNS_EXTENSION_TRUE

##################################  UPSTREAMS  #################################
# dnsforge.de - no-log, DNSSEC-validating, ad-free resolver (DE)
# PIN verified via: kdig -d @ +tls-ca +tls-host=dnsforge.de example.com
###############################################################################

upstream_recursive_servers:

  ## dnsforge.de - IPv4 primary
  - address_data: 49.12.67.122
    tls_port: 853
    tls_auth_name: "dnsforge.de"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: m51QwAhzNDSa3G7c1Y6eOEsskzp6ySzeOqy0LKcptDw=

  ## dnsforge.de - IPv4 secondary
  - address_data: 91.99.154.175
    tls_port: 853
    tls_auth_name: "dnsforge.de"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: m51QwAhzNDSa3G7c1Y6eOEsskzp6ySzeOqy0LKcptDw=

  ## dnsforge.de - IPv6 primary
  - address_data: 2a01:4f8:c013:29d::122
    tls_port: 853
    tls_auth_name: "dnsforge.de"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: m51QwAhzNDSa3G7c1Y6eOEsskzp6ySzeOqy0LKcptDw=

  ## dnsforge.de - IPv6 secondary
  - address_data: 2a01:4f8:c013:29d::175
    tls_port: 853
    tls_auth_name: "dnsforge.de"
    tls_pubkey_pinset:
      - digest: "sha256"
        value: m51QwAhzNDSa3G7c1Y6eOEsskzp6ySzeOqy0LKcptDw=

3. Run stubby as a service

To make sure stubby runs on startup, run it as a service

sudo brew services start stubby

4. Change default DNS Server

Navigate to Settings -> Network -> Wi-Fi. Click on details for your wifi and navigate to DNS. Remove any present IPs or hostnames, and add two IPs 127.0.0.1 and ::1

And you're all set!