Skip to content

Instantly share code, notes, and snippets.

@metametapod
Last active April 19, 2024 05:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save metametapod/4c4a7a9c888343fc6e722f8ed77aa763 to your computer and use it in GitHub Desktop.
Save metametapod/4c4a7a9c888343fc6e722f8ed77aa763 to your computer and use it in GitHub Desktop.
Script to automatically update hosts file from a filter list

Minimal script to quickly update hosts file from a filter list.

Note that this does not replace the entirety of the hosts file, so previous customizations will remain intact.

Installation

Download the update_hosts script below or run:

$ curl --proto "=https" --tlsv1.2 https://gist.githubusercontent.com/metametapod/4c4a7a9c888343fc6e722f8ed77aa763/raw/update_hosts -o update_hosts

Run as root:

# chown root:"$(id -gn root)" update_hosts
# chmod 544 update_hosts

Via anacron

Run as root:

# mv update_hosts /etc/cron.daily/

This will run the script once a day, or when waking your computer if powered off.

Via cron

Run as root:

# mv update_hosts /usr/local/bin/
# crontab -e

Then add the entry:

0 0 * * * /usr/local/bin/update_hosts

This will run the script once a day at midnight.

Via launchd (macOS)

Run as root:

# mv update_hosts /Library/PrivilegedHelperTools/
# curl --proto "=https" --tlsv1.2 https://gist.githubusercontent.com/metametapod/4c4a7a9c888343fc6e722f8ed77aa763/raw/update_hosts.plist -o /Library/LaunchDaemons/update_hosts.plist
# chmod 644 /Library/LaunchDaemons/update_hosts.plist
# chown root:wheel /Library/LaunchDaemons/update_hosts.plist
# launchctl list | awk '{print $3}' | grep -Fxq update_hosts && launchctl bootout system /Library/LaunchDaemons/update_hosts.plist
# launchctl bootstrap system /Library/LaunchDaemons/update_hosts.plist

This will run the script once a day at midnight, when launchctl first loads it, and when waking your computer if powered off at the scheduled time.

Changelog

2024-04-19

  • Updated link to generated AdGuard hosts filter.

2024-04-18

  • Replaced NoCoin list (unmaintained) with ZeroDot1 CoinBlocker hosts list in default filters.

2024-02-14

  • Added URLhaus Malicious Hosts Blocklist to default filters.
  • Added last updated header to hosts file comment.

2024-01-27

  • Improved error handling when connection is offline.

2024-01-26

  • Fixed handling of symlinked /etc/hosts file on certain platforms (e.g. macOS).

2024-01-25

  • Prefer temporary file over writing in-place, which is more reliable for larger files and background tasks.

2024-01-24

  • Added instructions for anacron and move suggested launchd install location to /Library/PrivilegedHelperTools/.

2024-01-22

  • Removed -Z/--parallel flag, which was causing unpredictably malformed output.

2024-01-14

  • Initial release.
#!/bin/sh
#
# Script to update hosts file from a filter list, to be used in
# cron/launchctl/etc.
#
# https://gist.github.com/metametapod/4c4a7a9c888343fc6e722f8ed77aa763
# License: MIT
set -o errexit -o nounset
# See https://en.wikipedia.org/wiki/Hosts_(file)#Location_in_the_file_system
HOSTS_FILE="/etc/hosts"
# List of hostname regexes to allow, separated by newlines, in POSIX grep
# BRE syntax.
ALLOWLIST='
\(^\|\.\)apple\.com$
\(^\|\.\)duckduckgo\.com$
\(^\|\.\)icloud\.com$
\(^\|\.\)mozilla\.org$
\(^\|\.\)mzstatic\.com$
'
# List of filter list URLs, separated by newlines. Must be hosts file syntax.
FILTERS="
https://raw.githubusercontent.com/metametapod/filters/main/generated/adguard/hosts/filter_1.txt
https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt
https://zerodot1.gitlab.io/CoinBlockerLists/hosts_browser
"
generate_blocklist() {
# %output{filename} is only supported in curl 8.3 and above. Fallback to
# stderr for logging in earlier versions.
{
echo "8.3.0"
printf "%s\n" "$(curl -V | head -n1 | awk '{print $2}')"
} | sort --version-sort --check=silent &&
output_var="%output{>>$1}" || output_var="%{stderr}"
# NB: curl not included in pipe below to exit early if connection is down.
fetched="$(printf "%s" "$FILTERS" |
xargs curl --proto "=https" --tlsv1.2 -sSf --fail-early --retry 10 \
--retry-all-errors --write-out \
"${output_var}Filter download time: %{time_total}s for %{url}\n")"
printf "%s\n" "$fetched" |
grep -v -f /dev/fd/3 3<<-EOF | sort -u
^\s*$
^[#!]
$(printf "%s" "$ALLOWLIST" | tail -n +2 | sed 's/\([^\]\)^/\1\\s/')
EOF
}
main() (
tmp_dir="$(mktemp -d)"
trap 'nohup rm -rf "$tmp_dir" >/dev/null 2>&1 &' EXIT HUP INT QUIT TERM
curl_log="$tmp_dir/curl.log"
current_datetime="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
divider="$(printf "%80s\n" " " | tr " " "#")"
end_separator="$(printf "%s\n#[/filters]\n%s" "$divider" "$divider")"
last_updated_header="$(printf "# Last updated %s\n" "$current_datetime")"
start_separator="$(printf "%s\n#[filters]\n%s" "$divider" "$divider")"
updated_hosts_file="$tmp_dir/hosts"
blocklist="$(generate_blocklist "$curl_log")"
test -f "$curl_log" && cat <"$curl_log"
cat <<-EOF >"$updated_hosts_file"
$(START="$start_separator" END="$end_separator" \
perl -0pe 's/\Q$ENV{"START"}\E.*?\Q$ENV{"END"}\E//gs' "$HOSTS_FILE")
$start_separator
$last_updated_header
$blocklist
$end_separator
EOF
chmod 644 "$updated_hosts_file"
mv "$updated_hosts_file" "$(realpath "$HOSTS_FILE")"
)
main
<?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>Label</key>
<string>update_hosts</string>
<key>Program</key>
<string>/Library/PrivilegedHelperTools/update_hosts</string>
<key>StandardOutPath</key>
<string>/Library/Logs/update_hosts.log</string>
<key>StandardErrorPath</key>
<string>/Library/Logs/update_hosts.error.log</string>
<key>RunAtLoad</key>
<true/>
<key>ProcessType</key>
<string>Background</string>
<key>LowPriorityBackgroundIO</key>
<true/>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>00</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment