Every Kubernetes workload finds its databases, APIs and peers through CoreDNS, so whoever can change the Corefile, or answer in its place, can quietly redirect traffic across the whole cluster. This guide explains how to run CoreDNS securely as cluster DNS and as a standalone server. It covers the Corefile and plugin chain, the coredns ConfigMap and the RBAC around it, the CoreDNS service account, the kubernetes plugin's pod record modes, upstream forwarding over TLS, the acl plugin, query logging and metrics, and the detections that show DNS being abused for spoofing or data exfiltration. It is written for platform engineers, Kubernetes administrators and detection engineers. For cluster-wide controls see the Kubernetes Security Guide; this page goes deeper on DNS.
This is an independent guide from NHI Mgmt Group, based on the CoreDNS project's public documentation as of November 2026. It is not sponsored or reviewed by the CoreDNS project. Product features change, so confirm settings against the CoreDNS project's current documentation.
Key takeaways
- The coredns ConfigMap is a cluster-wide routing table. Write access to
configmaps/corednsinkube-systemlets a user add a stub domain, rewrite or hosts entry that redirects any name, and the reload plugin applies it within about two minutes. Guard it like cluster-admin. - Choose the pod record mode deliberately. The default Kubernetes Corefile uses
pods insecurefor backward compatibility; CoreDNS warns it can be abused with wildcard TLS certificates. Usepods disabledif nothing needs pod records, orpods verifiedif something does. - Plugin order comes from plugin.cfg, not the Corefile. Execution order is fixed at build time, which is why
logsees a query beforeaclandaclacts beforecache,kubernetesandforward. - Verify upstreams.
forward . /etc/resolv.confsends external names to the node's resolvers in plain DNS; usetls://upstreams withtls_servernamewhere the path crosses networks you do not control. - Log and measure. The
logplugin withclass denial error, theerrorsplugin and Prometheus metrics on port 9153 are enough to spot tunnelling, scanning and configuration tampering when sent to your SIEM.
How identity and access work in CoreDNS
CoreDNS has no user accounts. Its security depends on three identities and one file:
| Identity or object | What it controls | Default in Kubernetes | Main risk if misused |
|---|---|---|---|
The Corefile (ConfigMap coredns, namespace kube-system) | Zones, plugins, upstreams, rewrites | Editable by anyone with update rights on that ConfigMap | Cluster-wide name redirection |
CoreDNS service account (coredns) | Reads cluster objects to build records | ClusterRole system:coredns: list and watch on endpoints, services, pods, namespaces and endpointslices | Read-only discovery of cluster topology if the token is taken |
| Clients (pods and nodes) | Who may query and recurse | Any pod with dnsPolicy: ClusterFirst | Tunnelling, enumeration, resolver abuse |
| Upstream resolvers | Answers for external names | /etc/resolv.conf of the node | Spoofed external answers on an untrusted path |
The upstream deployment manifest gives the service account only list and watch verbs, which is the right shape: CoreDNS needs to read, never write. Keep it that way when Helm charts or managed add-ons are upgraded, and check that no extra RoleBindings grant the coredns account more. The manifest also runs the container with allowPrivilegeEscalation: false, all capabilities dropped except NET_BIND_SERVICE, and readOnlyRootFilesystem: true. See the Kubernetes Service Account Token Guide for token handling.
When CoreDNS runs outside a cluster or talks to a remote API server, the kubernetes plugin authenticates with endpoint plus tls CERT KEY CACERT, or with a kubeconfig that may hold a token. Those files are machine credentials: mount them from a secret store, scope them to read-only access and rotate them.
Admin roles and privileged access
Privilege over cluster DNS is really privilege over three Kubernetes objects. Build roles around them:
- ConfigMap editors: only the platform team, through GitOps with review. No application team needs
updateorpatchonconfigmaps/coredns. - Deployment editors: rights to change the CoreDNS Deployment allow a new image, extra arguments or a different service account. Restrict
deploymentsinkube-systemto the same group. - Service and endpoint editors in
kube-system: thekube-dnsService ClusterIP is what every pod's/etc/resolv.confpoints at. Changing its selector moves cluster DNS to another pod.
Use an admission policy to reject ConfigMap changes that add hosts, rewrite or new forward targets outside an approved list (see the Kubernetes Admission Control Guide), and audit every change through the Kubernetes audit log. For role design see the Kubernetes RBAC Guide.
Integrations, API tokens and service accounts
The Corefile and plugin chain
Server blocks define the zones and port (default 53) a server answers. The CoreDNS manual explains that the order of plugins in the Corefile does not set execution order; plugin.cfg does, and it is fixed when the binary is built. In the standard build, log runs before acl, and acl runs before cache, hosts, kubernetes and forward, so blocked queries are still logged and never reach the cache or an upstream. A query that reaches the end of the chain unanswered returns SERVFAIL. Corefiles can import snippets and substitute environment variables with {$ENV_VAR}; review every imported file because it changes the running configuration.
Security-relevant plugins
| Plugin | Security use | Recommended setting |
|---|---|---|
kubernetes | Service and pod records | pods disabled or pods verified; limit with namespaces or labels where a server block serves a subset; scope fallthrough to named zones |
acl | Allow, block (REFUSED), filter (empty NOERROR) or drop by query type and source | Default action is allow, so end lists with an explicit block for net * where you need a closed list |
forward | Upstream resolution | tls:// upstreams with tls_servername; max_concurrent to cap load |
log and errors | Query and error records | log . { class denial error } for steady state |
prometheus | Metrics on :9153 | Reachable only by your monitoring namespace |
chaos | Answers version.bind | Leave out unless needed, so the version is not disclosed |
bind | Interfaces CoreDNS listens on | Use on standalone servers with several interfaces |
hosts, rewrite, template | Static or rewritten answers | Change only through review; alert on additions |
Pod records and spoofing inside the cluster
With pods insecure, CoreDNS returns an A record for any IP written in pod name form without checking that a pod has that address. The plugin documentation warns this can be abused together with wildcard TLS certificates: a name under the pod zone can be made to resolve to an arbitrary internal address while a wildcard certificate appears valid for it. pods verified answers only when a pod in that namespace holds the IP, at the cost of more memory because CoreDNS watches all pods. Our recommendation: start with pods disabled, check DNS error metrics and application logs in a staging cluster for a week, and move to verified only for workloads that genuinely need pod names. Avoid issuing wildcard certificates for cluster zones.
Other in-cluster spoofing paths are controlled outside CoreDNS. A pod with dnsPolicy: None and a custom dnsConfig, or with hostNetwork, can use a resolver you do not run. Restrict these with admission policy, and use NetworkPolicy so that pods can send UDP and TCP 53 only to the CoreDNS pods, which also stops workloads querying external resolvers directly.
Upstream forwarding
For tls:// upstreams CoreDNS sets a minimum of TLS 1.2. The forward documentation strongly recommends tls_servername (or per-endpoint SNI written as tls://IP%servername), because without it the upstream's identity is not verified. tls CERT KEY CA adds client authentication for private resolvers. Use except to keep internal zones off public resolvers, and leave health checking on, since max_fails 0 treats every upstream as healthy. Pointing the forwarder at your organisation's protective resolver gives cluster workloads the same filtering as the rest of the estate; see the Protective DNS Guide.
Logging and detection
The log plugin writes one line per query in Common Log Format: client IP and port, query ID, type, class, name, protocol, sizes, DNSSEC OK bit, response code, flags and duration. Its class filter accepts success, denial (NXDOMAIN or NODATA), error (SERVFAIL, NOTIMP, REFUSED) and all (the default). Full logging has a performance cost on busy clusters, so many teams log denial error continuously and switch to all during an investigation, or use the dnstap plugin to stream queries to a collector. Ship CoreDNS container logs to your SIEM with the pod's namespace attached so client IPs map back to workloads. See the DNS Logging and Detection Guide.
| Behaviour | Signal | ATT&CK |
|---|---|---|
| DNS tunnelling or command and control | One workload sending many long, high-entropy labels or TXT queries to a single external domain | T1071.004 |
| Data exfiltration over DNS | Sustained unique subdomains with large query sizes from one pod | T1048 |
| Service enumeration | Bursts of NXDOMAIN for *.svc.cluster.local names, SRV sweeps or reverse lookups across the service CIDR | T1046 |
| Redirection through configuration | Audit events for update or patch on configmaps/coredns; new forward, hosts or rewrite lines | T1557 |
| Resolver overload | Rising REFUSED from max_concurrent, SERVFAIL spikes, latency histogram growth | T1498 |
Pair CoreDNS logs with the Kubernetes Audit Log Detection Guide so ConfigMap and Deployment changes are caught at the API server, not only in query patterns.
Metrics that show the controls are working:
- Number of principals with write access to
configmaps/coredns(target: the platform group and GitOps controller only). - Percentage of clusters running
pods disabledorpods verified. - Percentage of namespaces with NetworkPolicy limiting egress port 53 to CoreDNS.
- Mean time to alert on a Corefile change made outside GitOps.
- Share of external forwarding sent over verified TLS upstreams.
Hardening baseline
- Inventory: for each cluster record the CoreDNS version, image source, Corefile, plugins in use and who can edit the ConfigMap and Deployment. Managed Kubernetes add-ons may own the ConfigMap; follow the provider's documented customisation method.
- Lock RBAC: remove broad
kube-systemwrite grants, keepsystem:corednsat list and watch, and manage the Corefile through GitOps. - Set pod records: change
pods insecuretopods disabledin staging, watch for resolution errors, then roll out; useverifiedwhere required. - Restrict clients: add NetworkPolicy for DNS egress, and on standalone servers use
bindandaclwith an explicit final block. - Secure upstreams: move to
tls://forwarders withtls_servernamewhere the path is untrusted, and useexceptfor internal zones. - Turn on logging: add
log . { class denial error }, keeperrors, scrape metrics on 9153 and limit that port to monitoring. - Keep the pod locked down: retain the non-root, read-only, capability-dropped security context from the upstream manifest, and apply CoreDNS security releases promptly following the project's security release process.
Testing and rollback: keep the previous ConfigMap in Git. Test each Corefile change in a staging cluster with a canary pod running lookups for services, external names and reverse records. The reload plugin applies changes without restarts, and the loop plugin halts CoreDNS if it detects a forwarding loop, so watch pod health for a few minutes after each change and revert the commit if lookups fail.
Small and large clusters: a single small cluster gets most of the value from steps 2, 3 and 6. Multi-tenant or regulated clusters should add admission policy on the ConfigMap, DNS egress NetworkPolicy in every namespace, dnstap to a central collector and TLS upstreams to a protective resolver.
Framework mapping
| Control area | CoreDNS control | NIST SP 800-53 Rev 5 | NIST CSF 2.0 |
|---|---|---|---|
| Resolver integrity | Verified TLS upstreams, pod record mode | SC-21 | PR.PS-01 |
| Name service architecture | Separate internal zones, except, NetworkPolicy | SC-22 | PR.PS-01 |
| Least privilege | RBAC on ConfigMap and service account | AC-6 | PR.AA-05 |
| Configuration control | GitOps, admission policy on Corefile | CM-6 | PR.PS-01 |
| Least functionality | Drop chaos, unused plugins | CM-7 | PR.PS-01 |
| Monitoring | log, errors, metrics, audit log | AU-12, AU-6 | PR.PS-04, DE.CM-01 |
Common misconfigurations
- Keeping
pods insecurebecause it shipped that way: the setting exists for backward compatibility. Most clusters can usedisabled; test and switch. - Broad edit rights in
kube-system: a CI service account or operator with namespace-wide edit can rewrite cluster DNS. Scope it to the objects it manages. - Assuming the acl plugin is deny by default: its default action is allow. Finish restrictive lists with a block rule, and remember the source IP is the packet source, which may be a NAT address.
- TLS upstream without a server name: encryption without identity checks. Set
tls_servernameor per-endpoint SNI. - Metrics port open to all namespaces: port 9153 shows query volumes and upstream details. Allow only the monitoring namespace.
- No DNS egress policy: pods that can reach any resolver bypass CoreDNS logging and filtering. Add NetworkPolicy for port 53.
Practitioner checklist
- List who can write the
corednsConfigMap, Deployment andkube-dnsService, and reduce it to the platform team and GitOps. - Confirm the
corednsservice account has only list and watch verbs. - Replace
pods insecurewithpods disabledorpods verifiedafter staging tests. - Add NetworkPolicy so pods reach port 53 only on CoreDNS.
- Use
tls://upstreams withtls_servernameon untrusted paths, andexceptfor internal zones. - Enable
logwithclass denial errorand ship logs with namespace metadata. - Alert on Corefile changes from the Kubernetes audit log.
- Restrict port 9153 to monitoring and drop the
chaosplugin unless needed. - Keep the hardened pod security context and track CoreDNS releases.
Standards and references
- CoreDNS: Manual
- CoreDNS: kubernetes plugin
- CoreDNS: acl plugin
- CoreDNS: forward plugin
- CoreDNS: log plugin
- CoreDNS: Kubernetes deployment manifest
- Kubernetes: Customizing DNS Service
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
Related NHI Mgmt Group resources: Kubernetes Security Guide · Kubernetes RBAC Guide · Kubernetes Audit Log Detection Guide · DNS Logging and Detection Guide · Kubernetes Service Account Token Guide