1 parent
7fa48bcdf9
commit
a3d1614dca
15 files changed
+704
-123
No files matched your search
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user