rework
build / build-push (push) Successful in 1m8s

This commit is contained in:
mats committed 2026-10-16 11:13:13 +02:00
1 parent 7fa48bcdf9
commit a3d1614dca
15 files changed
+704 -123

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
# Replace with a unique random secret before running. Keep the real .env private.
FANCYDNS_API_KEY=replace-with-a-long-random-value
+86
View File
@@ -0,0 +1,86 @@
# Run fancy-dns outside Kubernetes
This Compose deployment follows `deploy/k8s/00-config.yaml` and
`deploy/k8s/10-deployment.yaml`. It builds the image from this checkout instead
of pulling `registry.k6n.net/fancydns`, avoiding a bootstrap dependency on the
cluster registry DNS.
## Prepare and start
From the repository root:
```sh
mkdir -p deploy/compose/{data,zones,rpz,tls}
cp deploy/compose/.env.example deploy/compose/.env
```
Edit `deploy/compose/.env` (keep it private) and set a random API key as
`FANCYDNS_API_KEY=<long-random-value>`. Install the certificate and private key
for `dns.k6n.net` as `deploy/compose/tls/fullchain.pem` and
`deploy/compose/tls/privkey.pem`. Add the same zone files used by the cluster to
`deploy/compose/zones/`. Keep private key and `.env` out of source control.
The container runs as UID/GID 65532 with a read-only root filesystem, so make
the persistent directory writable first, for example:
```sh
sudo chown -R 65532:65532 deploy/compose/data
docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml build
docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml run --rm fancy-dns -config /etc/fancy-dns/config.yaml -check
docker compose --env-file deploy/compose/.env -f deploy/compose/compose.yaml up -d
```
The DNS ports publish on all host interfaces: UDP/TCP 53, TCP 853, and TCP 443.
The admin API is published only on `127.0.0.1:8080`; the embedded UI is disabled
by default for backup-resolver use. To enable it, set `api.ui: true` in
`config.yaml` and restart. Use an authenticated TLS reverse proxy for remote
access. Check that nothing already owns host port
53 before starting. The sample mounts TLS files and enables DoT and DoH, so the
files must exist before startup.
## How configuration is shared
The repository is the source of truth, but there is currently **no automatic
config distribution** between Kubernetes and the bare-metal Compose host.
Kubernetes GitOps reconciles `deploy/k8s` from `main` (see `gitops/README.md`);
the bare-metal host must get the updated checkout and have Compose restarted.
Kubernetes gets its config from the ConfigMap in `deploy/k8s/00-config.yaml`;
Compose reads the checked-in `deploy/compose/config.yaml` and files under
`deploy/compose/`. Treat these as two deployment configs: commit reviewed
changes to both when a shared resolver setting changes. The Compose config now
includes the same `local.` forward zone to `10.10.0.1:53` as Kubernetes.
The shared inputs to keep aligned are upstreams/forward zones, resolver policy,
cache sizing/TTL, blocklist sources and actions, ACLs, local zone files, RPZ
rules, and the DoT/DoH certificate for the same public DNS name. Deployment
specific settings intentionally differ: listener container ports/storage paths
must match each deployment, and Compose uses API-key auth with the UI disabled
while Kubernetes uses OIDC. On bare metal the YAML, zone and RPZ files are
bind-mounted; Kubernetes stores config, zones and RPZ in ConfigMaps and TLS in
a Secret. Secrets should be provisioned separately on each system, not copied
into Git. The Compose API key is in its private `.env` file.
Do not share the `data/` directory or copy its bbolt files while either server
is running. Cache snapshots and query logs are instance-local state. The
managed zones written by the console also live under this directory; edits
made on one instance do not appear on the other automatically. Make operator
zone changes in the source of truth and deploy them to both, or designate one
instance as the writer. Valkey clustering syncs cluster events/list leadership,
not these files or bbolt state.
The private forwarding target must remain reachable from both the Kubernetes
pods and the bare-metal host. Verify that the bare-metal resolver has outbound
access to its configured public upstreams before making it the cluster's
registry resolver.
For registry recovery, configure the Kubernetes nodes' OS resolver and the
container runtime's registry DNS path to use this host's **static IP directly**
as a DNS server (with another independent resolver as fallback). Do not rely on
a hostname for the backup resolver, and do not make pulling this Compose image
depend on `registry.k6n.net`: this Compose file builds the image locally from
the repository checkout. A working route from the cluster nodes to the
bare-metal host on UDP/TCP 53 is required; allow it through the host firewall.
To update the local build after code changes, rebuild and recreate the
container. The running server can hot-reload its YAML on SIGHUP, but Compose
does not automatically send SIGHUP when bind-mounted files change.
+39
View File
@@ -0,0 +1,39 @@
services:
fancy-dns:
# Build here so initial startup does not depend on the cluster registry
# whose name this resolver may be needed to resolve.
build:
context: ../..
dockerfile: Dockerfile
image: fancy-dns:local
container_name: fancy-dns
restart: unless-stopped
read_only: true
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
user: "65532:65532"
environment:
FANCYDNS_API_KEY: ${FANCYDNS_API_KEY:?Set FANCYDNS_API_KEY in deploy/compose/.env}
ports:
- "53:1053/udp"
- "53:1053/tcp"
- "853:1853/tcp"
- "443:8443/tcp"
# Keep the admin UI/API on the host loopback. Put it behind an
# authenticated TLS reverse proxy if remote access is needed.
- "127.0.0.1:8080:8080/tcp"
volumes:
- ./config.yaml:/etc/fancy-dns/config.yaml:ro
- ./zones:/etc/fancy-dns/zones:ro
- ./rpz:/etc/fancy-dns/rpz:ro
- ./tls:/etc/fancy-dns/tls:ro
- ./data:/var/lib/fancy-dns
tmpfs:
- /tmp:mode=1777,size=16m
healthcheck:
test: ["CMD", "/usr/local/bin/fancy-dns", "-version"]
interval: 30s
timeout: 3s
start_period: 10s
retries: 3
+114
View File
@@ -0,0 +1,114 @@
# Bare-metal equivalent of deploy/k8s/00-config.yaml.
# Keep this file, zones/, rpz/, and TLS material in sync with the cluster's
# ConfigMaps/Secret as appropriate. The persistent state in ./data is local
# to this host and must not be copied/shared while the server is running.
server:
max_concurrent: 2048
timeouts:
upstream: 2s
list_fetch: 60s
dns:
# Container ports stay unprivileged; Compose publishes the conventional
# host ports 53, 853 and 443.
udp: ["0.0.0.0:1053"]
tcp: ["0.0.0.0:1053"]
max_udp_size: 1232
dot:
enabled: true
listen: ["0.0.0.0:1853"]
cert_file: /etc/fancy-dns/tls/fullchain.pem
key_file: /etc/fancy-dns/tls/privkey.pem
doh:
enabled: true
listen: ["0.0.0.0:8443"]
cert_file: /etc/fancy-dns/tls/fullchain.pem
key_file: /etc/fancy-dns/tls/privkey.pem
base_path: /dns-query
upstream:
forward_zones:
- zone: local
servers:
- addr: 10.10.0.1:53
proto: udp
policy: parallel
max_attempts: 3
healthcheck_interval: 30s
servers:
- addr: 9.9.9.9:53
proto: udp
- addr: 1.1.1.1:853
proto: tls
server_name: cloudflare-dns.com
- addr: 9.9.9.9:853
proto: tls
server_name: dns.quad9.net
memory:
limit: 1GiB
reserve: 256MiB
cache_max_bytes: 384MiB
cache:
enabled: true
size: 500000
max_ttl: 1h
serve_stale: 30m
persist:
enabled: true
path: /var/lib/fancy-dns/cache.db
interval: 30s
blocklist:
enabled: true
default_action: nxdomain
managed_zones: /var/lib/fancy-dns/managed-zones
local_zones: ["/etc/fancy-dns/zones/*.zone"]
lists:
- name: edge-router-x-address-overrides
format: rpz
path: /etc/fancy-dns/rpz/address-overrides.rpz
enabled: true
refresh: 0s
- name: haas
format: plain
url: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
path: /var/lib/fancy-dns/lists/hosts
enabled: true
refresh: 24h
categories: [ads, tracking]
acl:
default_policy: allow
groups: []
qlog:
enabled: true
path: /var/lib/fancy-dns/queries.db
max_entries: 200000
sample_rate: 1.0
log_blocked: true
log_cached: false
api:
enabled: true
listen: ["0.0.0.0:8080"]
base_path: /api
# Backup resolver mode keeps the console disabled. Set true when an operator
# wants the embedded console; it remains reachable only via host loopback.
ui: false
allow_config_write: false
auth:
mode: apikey
api_keys: ["${FANCYDNS_API_KEY}"]
trust_loopback: true
metrics:
enabled: true
path: /metrics
protect: true
log:
level: info
format: json
+6
View File
@@ -0,0 +1,6 @@
$TTL 60
tornberg.me A 10.10.3.10
knatofs.se A 10.10.3.10
k6n.net A 10.10.3.10
slask.local A 10.10.3.10
iamu.dev A 10.10.3.10
+5 -2
View File
@@ -207,10 +207,13 @@ api:
read_only_api_keys: []
trust_loopback: true
session_ttl: 12h
# For mode: oidc, configure a registered public PKCE client and the API
# audience included in its access tokens. Users need the admin role to write.
# 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:
+1
View File
@@ -119,6 +119,7 @@ data:
api_keys: ["${FANCYDNS_API_KEY}"]
oidc_issuer: https://users.k6n.net
oidc_audience: https://dns.k6n.net
oidc_redirect_uri: https://dns.k6n.net/auth/oidc/callback
oidc_client_id: fancy-dns
oidc_write_roles: [admin]
trust_loopback: false