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 domain malicious-site.com.
  • block tracker.malicious-site.com: This blocks a specific subdomain. By default, acl also blocks all subdomains of a blocked domain unless an allow rule explicitly permits them. So, block malicious-site.com would also block www.malicious-site.com, api.malicious-site.com, etc.
  • allow another-safe-site.com: If you have a broader block rule (like blocking an entire TLD) but need to permit a specific domain, you’d use allow. allow rules take precedence over block rules. The order within the acl block matters: allow rules are checked before block rules.
  • drop: If you add the drop directive after your block and allow rules, CoreDNS will silently drop the query without sending any response to the client. This is a more aggressive approach than the default behavior of acl, which typically returns a NXDOMAIN (Non-Existent Domain) or an empty answer.
  • rewrite name malicious-site.com _malicious.blocked.local_: This is an alternative to drop. 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 or NXDOMAIN itself, 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.

Want structured learning?

Take the full Coredns course →