Description: Set up a personal DNS Firewall.
Overview: In working with Infoblox
DNS BloxOne Deployment I got to thinking why not deploy this on my own network to get an inexpensive way to control DNS requests. Here I ended up deploying a Primary and Secondary DNS Bind9 Server running on physical and virtual Ubuntu Servers. I leverage ioc2rpz
to sync community available RPZ’s then we leverage DNSCrypt-Proxy
project configuring a local listener that will send Recursive DNS lookups using DNSoverHTTPS.
Bind9 Server
Simple setup instructions can be found here
The instructions above will get the baseline Bind server up and running on a Ubuntu System. Once done you can configure your Replica or Secondary DNS server using similar setup, but adjusting the named.config.options file as follows:
Assuming the networks I am serving are 10.100.100.0/24 and 192.168.100.0/24 and my local domain is mynetwork.com (I use this for custom DNS entries if needed or setting up DDNS not covered here)
options {
directory "/var/cache/bind";
allow-query {
localhost;
192.168.100.0/24;
10.100.100.0/24;
};
forwarders {
8.8.8.8;
8.8.4.4;
// We will come back later and change this
}
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation auto;
// look for dnssec keys here:
key-directory "/etc/bind/keys";
// only sign DNSKEY with KSK
dnssec-dnskey-kskonly yes;
// expiration time 21d, refresh period 16d
sig-validity-interval 21 16;
edns-udp-size 4096;
recursion yes;
// Response Policies for RPZ
response-policy {
// FQDN allowlists
zone "mynetwork.com";
zone "rpz.whitelist";
// FQDN blocklist
zone "rpz.blacklist" policy nxdomain;
} qname-wait-recurse no break-dnssec yes;
auth-nxdomain no; // conform to RFC1035
allow-recursion {
127.0.0.1;
10.100.100.0/24;
192.168.100.0/24;
};
querylog yes;
};
NOTE: When writting to the /var/cache/bind directory the user running the named process requires read/write access to the directory. Ensure proper rights are given to this directory.
Next update the name.config.local file to configuring the Primary and Secondary servers locally as needed. Add the local RPZ files you want to superseed any allow or block list that will follow below:
Assuming
- Primary Server IP: 192.168.100.53
- Secondary Server IP: 10.100.100.53
//
// Do any local configuration here
//
// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";
zone "mynetwork.com" {
type master;
file "/etc/bind/forward.mynetwork.com";
allow-transfer {
10.100.100.53;
};
};
zone "rpz.whitelist" {
type master;
file "/etc/bind/db.rpz.whitelist";
allow-transfer {
10.100.100.53;
};
};
zone "rpz.blacklist" {
type master;
file "/etc/bind/db.rpz.blacklist";
allow-transfer {
10.100.100.53;
};
};
zone "100.100.10.in-addr.arpa" {
type master;
file "/etc/bind/100.100.10.in-addr.arpa";
allow-transfer {
10.100.100.53;
};
};
zone "100.168.192.in-addr.arpa" {
type master;
file "/etc/bind/100.168.192.in-addr.arpa";
allow-transfer {
10.100.100.53;
};
};
ioc2rpz
Intro video to ioc2rpz from Vadim Pavloc’s opensource project:
Configuration Files
Update response-policy section in named.config.options file:
Update response section
// Response Policies for RPZ
response-policy {
// FQDN allowlists
zone "mynetwork.com";
zone "rpz.whitelist";
// FQDN blocklist
zone "rpz.blacklist" policy nxdomain;
// ioc2rpz files
zone "covid19.ioc2rpz" policy nxdomain;
zone "notracking-dead.ioc2rpz" policy nxdomain;
zone "notracking.ioc2rpz" policy nxdomain;
zone "phishtank.ioc2rpz" policy nxdomain;
zone "dns-bh.ioc2rpz" policy nxdomain;
} qname-wait-recurse no break-dnssec yes;
Add key and hash to end of file
key "<key provided from ioc2rp>"{
algorithm hmac-sha256; secret "<sha265 hash from ioc2rpz>";
};
Add new RPZ zones to the Primary and Replica Servers as a Secondary. This is updated in the named.config.local file:
zone "notracking-dead.ioc2rpz" {
type slave;
file "/var/cache/bind/notracking-dead.ioc2rpz";
masters {
<IP Address Provided> key "<your key>";
};
};
zone "notracking.ioc2rpz" {
type slave;
file "/var/cache/bind/notracking.ioc2rpz";
masters {
<IP Address Provided> key "<your key>";
};
};
zone "phishtank.ioc2rpz" {
type slave;
file "/var/cache/bind/phishtank.ioc2rpz";
masters {
<IP Address Provided> key "<your key>";
};
};
zone "covid19.ioc2rpz" {
type slave;
file "/var/cache/bind/covid19.ioc2rpz";
masters {
<IP Address Provided> key "<your key>";
};
ixfr-from-differences no;
};
NOTE: ixfr-from-differences Defines how the server calculates incremental zone changes. Normally incremental zone transfers are only possible when used in conjunction with Dynamic DNS (DDNS). ixfr-from-diffrences allows the slave server to create incremental zone transfers for non-dynamic zones. If set to yes when the server receives a new version of a slave file by a non-incremental zone transfer it will compare the new version to the previous one and calculate a set of differences. The differences are then logged in the zone’s journal file (.jnl appended to zone file name) such that the changes can be transmitted to downstream slaves as an incremental zone transfer. This statement saves bandwidth at the expense of increased CPU and memory consumption. This statement may be used in a zone, view or global options clause.
When you restart the local named server using:
sudo systemctl restart named
You should now see the rpz zones downloaded and if you have logging enabled you can review the logs to see the domains being blocked depending on what RPZ’s you are subscribing to.
Example
If you review one of the RPZ files you set up you can find a Domain that is blocked and test to see if all is working. I’m using googleads.g.doubleclick.net in the below example. You can also look at your logs if you are logging your queires for a rewrite to find what’s being blocked as well.
Running a dig against a public DNS Name Server.
➜ ~ dig @8.8.8.8 googleads.g.doubleclick.net
; <<>> DiG 9.10.6 <<>> @8.8.8.8 googleads.g.doubleclick.net
;; QUESTION SECTION:
;googleads.g.doubleclick.net. IN A
;; ANSWER SECTION:
googleads.g.doubleclick.net. 299 IN A 172.217.15.194
;; Query time: 1051 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
Running a dig against my Internal DNS Servers.
➜ ~ dig @192.168.100.53 googleads.g.doubleclick.net
; <<>> DiG 9.10.6 <<>> @192.168.100.53 googleads.g.doubleclick.net
;; QUESTION SECTION:
;googleads.g.doubleclick.net. IN A
;; ADDITIONAL SECTION:
notracking.ioc2rpz. 1 IN SOA ioc2rpz-srv1.ioc2rpz.net. ioc2rpz.ioc2rpz.com. 1619932080 43200 900 2592000 7200
;; Query time: 742 msec
;; SERVER: 192.168.100.53#53(192.168.100.53)
DNSCrypt-Proxy
Now we set up DNSCrypt-Proxy if we want to keep our ISP from inspecting our DNS traffic.
On Ubuntu run:
sudo apt-get install dnscrypt-proxy
Read the documentation to find out where the dnscrypt-proxy config files are located. I have seen them in /etc/dnscrypt/ and /opt/dnscrypt-proxy. You’ll need to adjust the dnscrypt-proxy.toml config file.
I set up a local listener outside of my loopback like 127.0.2.1:53 using the following:
listen_addresses = ['127.0.2.1:53']
Update the servers you want to leverage that support DoH:
server_names = ['google', 'yandex', 'cloudflare']
Update your Recursive Lookup Forwarders by going back to update named.config.options file:
forwarders {
// 8.8.8.8;
// 8.8.4.4;
// We will come back later and change this
127.0.2.1;
}