Files
fancy-dns/deploy/config.example.yaml
mats a3d1614dca
build / build-push (push) Successful in 1m8s
rework
2026-10-16 11:13:13 +02:00

232 lines
6.6 KiB
YAML

# fancy-dns — example configuration.
#
# Every value below is a default; delete what you do not need. The admin API
# rewrites this file when you save from the console, so keep it in git and
# review the diff.
version: 0
server:
# In-flight upstream resolutions. Excess queries get SERVFAIL rather than
# queueing without bound.
max_concurrent: 1024
timeouts:
udp: 2s
tcp: 4s
tls: 4s
query: 5s
upstream: 2s
list_fetch: 60s
dns:
udp: ["0.0.0.0:53", "[::]:53"]
tcp: ["0.0.0.0:53", "[::]:53"]
# Advertised EDNS0 buffer size for our own answers.
max_udp_size: 1232
serve_stale: 0s
edns0:
# Forward the client's EDNS-Client-Subnet so upstream picks a nearby
# anycast location.
client_subnet: true
ipv4_bits: 24
ipv6_bits: 56
preserve: true
dot:
enabled: false
listen: ["0.0.0.0:853"]
cert_file: /etc/fancy-dns/tls/fullchain.pem
key_file: /etc/fancy-dns/tls/privkey.pem
min_version: "1.2"
doh:
enabled: false
listen: ["0.0.0.0:443"]
base_path: /dns-query
cert_file: /etc/fancy-dns/tls/fullchain.pem
key_file: /etc/fancy-dns/tls/privkey.pem
min_version: "1.2"
allow_post: true
transport:
mode: auto
upstream:
# parallel: race them, take the first good answer. random: try N at random.
# failover: walk the list in order.
policy: parallel
max_attempts: 3
healthcheck_interval: 30s
breaker_threshold: 5
breaker_cooldown: 30s
servers:
- addr: 9.9.9.9:53
proto: udp
health_name: dns.quad9.net
- addr: 149.112.112.112:53
proto: udp
health_name: dns.quad9.net
- addr: 1.1.1.1:53
proto: tls
server_name: cloudflare-dns.com
- addr: 9.9.9.9:853
proto: tls
server_name: dns.quad9.net
# The memory envelope. A cgroup kill is an outage; a cache miss is not, so the
# runtime gets a soft ceiling to work towards instead of finding out the hard
# way. Set limit to your container's memory limit, minus the reserve.
memory:
# Soft heap ceiling (GOMEMLIMIT). 0 = unbounded. The Go heap excludes
# goroutine stacks, runtime metadata and the bbolt mmap for the cache
# snapshot and the query log, which is what the reserve is for.
limit: "0"
reserve: "256MiB"
# Byte budget for the in-memory LRU. 0 derives it from the limit (60%).
# cache.size alone does not bound memory: one cached ANY or TXT answer can
# be tens of kilobytes, so 500k entries is anywhere from 150MB to several GB
# depending on what the traffic asked for.
cache_max_bytes: "0"
cache:
enabled: true
size: 200000
shards: 32
min_ttl: 0s
max_ttl: 1h
# Keep expired answers around this long so a dead upstream costs latency,
# not availability.
serve_stale: 30m
prefetch: true
negative_ttl: 30m
persist:
enabled: true
path: data/cache.db
interval: 30s
max_entries: 500000
load_on_start: true
prewarm: []
blocklist:
enabled: true
# nxdomain | zero | redirect | drop | refused
default_action: nxdomain
response_ip: 0.0.0.0
response_ipv6: "::"
response_ttl: 60s
# RPZ-style policy zones and/or local authoritative data, as master files.
local_zones: []
# Inline rules. "*.example.com" covers subdomains, "**.example.com" covers
# the apex too, a bare name matches only itself.
block_entries: []
allow_entries: []
lists:
- name: haas
format: plain
url: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
path: data/lists/hosts
enabled: true
refresh: 24h
categories: [ads, tracking]
- name: easylist
format: adblock
url: https://easylist.to/easylist/easylist.txt
path: data/lists/easylist.txt
enabled: false
refresh: 24h
categories: [ads]
- name: local-rpz
format: rpz
path: zones/local.rpz.zone
enabled: true
refresh: 0s
acl:
# allow | deny | block_only
default_policy: allow
default_rate_limit: 0
groups:
- name: guests
comment: Blocklists apply, but the kitchen-sink allowlist does not.
match:
cidrs: ["192.168.90.0/24"]
policy: block_only
rate_limit: 50
- name: printers
comment: No recursion at all; they only need DHCP.
match:
cidrs: ["192.168.91.0/24"]
policy: deny
# One JSON line on stdout per query worth looking at, for whatever collects the
# pod's logs (Loki, a Grafana agent, `kubectl logs`). Nothing is stored here;
# the metrics count everything. The default is blocked queries and failures.
access_log:
enabled: true
log_blocked: true # blocklist hits and redirects
log_errors: true # SERVFAIL, drops, ACL refusals
log_cached: false # also cache hits
# Fraction of ordinary answers to log as well: 0 none, 1 all. For a short
# debugging session; leave at 0 otherwise.
sample_rate: 0
buffer: 4096 # lines that may wait for the log stream before dropping
cluster:
enabled: false
# node_id: dns-1
# advertise: http://10.0.0.10:8080
peer_ttl: 15s
valkey:
addrs: ["valkey:6379"]
prefix: fancydns
pool_size: 10
dial_timeout: 2s
read_timeout: 2s
# Only the lease holder fetches lists, so N instances do not hit a
# mirror N times.
leader_ttl: 30s
api:
enabled: true
listen: ["127.0.0.1:8080"]
# tls_listen: ["0.0.0.0:8443"]
base_path: /api
ui: true
allow_config_write: true
allow_blocklist_write: true
# Only these proxies may set X-Forwarded-For; anything else is ignored, so a
# client cannot spoof its identity past the ACL or the rate limiter.
trusted_proxies: []
cors_origins: []
# Optional buttons on the console's Status page. The resolver keeps no traffic
# history or query log of its own; these point at where they live.
# links:
# grafana: https://grafana.example/d/fancy-dns
# logs: https://grafana.example/explore?left=...
auth:
# none | apikey | basic | oidc
mode: apikey
api_keys: ["CHANGE-ME-long-random-string"]
read_only_api_keys: []
trust_loopback: true
session_ttl: 12h
# For mode: oidc, register a public PKCE client with this callback.
# Go exchanges codes and keeps tokens in an HttpOnly cookie session;
# no token-endpoint CORS setting is needed. The audience must be included
# in access tokens. Users need the admin role to write.
oidc_issuer: https://users.k6n.net
oidc_audience: https://fancy-dns.example.net
oidc_redirect_uri: https://fancy-dns.example.net/auth/oidc/callback
oidc_client_id: fancy-dns
oidc_write_roles: [admin]
rate_limit:
per_minute: 600
failures: 10
lockout: 5m
metrics:
enabled: true
path: /metrics
protect: true
log:
level: info
format: text