DNS Response Policy Zones (RPZs) let you block malicious domains by injecting custom DNS responses, effectively telling clients that a domain doesn’t exist or should go somewhere else.

# Example: Querying a domain that should be blocked
dig @192.168.1.1 bad-actor.com

# Expected (RPZ configured): NXDOMAIN (Non-Existent Domain)
;; ANSWER SECTION:
bad-actor.com.		300	IN	NXDOMAIN

# Expected (RPZ configured): Redirect to a sinkhole IP
;; ANSWER SECTION:
bad-actor.com.		300	IN	A	192.0.2.1

The core problem RPZs solve is preventing users and systems on your network from reaching known malicious domains. Traditional methods like firewall rules or IP blacklists are often insufficient because malicious domains can be numerous, change frequently, or use a wide range of IP addresses. RPZs operate at the DNS resolution layer, allowing you to intercept and manipulate DNS queries before they resolve to a malicious IP.

Internally, an RPZ is a special type of DNS zone file. When a DNS server (like BIND or Unbound) is configured to use an RPZ, it consults this zone file for specific domains. If a domain being queried appears in the RPZ, the server doesn’t perform a regular DNS lookup; instead, it applies the policy defined in the RPZ for that domain. This policy can be:

  • NXDOMAIN: Respond with "Non-Existent Domain." The client gets no IP address and cannot connect.
  • CNAME: Redirect the query to another domain. This is often used to redirect to a "sinkhole" domain that resolves to a specific IP.
  • A/AAAA Record: Directly provide a specific IP address. This is the most common method for sinkholing, directing traffic to an IP address controlled by you, which can then log attempts or serve a block page.
  • NODATA: Respond that the domain exists but has no records of the requested type (e.g., no A record).

To implement this, you’ll typically configure your authoritative DNS server (or a caching resolver like BIND or Unbound acting as your internal DNS server) to load and use an RPZ zone.

Let’s say you’re using BIND. You’d define your RPZ zone in named.conf:

zone "rpz.local" {
    type master;
    file "/etc/bind/db.rpz.local";
    allow-query { none; }; // RPZ zones are not queried directly
};

And then, you’d instruct your server’s view (or the global options) to use this RPZ:

view "internal" {
    // ... other view configurations ...

    rpz-zone "rpz.local"; // Link the RPZ zone to this view
    rpz-log yes;          // Optional: Log RPZ hits
    rpz-nsid "RPZ-BIND";  // Optional: Add a Name Server Identifier to responses

    // ... other view configurations ...
};

The RPZ zone file (/etc/bind/db.rpz.local) would contain your blocking rules. For example, to block malicious-site.com and redirect phishing-domain.net to 192.0.2.1:

$TTL 300
@       IN      SOA     ns1.rpz.local. admin.rpz.local. (
                        2023102701 ; serial
                        3600       ; refresh
                        1800       ; retry
                        604800     ; expire
                        86400      ; minimum TTL
                        )

; Name Server records
@       IN      NS      ns1.rpz.local.
ns1     IN      A       127.0.0.1

; Blocking rules

; NXDOMAIN example: completely remove the domain
malicious-site.com      CNAME   .

; Redirect to sinkhole IP example
phishing-domain.net     A       192.0.2.1

; Another NXDOMAIN example using a special RPZ CNAME
compromised-server.org  CNAME   *.*.rpz-nxdomain.

The CNAME . syntax is a BIND-specific way to generate an NXDOMAIN response. The *.*.rpz-nxdomain is a special name that BIND recognizes to produce an NXDOMAIN. For phishing-domain.net, the A 192.0.2.1 rule tells BIND to respond with an A record pointing to 192.0.2.1 for any query about phishing-domain.net.

The "catch-all" mechanism in RPZs is incredibly powerful. You can use wildcard entries. For instance, *.bad-stuff.com CNAME . would block www.bad-stuff.com, mail.bad-stuff.com, and any other subdomain. Even more powerfully, *.bad-stuff.com (without the explicit *.) acts as a wildcard for any subdomain of bad-stuff.com.

A common misconception is that RPZs require a separate DNS server. While you can dedicate a server to RPZ, it’s far more common and efficient to integrate RPZs into your existing internal DNS infrastructure. This allows your internal DNS server to handle both regular lookups and policy enforcement simultaneously. The key is that the RPZ zone itself is not meant to be publicly authoritative; it’s a policy source for your resolver.

The next step after implementing basic blocking is to automate RPZ updates. Manually maintaining large blocklists is tedious and error-prone. You’ll want to explore tools or scripts that can fetch updated blocklists from threat intelligence feeds (like those provided by abuse.ch, OTX, or commercial vendors) and automatically convert them into the RPZ zone file format, triggering a DNS server reload.

Want structured learning?

Take the full Dns course →