Open saghul opened 5 years ago
👍
As discussed offline, I'd like to give this a try too!
Adding to the one you shared, we can consider the following feeds/providers:
We can either redirect to httpd or DROP/REJECT with pf.
Stuff for the upcoming bromanceathon!
https://github.com/gbxyz/unbound-block-hosts for converting the hosts file to unbound. https://deadc0de.re/articles/unbound-blocking-ads.htmlsimpler version using awk
We can use those feeds to either drop requests at the network (pf(4)
) or DNS (unbound(8)
).
Dropping at the firewall will give us increased visibility via pflog(4)
but we'd have to resolve all these low-TTL RRs daily or weekly.
A quick data point here:
goya$ time dig -f /tmp/hosts +tcp @8.8.8.8
<snip>
;; Query time: 105 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Sep 11 16:22:38 CEST 2020
;; MSG SIZE rcvd: 91
139m56.98s real 0m12.43s user 0m16.50s system
openhrc$ time dig -f /tmp/hosts.block +tcp @10.0.0.1
<snip>
^C # cause it was only half-way there
196m59.59s real 0m14.16s user 0m18.18s system
Dropping at the resolver is easier to integrate as we only need to re-format the host file from upstream and reload unbound(8)
but we do lose some of the visibility as we cannot directly see the blocked requests ¯_(ツ)_/¯
DNS names come and go much more rapidly than IPs but I guess losing some efficacy in our filtering approach is fine since our game here is convenience (remove ads, etc), not security (remove low-rep, fast-flux or newly observed domains).
At $JOB
, I'd probably go for a solution based on DNS RPZ, webserver and firewall but in our contraption, I think I'd go for the simpler DNS route which should be good enough and have fewer moving parts.
If we see a reason to, we can always change this in the future.
In terms of what to serve, ideally I think we should serve NXDOMAIN to skip the connection attempt.
Otherwise, 127.0.0.1
or 0.0.0.0
will have to do. Their meaning seems to be implementation-specific:
goya$ curl -v 0.0.0.0
* Trying 0.0.0.0:80...
* Immediate connect fail for 0.0.0.0: Invalid argument
* Closing connection 0
curl: (7) Couldn't connect to server
goya$ curl -v 127.0.0.1
* Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to 127.0.0.1 port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused
monet:~> curl -v 0.0.0.0
* Trying 0.0.0.0:80...
* TCP_NODELAY set
* connect to 0.0.0.0 port 80 failed: Connection refused
* Failed to connect to 0.0.0.0 port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 0.0.0.0 port 80: Connection refused
monet:~> curl -v 127.0.0.1
* Trying 127.0.0.1:80...
* TCP_NODELAY set
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to 127.0.0.1 port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused
There's also the thing that something might already be bound to 127.0.0.1:80
or 127.0.0.1:443
.
Let me try to set it up using RPZ instead!
So I followed this.
Added the following to the server
section:
module-config: "respip validator iterator"
rpz:
name: rpz.home.lan
zonefile: "/var/unbound/rpz.home.lan.zone"
And populated the fake zone:
openhrc# head /var/unbound/rpz.home.lan.zone
$ORIGIN rpz.home.lan
n2019cov.000webhostapp.com CNAME .
webmail-who-int.000webhostapp.com CNAME .
010sec.com CNAME .
01mspmd5yalky8.com CNAME .
0byv9mgbn0.com CNAME .
ns6.0pendns.org CNAME .
dns.0pengl.com CNAME .
ios.0pengl.com CNAME .
0x4fc271.tk CNAME .
openhrc# tail /var/unbound/rpz.home.lan.zone
zenzuu.com CNAME .
zeus.developershed.com CNAME .
zeusclicks.com CNAME .
zlp6s.pw CNAME .
zm232.com CNAME .
zmedia.com CNAME .
zpu.samsungelectronics.com CNAME .
zqtk.net CNAME .
zukxd6fkxqn.com CNAME .
zy16eoat1w.com CNAME .
These are the posible actions:
Action | RR type and RDATA |
---|---|
NXDOMAIN | CNAME . |
NODATA | CNAME *. |
PASSTHRU | CNAME rpz-passthru. |
DROP | CNAME rpz-drop. |
Applied the config by restarting, cause of the modules:
openhrc# rcctl restart unbound
unbound(ok)
unbound(ok)
We get, from a cold cache:
monet:~> dig @10.0.0.1 a n2019cov.000webhostapp.com
; <<>> DiG 9.11.2 <<>> @10.0.0.1 a n2019cov.000webhostapp.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 25874
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;n2019cov.000webhostapp.com. IN A
;; Query time: 1 msec
;; SERVER: 10.0.0.1#53(10.0.0.1)
;; WHEN: Fri Sep 11 19:51:43 CEST 2020
;; MSG SIZE rcvd: 55
monet:~> dig @8.8.8.8 a n2019cov.000webhostapp.com
; <<>> DiG 9.11.2 <<>> @8.8.8.8 a n2019cov.000webhostapp.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 38759
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;n2019cov.000webhostapp.com. IN A
;; ANSWER SECTION:
n2019cov.000webhostapp.com. 3599 IN CNAME us-east-1.route-1.000webhost.awex.io.
us-east-1.route-1.000webhost.awex.io. 59 IN A 145.14.145.148
;; Query time: 25 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Sep 11 19:51:46 CEST 2020
;; MSG SIZE rcvd: 121
TL;DR it seems to just work from base
We can use something naive like this to be run daily (until it breaks):
#!/bin/ksh
set -e
URL=https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
RPZ_ZONE=rpz.home.lan
TMP_ZONE=`mktemp -t $RPZ_ZONE-zone.XXXXXXXXXX`
TMP_HOSTS=`mktemp -t $RPZ_ZONE-hosts.XXXXXXXXXX`
DEST_ZONE=/var/unbound/${RPZ_ZONE}.zone
ftp -MV -o $TMP_HOSTS $URL
awk -v RPZ_ZONE="$RPZ_ZONE." \
'BEGIN {printf "$ORIGIN %s\n",RPZ_ZONE;
printf "%s IN SOA ns1.%s admin.%s 0000000001 86400 7200 604800 300\n",RPZ_ZONE,RPZ_ZONE,RPZ_ZONE}
/^0\.0\.0\.0.+[a-z]$/ {printf "%s CNAME .\n",$NF}' $TMP_HOSTS > $TMP_ZONE
nsd-checkzone $RPZ_ZONE $TMP_ZONE >/dev/null
mv -f $TMP_ZONE $DEST_ZONE
chown _unbound:_unbound $DEST_ZONE
unbound-control -q reload
rm -f $TMP_HOSTS $TMP_ZONE
It will not output anything unless there are errors, which will be wrapped in an email and sent to the local root
mailbox.
Tested it a bit already.
https://github.com/StevenBlack/hosts