ufwlist.sh: quickly build an IP whitelist for UFW

Discussion in 'all things UNIX' started by Gullible Jones, Dec 8, 2014.

  1. Gullible Jones

    Gullible Jones Registered Member

    Joined:
    May 16, 2013
    Posts:
    1,459
    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...
     
  2. Gullible Jones

    Gullible Jones Registered Member

    Joined:
    May 16, 2013
    Posts:
    1,459
    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!
     
  3. Gullible Jones

    Gullible Jones Registered Member

    Joined:
    May 16, 2013
    Posts:
    1,459
    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.
     
  4. Gullible Jones

    Gullible Jones Registered Member

    Joined:
    May 16, 2013
    Posts:
    1,459
    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.
     
  5. The Red Moon

    The Red Moon Registered Member

    Joined:
    May 17, 2012
    Posts:
    3,871
    Just out of interest have you attempted this with say firewalld for example.?.;)
     
  6. Gullible Jones

    Gullible Jones Registered Member

    Joined:
    May 16, 2013
    Posts:
    1,459
    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.
     
Loading...