Just posting this for future reference, as it is extremely crude right now: Code: #!/bin/bash - LIST=/root/allowed-domains.txt read -d '' domains < $LIST ufw reset ufw default reject outgoing for entry in $domains; do case $entry in *[0-9]) ufw allow out to $entry ;; *) dig $entry ANY | awk '/\tA\t/ {print $5}' \ | xargs -iADDR ufw allow out to ADDR ;; esac done ufw enable You put the domains and IP ranges you want in the specified list file, and run the script as root. Be warned that this will wipe your current UFW ruleset. It forces UFW to reject all outgoing connections that don't go to the specified domains, IPs, or IP ranges. It detects what is an IP or IP range by the simple heuristic that domain names don't end in a number. Caveats: - Subdomain handling is dubious. If you really need a subdomain to work, you should probably put it in specifically. - The reject policy (as opposed to deny) is necessary, otherwise browsers etc. will hang as they keep spamming requests. - Don't allow passwordless sudo access to this script, as that will create a local vulnerability. - It's not as fine-grained as I would like. - It's not as fast as I would like, either. - As indicated above, it WILL wipe your current UFW ruleset! - Make sure you include your DHCP and DNS servers in the allowed list, or nothing will work! - Due to the behavior of read and me being lazy, it is dependent on bash and not tested with anything else. Possible uses: - Ad and web tracker blocking - Drive-by malware prevention - Avoiding time wasters - Putting some limits on your kids' internet excursions Future possibilities: -> Get rid of the bash dependency. (Make it an awk script perhaps? Perl seems gratuitous for this.) -> Get rid of the UFW/Python/etc. dependencies. (This is not hard, but like I said, I'm lazy.) -> Have some kind of record format, for indicating what ports/protocols should be allowed for each destination? (Might not be worth it.) Disclaimers: - This is a silly little public domain script, it has no warranty, don't rely on it for anything too serious, etc. etc. I'll work on the improved version later tonight, and probably commit it to Gitorious at some point...
Okay, here is an improved version. Uses iptables only, relies on POSIX shell rather than bash... Code: #!/bin/sh - LIST=/root/allowed-domains.txt iptables-restore <<'END' *filter :INPUT DROP :FORWARD DROP :OUTPUT ACCEPT -A INPUT -i lo -j ACCEPT -A OUTPUT -o lo -j ACCEPT -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT COMMIT END while read entry; do case $entry in *[0-9]) iptables -A OUTPUT -d $entry -j ACCEPT ;; *) dig $entry ANY | awk '/\tA\t/ {print $5}' \ | xargs -iADDR iptables -A OUTPUT -d ADDR -j ACCEPT ;; esac done < $LIST iptables -A OUTPUT -j REJECT Note that this doesn't cover IPv6 at all!
Current revision is rather simpler, and uses UFW again: Code: #!/bin/sh - for entry in $@; do dig www.$entry | awk '/\tA\t/ {print $5}' | xargs -iADDR ufw allow out to ADDR done auto-appending the 'www.' seems to improve the resolving of correct IPs. This has shortened my whitelist considerably.
Okay, we have problems here - UFW seems to slow network traffic way down when the whitelist gets too long. Maybe there's a better way to do this? At the DNS level maybe, or with a proxy... Hmm.
I realized it was better to just have one file with a static list of addresses. So... Code: #!/bin/sh - DNS_SERVER=192.168.1.1 # Example list OKAY_DOMAINS=$(cat <<END google.com mail.google.com talk.google.com wilderssecurity.com osnews.com END ) iptables -F iptables -X iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT iptables -A OUTPUT -d $DNS_SERVER -j ACCEPT for dest in $OKAY_DOMAINS; do host $dest | awk '/has address/{print $4}' \ | xargs -iX iptables -A OUTPUT -d X -j ACCEPT done iptables -A OUTPUT -j REJECT Fast, simple, gets the job done.