Skip to main content
Celum can answer every DNS query on a supervisor itself: a node DNS on each node caches and logs, and one central DNS holds the policy — where names are forwarded and which ones get a fixed answer. Every query, to any server, can be made to pass through it, which is what fills the DNS views under Security. Two pages configure it:
  • DNS in the sidebar’s global section (/dns) — Global DNS, declared once for every supervisor: forwarders, rules, blocking of encrypted DNS, and the malicious-domain sinkhole.
  • Networking → DNS on a supervisor — the switches that run it there, and what that supervisor adds to or overrides from Global.
Nothing changes until you turn it on: a supervisor where Run the DNS is off resolves exactly as its CoreDNS always did.

How a query travels

  • The node DNS is one CoreDNS per node, running the same image as the cluster’s own CoreDNS. It caches and sends every answered query to the cluster agent on its node.
  • The central DNS is two CoreDNS pods that hold the rules and forwarders, and filter encrypted-DNS server names and malicious domains when those blocks are on.
  • The redirect is a Cilium Local Redirect Policy on kube-dns: queries to the cluster’s DNS address go to the node DNS on the same node. It is a socket-level service redirect, not a proxy, so a cilium-agent restart loses no queries.
  • Capture covers workloads that pick their own resolver: in each pod’s and VM’s network namespace the cluster agent rewrites every query to port 53 to the node DNS on the same node.

Turning it on, in order

Each switch on Networking → DNS takes effect on Apply, and each needs the one before it.
1

Turn on Local Redirect Policies

The Status block offers this when Cilium has them off. It restarts every cilium-agent once, node by node, which briefly interrupts DNS and the traffic Cilium proxies on that node. Nothing is redirected yet.
2

Run the DNS

Starts a node DNS pod on every node and two central DNS pods, plus a cluster-wide policy that lets every pod reach the node DNS. Nobody’s DNS changes yet.
3

Redirect kube-dns to the node DNS

From here, a query to the cluster’s DNS is answered by the node DNS. Needs Local Redirect Policies.
4

Capture every query (DNAT)

A workload with a resolver of its own is answered — and logged — by the node DNS all the same. Needs the redirect, and rolls the cluster agents once.
5

Deny DNS anywhere else

Cluster-wide policies deny UDP and TCP 53 and 853 (DNS over TLS) from every pod to anything but the node DNS, the central DNS, CoreDNS and a vcluster’s own DNS. Needs capture, and is applied only once every node reports full capture; it is held back while a VM with bridge binding exists.
6

Query log

Every answered query goes to ClickHouse through the cluster agent on its node. It can be on without capture, but then a workload with its own resolver is not in it. Turning it on or off rolls the cluster agents once.
Result: the tiles at the top of the tab read Node DNS, Central DNS, Capture and Query log as on and running, and the Status block lists the redirected pods and the policies that are in force.
Not captured: hostNetwork pods, the DNS pods themselves, and VMs with bridge binding — the nodes of guest clusters. A bridged VM’s pod has no address to rewrite to, so its queries are left alone; guest nodes are pointed at the supervisor’s DNS through their Talos configuration instead. The Status block lists bridged VMs per node.

Forwarders, cache and rules

Forwarders are where the central DNS sends every name no rule takes, tried in order. Empty means the node’s own resolvers, the ones the cluster’s CoreDNS forwards to. Names under the cluster domain and every reverse name always go to the cluster’s CoreDNS. Cache (seconds) is the longest an answer is kept, on the node DNS and the central DNS alike. Answers about missing cluster names are kept 5 seconds at most. A rule matches one of:
  • a zone — the name and everything under it;
  • an exact name;
  • a pattern, where * is any characters;
optionally only for some record types. It either forwards to servers of its own, or answers with fixed values of one type (A, AAAA, MX or TXT; no CNAME). A fixed answer makes the name local: every other record type of it is answered with no record, so a client asking AAAA for a name with a fixed A does not conclude the name does not exist. Rules are applied most specific first, whatever order you declared them in: exact names, then patterns, then zones from the deepest, and a rule limited to record types before one that is not.
A zone that a rule forwards to its own servers is never retried against the cluster’s resolver. If those servers are down, clients get an uncached SERVFAIL and retry, rather than the public view of an internal zone answering NXDOMAIN and being cached.

Global DNS and what a supervisor overrides

Global DNS holds the same forwarders and rules, for every supervisor at once: Saving Global DNS rewrites every supervisor’s central DNS at once; a supervisor that was unreachable catches up within five minutes. CoreDNS takes a minute or two to reload.
A supervisor registered in two Celum installations (production and a test bench, say) is reconciled only by the one that last saved its DNS, so the two do not overwrite each other’s policy every few minutes. The other one says the supervisor is managed elsewhere.

Blocking encrypted DNS (DoH, DoT, DoQ)

Keeps DNS from leaving a supervisor uninspected over an encrypted channel. With Block DNS over HTTPS, TLS and QUIC on, the central DNS answers NXDOMAIN for the listed server names and every name under them, so a client that looks its server up gets no address.
  • The list is Celum’s shipped list of public resolvers (Cloudflare, Google, Quad9 and others), edited under Manage servers and lists, plus downloaded lists of names.
  • Nothing is blocked by address. A client with its server’s address written in is not stopped here; the network policies are where an address is denied.
  • A name a supervisor’s own rule forwards elsewhere does not pass the filter.

Answering malicious domains with a sinkhole

With Answer malicious domains with the sinkhole on, every name on the lists — exactly that name, not the names under it, because lists name hosts on shared platforms — is answered with the sinkhole address: A with the IPv4 one, AAAA with the IPv6 one, and every other record type with nothing.
  • 0.0.0.0 refuses at once; an internal block page shows the user why.
  • Manage lists holds the downloaded lists (fetched once a day while the block is on, or with Download now), names of your own, and names that are never sinkholed whatever a list says.
  • Who asked for a sinkholed name shows in Security → Blocked DNS.
Honeypot instead answers listed names with a honeypot rather than a sinkhole: one pod per supervisor on the first address of its egress band, reachable from every project, isolated ones included. It accepts every TCP and UDP port and ICMP, and records who connected and what they tried — TLS server name, HTTP request, first bytes — in Security → DNS Honeypot. The lists reach every supervisor as ConfigMaps, so filtering keeps working while Celum is out of reach.

The query log

Every answered query is written to dns_queries_local in the supervisor’s ClickHouse, with the client named — namespace, pod, workload, project, guest cluster — the upstream that answered, the latency, and whether it came from the cache. Rows are kept 14 days, and per-minute rollups 30 days. It is read on the Security pages: An empty DNS view usually means the query log is off on that supervisor, or the node DNS is not running — the tiles on Networking → DNS say which.

API and MCP

Turning on Local Redirect Policies is POST /api/supervisors/{name}/networking/cilium-feature with {"feature": "localRedirectPolicies", "enabled": true}, authorised by cilium:Apply.

What commonly goes wrong

Networking overview

The Cilium datapath the redirect and capture sit on.

Network policies

Denying an address, which DNS blocking does not do.

Security overview

Where the DNS views sit among the Security tabs.

Pools & IPAM

The egress band whose first address the honeypot takes.