Skip to content

Instantly share code, notes, and snippets.

@samuelthomas2774
Last active May 20, 2019 16:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuelthomas2774/2759b93ffe661e53ae1c4ca7790857d3 to your computer and use it in GitHub Desktop.
Save samuelthomas2774/2759b93ffe661e53ae1c4ca7790857d3 to your computer and use it in GitHub Desktop.
Generate a BIND Response Policy Zone to block ad hosts
  1. Run generate-rpz.js
  2. Set up a cron/launchd/systemd job/task to run generate-rpz.js every week to month
  3. Add to named.conf:
    response-policy {
        zone "rpz";
    };
    zone "rpz" IN {
        type master;
        file "/path/to/here/current-zone";
    };
    
$TTL 600
@ IN SOA server.fancy.org.uk. root.server.fancy.org.uk. (
2015112501 ; serial
1h ; refresh
30m ; retry
1w ; expiry
30m) ; minimum
IN NS server.fancy.org.uk.
const fs = require('fs');
const https = require('https');
const child_process = require('child_process');
const path = require('path');
// This is the same as the default lists for Pi-hole except http://sysctl.org/cameleon/hosts as it doesn't support HTTPS
// https://github.com/pi-hole/pi-hole/wiki/Customising-Sources-for-Ad-Lists
const ad_list_urls = [
'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts',
'https://mirror1.malwaredomains.com/files/justdomains',
'https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist',
'https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt',
'https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt',
'https://hosts-file.net/ad_servers.txt',
];
const redirect_ipv4_address = '0.0.0.0';
const redirect_ipv6_address = '::';
const rndc_command = '/usr/local/sbin/rndc';
const rndc_args = ['-s', '::1', '-p', '54'];
const zone_name = 'rpz';
const zone_view = 'com.apple.ServerAdmin.DNS.public';
const request = url => new Promise((resolve, reject) => {
https.get(url, res => {
let data = '';
res.on('data', d => data += d);
res.on('end', () => resolve([data, res]));
}).on('error', err => reject(err));
});
const exec = (command, options) => new Promise((resolve, reject) => {
child_process.exec(command, options, (err, stdout, stderr) => {
err ? reject({err, stdout, stderr}) : resolve({err, stdout, stderr});
});
});
const spawn = (command, args, options) => new Promise((resolve, reject) => {
child_process.spawn(command, args, options, (err, stdout, stderr) => {
err ? reject({err, stdout, stderr}) : resolve({err, stdout, stderr});
});
});
const readFile = (path, options) => new Promise((resolve, reject) => {
fs.readFile(path, options, (err, data) => err ? reject(err) : resolve(data));
});
const writeFile = (path, data, options) => new Promise((resolve, reject) => {
fs.writeFile(path, data, options, (err, data) => err ? reject(err) : resolve(data));
});
(async () => {
let hosts = [];
for (let ad_list_url of ad_list_urls) {
const [data, res] = await request(ad_list_url);
const lines = data.replace(/#.*$/g).trim().split('\n');
const regex = /^((0\.0\.0\.0|127\.0\.0\.1)\s+)?((?!0\.0\.0\.0|127\.0\.0\.1|255\.255\.255\.255|local(host)?(\..*)?|#)[^\s_:%]*)\s*(#.*)?$/;
let match;
const _hosts = lines.map(host => host && (match = regex.exec(host)) && match[3]).filter(h => h);
console.log('Downloaded', _hosts.length, 'hosts from', ad_list_url);
hosts = hosts.concat(_hosts);
}
console.log(hosts.length + ' total hosts (including duplicates)');
const hosts_with_duplicates = hosts;
hosts = Array.from(new Set(hosts));
console.log(hosts.length + ' total hosts');
const rpz = await readFile(path.join(__dirname, 'base-zone'), 'utf8')
+ hosts.map(host => `${host.replace(/[^a-zA-Z0-9-.]/g, '\\$&')} A ${redirect_ipv4_address}\n${host.replace(/[^a-zA-Z0-9-.]/g, '\\$&')} AAAA ${redirect_ipv6_address}`).join('\n') + '\n';
writeFile(path.join(__dirname, 'current-zone'), rpz);
if (rndc_command) {
console.log('Reloading the response policy zone');
await spawn(rndc_command, rndc_args.concat(['reload', zone_name, 'IN'], zone_view ? [zone_view] : []), {
stdio: 'inherit',
});
}
})();
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>update-rpz</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/path/to/here/generate-rpz.js</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/path/to/here/error.log</string>
<key>StandardOutPath</key>
<string>/path/to/here/output.log</string>
<key>StartInterval</key>
<integer>604800</integer>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment