DNS servers can be told to return specific answers for specific domain names, and Response Policy Zones (RPZs) are the standard way to do it.

Let’s say you’re running a BIND DNS server and you want to block access to malicious.example.com. You’d configure a zone in BIND, let’s call it rpz.local, that looks like this:

$TTL 1H
@       IN      SOA     ns1.example.com. admin.example.com. (
                        2023010101 ; serial
                        3H         ; refresh
                        1H         ; retry
                        1W         ; expire
                        1H )       ; negative TTL
;
        IN      NS      ns1.example.com.

malicious.example.com.  IN      A       127.0.0.1
bad-actor.example.com.  IN      A       127.0.0.1
phishing.site.net.      IN      A       127.0.0.1

When a client asks for malicious.example.com, BIND will consult rpz.local. It finds a record for malicious.example.com and returns 127.0.0.1 as the IP address. The client then tries to connect to 127.0.0.1 and, assuming nothing is listening there, the connection fails. You’ve effectively blocked access.

This is powerful because it happens at the DNS resolution stage, before any network connection is even attempted. It’s a lightweight and efficient way to filter traffic.

Here’s how it works under the hood with BIND:

  1. Zone Configuration: You define a zone (e.g., rpz.local) in your named.conf file and specify its type as rpz.

    zone "rpz.local" {
        type master;
        file "rpz.local.db"; // The file containing the RPZ rules
        allow-transfer { none; }; // Prevent zone transfers if not needed
    };
    
  2. RPZ Action Types: The records within your RPZ zone file define the action BIND should take. Common actions include:

    • A 127.0.0.1 (or any IP): Returns a specific IP address. This is what we used above. The client will try to connect to this IP.
    • CNAME .: This is a special "NXDOMAIN" (Non-Existent Domain) response. The client is told the domain doesn’t exist. This is often preferred for blocking as it prevents clients from even attempting a connection.
      malicious.example.com.  IN      CNAME   .
      
    • DNAME: This is used for redirection. If a client requests sub.malicious.example.com, and you have malicious.example.com DNAME redirect.example.com., the client will be redirected to resolve redirect.example.com.
    • CNAME to a different domain: Similar to redirection, but you can point it to a specific domain for logging or a sinkhole page.
      malicious.example.com.  IN      CNAME   blocked.example.net.
      
  3. RPZ Policies: You can apply RPZ zones to specific clients or client subnets using rpz-clients or rpz-policy clauses in your view or options blocks. This allows you to have different blocking policies for different user groups.

    options {
        // ...
        rpz-policy "rpz-policy-default";
        // ...
    };
    
    rpz-policy "rpz-policy-default" {
        rpz "rpz.local";
        // Other options like logging
    };
    
  4. Lookup Order: When a query arrives, BIND checks if the queried domain name matches any entry in an active RPZ zone before it checks its regular zone data. If a match is found and an action is defined, that action is taken.

The true power of RPZs isn’t just blocking; it’s in the control you have over the DNS resolution process. You can, for instance, define a rule that redirects all queries for domains within a certain TLD to a specific IP address that serves a "blocked" page.

.example.xyz. IN CNAME blocked-page.example.com.

This single rule would block all subdomains and direct queries for any.example.xyz and another.example.xyz to blocked-page.example.com.

What many people miss is that RPZs can also be used to override legitimate DNS records if needed, though this is rarely a good idea for production systems. You could, for example, create an RPZ zone that maps internal.company.com to a specific internal IP address, effectively ensuring that even if the public DNS record for internal.company.com changes, your internal users always resolve it to the correct internal IP. This is done by adding the RPZ zone to the view that serves your internal clients.

When you set up RPZs, it’s common to use a CNAME . (NXDOMAIN) for most blocks. This is because it immediately tells the client the domain doesn’t exist, preventing any further network activity. If you instead return A 127.0.0.1, the client will still attempt a TCP or UDP connection to that IP, which consumes resources on the client and potentially your network, even though it will fail.

After successfully blocking domains with RPZs, the next hurdle is often managing and updating these lists efficiently.

Want structured learning?

Take the full Bind course →