CoreDNS can drop DNS queries that match a specified blocklist, preventing them from reaching upstream servers and stopping clients from receiving potentially unwanted or malicious responses.
Let’s see it in action. Imagine you want to block all DNS queries for malicious-site.com and any subdomain like tracker.malicious-site.com.
Here’s a basic CoreDNS Corefile that achieves this:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
cache 30
forward . 8.8.8.8 1.1.1.1 {
max_concurrent 1000
}
# This is the crucial part
acl {
# Block specific domains
block malicious-site.com
block tracker.malicious-site.com
# Block a whole TLD (use with caution!)
# block .example.org
# Allow specific domains that might otherwise be blocked
# allow another-safe-site.com
# Default action if no match
# This is implicit, but you could be explicit:
# allow all
}
rewrite name malicious-site.com _malicious.blocked.local_
# If you want to completely drop the query and not even send a reply:
# drop
}
In this setup, the acl plugin is placed before the forward plugin. This is important because acl acts as a gatekeeper. If a query matches a rule in acl, it’s handled there and never reaches forward to be sent upstream.
Here’s how it works:
acl { ... }: This block defines the Access Control List.block malicious-site.com: This directive tells CoreDNS to block any query for the exact domainmalicious-site.com.block tracker.malicious-site.com: This blocks a specific subdomain. By default,aclalso blocks all subdomains of a blocked domain unless anallowrule explicitly permits them. So,block malicious-site.comwould also blockwww.malicious-site.com,api.malicious-site.com, etc.allow another-safe-site.com: If you have a broaderblockrule (like blocking an entire TLD) but need to permit a specific domain, you’d useallow.allowrules take precedence overblockrules. The order within theaclblock matters:allowrules are checked beforeblockrules.drop: If you add thedropdirective after yourblockandallowrules, CoreDNS will silently drop the query without sending any response to the client. This is a more aggressive approach than the default behavior ofacl, which typically returns aNXDOMAIN(Non-Existent Domain) or an empty answer.rewrite name malicious-site.com _malicious.blocked.local_: This is an alternative todrop. Instead of blocking the query outright, you can rewrite the requested domain to a known-good, non-routable domain. This effectively prevents the user from reaching the malicious site while still providing a DNS response. The client will attempt to resolve_malicious.blocked.local_, which will likely resolve to an invalid IP address orNXDOMAINitself, preventing access.
The acl plugin processes queries sequentially. When a query arrives, CoreDNS iterates through the acl block. If it encounters an allow rule that matches the query, it permits the query and stops processing the acl block. If it encounters a block rule that matches, it blocks the query and stops processing the acl block. If no rules match, the query proceeds to the next plugin in the Corefile (in this case, rewrite and then forward).
The acl plugin is remarkably efficient. When a query is blocked, CoreDNS simply returns a predetermined response (like NXDOMAIN) without performing any upstream lookups. This saves bandwidth and processing time on your DNS resolvers and upstream servers, and crucially, prevents clients from resolving malicious domains.
A common misconception is that acl only blocks exact domain matches. In reality, it’s quite powerful and can handle wildcard matching and subdomain blocking implicitly. For instance, block example.com will block www.example.com and mail.example.com by default.
The next step after implementing basic blocking is to integrate acl with external blocklists, often by using the file directive within the acl block to load domains from a text file.