Files
fancy-dns/deploy/k8s/console/console.yaml
T
matsandClaude Sonnet 5.5 03d771430b Console: return [] for an empty group list, and fix its deployment manifest
GET /api/clients returned "groups": null when there were no groups, because the
merged list was a nil slice. Found against the real cluster.

The console manifest, never applied before, had an image name that does not
exist here, no image pull secret or architecture selector (the resolver's pods
have both), and pointed at a Service (fancy-dns-admin) that is not there. It
now uses the registry image, the pull secret, and the existing fancy-dns-web
Service.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 10:44:30 +02:00

119 lines
3.9 KiB
YAML

# The console: web UI plus the API behind it, as its own Deployment. It writes
# ClientGroup / BlockList / DNSZone objects and forwards everything else to the
# resolver's admin API. It is not on the DNS path; stopping it changes no answer.
#
# Secrets it needs (create them out of band):
# fancy-dns-console-runtime key FANCYDNS_RUNTIME_KEY : an API key the resolver accepts
# fancy-dns-console-apikey key FANCYDNS_API_KEY : the console's own machine key
apiVersion: v1
kind: ConfigMap
metadata:
name: fancy-dns-console
namespace: dns
data:
console.yaml: |
# The console has no blocklists or listeners of its own.
blocklist: {enabled: false}
api:
enabled: true
listen: ["0.0.0.0:8080"]
base_path: /api
ui: true
auth:
mode: oidc
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
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fancy-dns-console
namespace: dns
---
# The only component allowed to write the objects.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: fancy-dns-console
namespace: dns
rules:
- apiGroups: [fancydns.k6n.net]
resources: [dnszones, clientgroups, blocklists, addressoverrides, domainrules]
verbs: [get, list, create, update, delete]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: fancy-dns-console
namespace: dns
subjects:
- {kind: ServiceAccount, name: fancy-dns-console, namespace: dns}
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: fancy-dns-console}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: fancy-dns-console
namespace: dns
labels: {app.kubernetes.io/name: fancy-dns-console}
spec:
# One replica: OIDC login state and sessions are in memory, as in the
# resolver's console. More than one needs sticky routing.
replicas: 1
selector:
matchLabels: {app.kubernetes.io/name: fancy-dns-console}
template:
metadata:
labels: {app.kubernetes.io/name: fancy-dns-console}
spec:
serviceAccountName: fancy-dns-console
nodeSelector:
kubernetes.io/arch: amd64
imagePullSecrets:
- name: reg-k6n
securityContext:
runAsNonRoot: true
runAsUser: 65532
seccompProfile: {type: RuntimeDefault}
containers:
- name: console
image: registry.k6n.net/fancydns
imagePullPolicy: Always
command: ["/usr/local/bin/fancy-dns-console"]
args:
- -config=/etc/fancy-dns/console.yaml
# A Service in front of the resolver pods. With several replicas, the
# per-pod views (status, cache, upstreams) come from whichever pod answers.
- -runtime-url=http://fancy-dns-web.dns.svc:8080
env:
- name: FANCYDNS_RUNTIME_KEY
valueFrom: {secretKeyRef: {name: fancy-dns-console-runtime, key: FANCYDNS_RUNTIME_KEY}}
- name: FANCYDNS_API_KEY
valueFrom: {secretKeyRef: {name: fancy-dns-console-apikey, key: FANCYDNS_API_KEY}}
ports: [{name: http, containerPort: 8080}]
readinessProbe:
httpGet: {path: /api/healthz, port: http}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: [ALL]}
volumeMounts:
- {name: config, mountPath: /etc/fancy-dns, readOnly: true}
volumes:
- name: config
configMap: {name: fancy-dns-console}
---
apiVersion: v1
kind: Service
metadata:
name: fancy-dns-console
namespace: dns
spec:
selector: {app.kubernetes.io/name: fancy-dns-console}
ports: [{name: http, port: 80, targetPort: http}]