- Run generate-rpz.js
- Set up a cron/launchd/systemd job/task to run generate-rpz.js every week to month
- Add to named.conf:
response-policy { zone "rpz"; }; zone "rpz" IN { type master; file "/path/to/here/current-zone"; };
Last active
May 20, 2019 16:45
-
-
Save samuelthomas2774/2759b93ffe661e53ae1c4ca7790857d3 to your computer and use it in GitHub Desktop.
Generate a BIND Response Policy Zone to block ad hosts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | |
}); | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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