TLS Configuration

Enable encrypted DNS with DoT and DoH

Blockasaurus supports DNS-over-TLS (DoT, port 853) and DNS-over-HTTPS (DoH) for encrypted DNS resolution. TLS configuration depends on your deployment scenario. Choose the one that matches your setup:

ScenarioTLS?CertificateUse Case
1. No TLS No None Simple homelab, LAN only
2. Self-signed cert Yes Self-signed wildcard Homelab with DoT/DoH, no public domain
3. Let’s Encrypt Yes Wildcard via DNS-01 Custom domain, production deployment

Scenario 1: No TLS

The simplest setup. Clients send plain DNS queries over UDP/TCP to port 53. No certificates needed. Client identification uses source IP or EDNS CPE-ID.

Standalone config.yml

config.yml
databasePath: /var/lib/blockasaurus/blockasaurus.db

ports:
  dns: 53
  http: 4000

log:
  level: info

Helm values.yaml

values.yaml
config:
  ports:
    dns: 53
    http: 80
  databasePath: /data/blocky.db
  # Optional: enable CPE-ID for per-client identification
  # without needing TLS or subdomains
  clientGroupEndpoints:
    cpeId: true

service:
  dns:
    enabled: true
    type: LoadBalancer

Without TLS, DNS queries are sent in plain text. This is fine for a home network where you trust the local transport, but not suitable for remote clients or networks you don’t control.

Scenario 2: Self-Signed Certificate

For homelabs that want DoT and DoH but don’t have a public domain. You generate a self-signed wildcard certificate and distribute the CA to your devices.

Generate the Certificate

Create a self-signed CA and wildcard certificate for your local domain:

# Generate CA key and certificate
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key \
  -sha256 -days 3650 -out ca.crt \
  -subj "/CN=Blockasaurus CA"

# Generate server key
openssl genrsa -out tls.key 2048

# Create certificate signing request with SANs
cat > san.cnf <<EOF
[req]
distinguished_name = req_dn
req_extensions = v3_req
prompt = no

[req_dn]
CN = blockasaurus.local

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = blockasaurus.local
DNS.2 = *.blockasaurus.local
EOF

openssl req -new -key tls.key -out tls.csr -config san.cnf

# Sign with CA
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out tls.crt -days 825 \
  -extfile san.cnf -extensions v3_req

The wildcard *.blockasaurus.local is required for per-client-group subdomains (e.g., kids.blockasaurus.local). Include both the bare domain and the wildcard in the certificate’s Subject Alternative Names.

Standalone config.yml

config.yml
databasePath: /var/lib/blockasaurus/blockasaurus.db

certFile: /etc/blockasaurus/tls.crt
keyFile: /etc/blockasaurus/tls.key

ports:
  dns: 53
  http: 4000
  https: 443
  tls: 853

clientGroupEndpoints:
  domains:
    - blockasaurus.local
  cpeId: true

log:
  level: info

Helm values.yaml

First, create the TLS secret in your cluster:

kubectl create secret tls blockasaurus-tls \
  --cert=tls.crt --key=tls.key \
  -n blockasaurus
values.yaml
tls:
  enabled: true
  secretName: blockasaurus-tls

config:
  ports:
    dns: 53
    http: 80
    https: 443
    tls: 853
  databasePath: /data/blocky.db
  certFile: /certs/tls.crt
  keyFile: /certs/tls.key
  clientGroupEndpoints:
    domains:
      - blockasaurus.local
    advertiseAddress: auto
    cpeId: true

service:
  dns:
    enabled: true
    type: LoadBalancer
    dot: true
    http: true
    https: true

Trust the CA on Clients

Since the certificate is self-signed, each client device needs to trust your CA certificate (ca.crt). The web UI’s Client Groups setup guide provides platform-specific instructions, and iOS/macOS configuration profiles can include the CA automatically.

Client CA Trust
macOS Import ca.crt into Keychain Access, mark as trusted
iOS Install the config profile from the web UI (includes CA)
Windows Import into Trusted Root Certification Authorities
Android Settings → Security → Install certificate
Linux Copy to /usr/local/share/ca-certificates/ and run update-ca-certificates

Scenario 3: Let’s Encrypt Wildcard Certificate

The production setup. You own a domain (e.g., dns.example.com) and use Let’s Encrypt with DNS-01 validation to get a trusted wildcard certificate. No CA distribution needed — clients trust Let’s Encrypt by default.

Prerequisites

Standalone: certbot

Use certbot with a DNS plugin to obtain a wildcard certificate:

# Example with Cloudflare DNS plugin
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/cloudflare.ini \
  -d dns.example.com \
  -d "*.dns.example.com"
config.yml
databasePath: /var/lib/blockasaurus/blockasaurus.db

certFile: /etc/letsencrypt/live/dns.example.com/fullchain.pem
keyFile: /etc/letsencrypt/live/dns.example.com/privkey.pem

ports:
  dns: 53
  http: 80
  https: 443
  tls: 853

clientGroupEndpoints:
  domains:
    - dns.example.com
  cpeId: true

log:
  level: info

Set up a cron job or systemd timer to renew the certificate automatically. Blockasaurus will pick up the new certificate on restart.

Kubernetes: cert-manager

The Helm chart creates a cert-manager Certificate resource automatically when certificate.enabled: true:

values.yaml
certificate:
  enabled: true
  issuerRef:
    name: letsencrypt-dns01-cloudflare
    kind: ClusterIssuer
  dnsNames:
    - dns.example.com         # base domain
    - "*.dns.example.com"     # wildcard for per-group subdomains
  # secretName defaults to {fullname}-tls

tls:
  enabled: true
  secretName: blockasaurus-tls

config:
  ports:
    dns: 53
    http: 80
    https: 443
    tls: 853
  databasePath: /data/blocky.db
  certFile: /certs/tls.crt
  keyFile: /certs/tls.key
  clientGroupEndpoints:
    domains:
      - dns.example.com
    advertiseAddress: auto
    cpeId: true

service:
  dns:
    enabled: true
    type: LoadBalancer
    externalTrafficPolicy: Local  # preserves client source IPs
    dot: true
    http: true
    https: true

Both the base domain and wildcard are required in dnsNames. A wildcard cert (*.dns.example.com) does not cover the bare domain (dns.example.com).

Port Reference

PortProtocolServiceRequires TLS
53 UDP/TCP Plain DNS No
80 TCP HTTP (Web UI + DoH fallback) No
443 TCP HTTPS (DoH + Web UI) Yes
853 TCP DNS-over-TLS (DoT) Yes

Advanced: Separate Admin Port

If your DoH endpoint is internet-facing, you may want to restrict the web UI to an internal-only port. Use adminPort to separate the admin UI from the DNS server endpoint:

values.yaml (excerpt)
config:
  ports:
    dns: 53
    http: 80          # DNS server only (internet-facing DoH)
    https: 443        # DNS server only (internet-facing DoH)
    adminPort: 8080   # Web UI, API, metrics (internal only)
    tls: 853

service:
  dns:
    externalTrafficPolicy: Local
    dot: true
    http: true        # DoH on the LoadBalancer
    https: true
  adminPort:
    enabled: true     # Admin UI on a separate ClusterIP
    type: ClusterIP
    port: 8080

When adminPort is configured, the /metrics Prometheus endpoint moves to the admin port. Update your prometheus.io/port pod annotation accordingly.