New in version 1.10.0: Intial release with QNAME and Response IP Address triggers
Changed in version 1.14.0: Full set of RPZ triggers and actions are supported
Response Policy Zones¶
Response Policy Zones (RPZ) is a mechanism that makes it possible to define your local policies in a standardised way and load your policies from external sources.
Introduction¶
Unbound has support for local-zone and local-data. This makes it possible to
give a custom answer back for specified domain names. It also contains the
respip
module which makes it possible to rewrite answers containing specified
IP addresses. Although these options are heavily used by users, they are Unbound
specific. If you operate multiple resolvers from multiple vendors you have to maintain
your policies for multiple configurations, which all will have their own syntax.
Using the Unbound specific configuration also makes it challenging to consume
policies from external sources.
To get these external sources to work manually, you have to fetch the external policies in the offered format, reformat it in such a way that Unbound will understand, and keep this list up-to-date, for example using unbound-control(8).
To automate this process in a generic, standardised way, Response Policy Zones (RPZ) is a policy format that will work on different resolver implementations, and that has capabilities to be directly transferred and loaded from external sources.
We’ll first discuss the different policies and RPZ actions with examples, and then show how to implement RPZ in a configuration.
RPZ Policies¶
RPZ policies are formatted in DNS zone files. This makes it possible to easily consume and keep them to up-to-date by using DNS zone transfers. Something that Unbound is already capable of doing for its auth-zone feature.
Each policy in the policy zone consists of a trigger and an action. The trigger describes when the policy should be applied. The action describes what action should be taken if the policy needs to be applied. Each trigger and action combination is defined as a Resource Record (RR) in the policy zone. The owner of the RR states the trigger, the type and RDATA state the action.
Unbound supports all the RPZ policies described in the RPZ internet draft:
Trigger |
Description and example |
---|---|
|
The query name: |
|
The IP address of the client: |
|
response IP address in the answer: |
|
The nameserver name: |
|
The nameserver IP address: |
Note that the IP address encoding for RPZ triggers in the IN-ADDR.ARPA naming
convention. So 192.0.2.24
will be written as 24.2.0.192
.
In the implementation step we will go trough all the triggers.
RPZ Actions¶
Aside from RPZ triggers, RPZ also specifies actions as a result of these triggers. Unbound currently supports the following actions: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, and TCP-only.
The Local Data action responds with a preconfigured resource record. Queries for types that do not exist in the policy zones will result in a NODATA answer.
Other RPZ actions that are supported by Unbound are the NXDOMAIN, NODATA, PASSTHRU, DROP and TCP-Only actions. All of these actions are defined by having a CNAME to a specific name.
The CNAME targets for the other RPZ actions are:
Action |
RR type and RDATA |
---|---|
|
|
|
|
|
|
|
|
|
|
The NODATA action returns a response with no attached data. The DROP action ignores (drops) the query. The TCP-Only action responds to the query over TCP. The PASSTHRU action makes it possible to exclude a domain, or IP address, from your policies so that if the PASSTHRU action is triggered no other policy from any of the available policy zones will be applied.
How to use RPZ with Unbound¶
The RPZ implementation in Unbound depends on the respip
module, this module
needs to be loaded using module-config:.
Each policy zone is configured in
Unbound using the rpz: clause.
The full documentation for RPZ in Unbound can be found in the
unbound.conf(5).
A minimal configuration with a single policy zone can look like the following,
where additional elements can be uncommented:
server:
module-config: "respip validator iterator"
rpz:
# The name of the RPZ authority zone
name: rpz.nlnetlabs.nl
# The filename where the zone is stored. If left empty
zonefile: rpz.nlnetlabs.nl
# The location of the remote RPZ zonefile.
# url: http://www.example.com/example.org.zone (not a real RPZ file)
# Always use this RPZ action for matching triggers from this zone.
# Possible action are: nxdomain, nodata, passthru, drop, disabled,
# and cname.
# rpz-action-override: nxdomain
# Log all applied RPZ actions for this RPZ zone. Default is no.
# rpz-log: yes
# Specify a string to be part of the log line.
# rpz-log-name: nlnetlabs
In above example the policy zone will be loaded from the file
rpz.nlnetlabs.nl
. An example RPZ file with all the triggers and actions
looks like this:
$ORIGIN rpz.nlnetlabs.nl.
# QNAME trigger with local data action
example.com.rpz.nlnetlabs.nl. TXT "trigger for example.com"
*.example.com CNAME .
# IPv4 subnet (192.0.2.0/28) which drops clients and IPv6 subnet
(2001:db8::3/128) which is not subject to policy
28.0.2.0.192.rpz-client-ip CNAME rpz-drop.
128.3.zz.db8.2001.rpz-client-ip CNAME rpz-passthru.
# Clients at 192.2.0.64 only get responses over TCP.
64.2.0.192.rpz-client-ip CNAME rpz-tcp-only.
# Fills the responses for these queries with NXDOMAIN and the correct
# answers respectively
24.0.2.0.192.rpz-ip CNAME .
32.2.2.0.192.rpz-ip CNAME rpz-passthru.
# Answers queries for the nlnetlabs.nl nameserver with NXDOMAIN
ns.nlnetlabs.nl.rpz-nsdname CNAME .
# Drops queries for the nameserver at 192.0.2.0/24 subnet
24.0.2.0.192.rpz-nsip CNAME rpz-drop.
It is also possible to load the zone using DNS zone transfers. Both AXFR and IXFR is supported, all additions and deletion in the zone will be picked up by Unbound and reflected in the local policies. Transferring the policy using a DNS zone transfer is as easy as specifying the server to get the zone from:
server:
module-config: "respip validator iterator"
rpz:
name: rpz.nlnetlabs.nl
master: <ip address of server to transfer from>
zonefile: rpz.nlnetlabs.nl
The zone will now be transferred from the configured address and saved to a zonefile on disk. It is possible to have more than one policy zone in Unbound. Having multiple policy zones is as simple as having multiple rpz: clauses:
server:
module-config: "respip validator iterator"
rpz:
name: rpz.nlnetlabs.nl
zonefile: rpz.nlnetlabs.nl
rpz:
name: rpz2.nlnetlabs.nl
zonefile: rpz2.nlnetlabs.nl
The policy zones will be applied in the configured order. In the example,
Unbound will only look at the rpz2.nlnetlabs.nl
policies if there is no
match in the rpz.nlnetlabs.nl
zone. If there is no match in any of the
configured zones Unbound will continue to resolve the domain by sending upstream
queries. Note that a PASSTHRU action is considered a match, having that action
in the first zone will therefore stop Unbound from looking further at other
policy zones.
Unbound has the possibility to override the actions that will be used for
policies in a zone that matches the zone’s triggers. This can be done using the
rpz-action-override: configuration
option.
The possible values for the option are: nxdomain
, nodata
, passthru
,
drop
, disabled
, and cname
.
The first four options of this list will do the same as the RPZ actions with
the same name.
The cname
override option will make it possible to apply a local data action
using a CNAME for all matching triggers in the policy zone.
The CNAME to use in the answer can be configured using the
rpz-cname-override: configuration
option.
Using these overrides is nice if you use an external feed to get a list of
triggers, but would like to redirect all your users to your own domain:
RPZ zone (rpz.nlnetlabs.nl):
$ORIGIN rpz.nlnetlabs.nl.
drop.example.com.rpz.nlnetlabs.nl. CNAME rpz-drop.
32.34.216.184.93.rpz-ip.rpz.nlnetlabs.nl. A 192.0.2.1
This also requires a change in the Unbound configuration:
server:
module-config: "respip validator iterator"
rpz:
name: rpz.nlnetlabs.nl
zonefile: rpz.nlnetlabs.nl
rpz-action-override: cname
rpz-cname-override: "example.nl."
The disabled
option will stop Unbound from applying any of the actions in
the zone. This, combined with the rpz-log
option, is a nice way to test what
would happen to your traffic when a policy will be enabled, without directly
impacting your users. The difference between disabled
and passthru
is
that disabled is not considered to be a valid match and will therefore not stop
Unbound from looking at the next configured policy zone.
When rpz-log: is set to yes, Unbound will log
all applied actions for a policy zone.
With rpz-log
enabled you can specify a name for the log using
rpz-log-name:, this way you can easily
find all matches for a specific zone.
It is also possible to get statistics per applied RPZ action using
unbound-control stats or
unbound-control stats_noreset.
This requires the extended-statistics:
to be enabled.
Unbound’s RPZ implementation works together with the tags functionality. This makes it possible to enable (some of) the policy zones only for a subset of users. To do this, the tags need to be defined using define-tag:, the correct tags need to be matched either with the client IP prefix using access-control-tag: or the clients on a listening interface using interface-tag:, and the tags need to be specified for the policy zones for which they apply.
server:
module-config: "respip validator iterator"
interface: eth0
define-tag: "malware social"
# Per client IP ...
access-control-tag: 127.0.0.10/32 "social"
access-control-tag: 127.0.0.20/32 "social malware"
access-control-tag: 127.0.0.30/32 "malware"
# ... and/or per listening interface
interface-tag: eth0 "social"
rpz:
name: malware.rpz.example.com
zonefile: malware.rpz.example.com
tags: "malware"
rpz:
name: social.rpz.example.com
zonefile: social.rpz.example.com
tags: "social"
Queries from 127.0.0.1 will not be filtered. For queries coming from 127.0.0.10 or the eth0 interface, only the policies from the social.rpz.example.com zone will be used. For queries coming from 127.0.0.30 only the policies from the malware.rpz.example.com zone will be used. Queries coming from 127.0.0.20 will be subjected to the policies from both zones.
See also
Response Policy Zone Options, module-config, define-tag, access-control-tag, and extended-statistics in the unbound.conf(5) manpage.