#!/bin/zsh # Generate nftables rules from the list of rejected instances. The list is # fetched from nodeinfo 2.1. masto.host- and CloudFlare-IPs are filtered out. # BE CAREFUL: Some instances may run under a shared IP, you may block more than # you want. Watch out for timeouts in the logs. No warranties and so on. # Version: 2019-10-25_1 # You can modify the ignore-list in # "${XDG_CONFIG_HOME}/firewall_pleroma_rejects/ignorelist.user", # "${HOME}/.config/firewall_pleroma_rejects/ignorelist.user" or # "${HOME}/.firewall_pleroma_rejects/ignorelist.user". The file will get sourced # in gen_rule() right after ${ignorelist} is defined. # Before you run this script for the first time, run: # nft add table inet fediverse function get_domains() # Outputs domains, separated by newline. { local instance="${1}" local nodeinfo=$(curl -s "https://${instance}/nodeinfo/2.1.json") local domains_json=$(jq -c '.metadata.federation.mrf_simple.reject' \ <<<"${nodeinfo}") sed -e 's/\[//' -e 's/\]//' -e 's/"//g' -e 's/,/\n/g' <<<"${domains_json}" } function get_ips() # Outputs IPs, separated by newline. { local domain="${1}" # Filter out wildcard domains. if [[ "${domain}" =~ '\*' ]]; then return fi # Resolve aliases. local realdomain="${domain}" while [[ -n "${realdomain}" ]]; do realdomain=$(dig +short "${domain}" CNAME) if [[ -n "${realdomain}" ]]; then domain="${realdomain:0:-1}" fi done for ip_version in AAAA A; do for ip in $(dig +short "${domain}" "${ip_version}"); do [[ "${?}" -eq 0 ]] && echo "${ip}" done done } function gen_rule() # Generates rule for IP if it is not in ignorelist. { local ip="${1}" local domain="${2}" # Regular expressions of IPs to ignore. local -a ignorelist=( "^2001:41d0:302:1100:" # masto.host shared IPs "^217\.182\.80\.236$" "^178\.33\.220\.14[0-3]$" "^176\.31\.213\.22[89]$" "^176\.31\.213\.23[01]$" "^54\.38\.247\.97$" "^54\.37\.254\.138$" "^54\.37\.254\.14[12]$" "^54\.37\.254\.2[56]$" "^54\.37\.254\.3[19]$" "^54\.37\.254\.46$" "^145\.239\.141\.74$" "^51\.38\.62\.175$" "^2a06:98c[0-7]:" # CloudFlare 2a06:98c0::/29 "^2400:cb00:" # CloudFlare 2400:cb00::/32 "^2606:4700:" # CloudFlare 2606:4700::/32 "^2803:f800:" # CloudFlare 2803:f800::/32 "^2405:b500:" # CloudFlare 2405:b500::/32 "^2405:8100:" # CloudFlare 2405:8100::/32 "^2c0f:f248:" # CloudFlare 2c0f:f248::/32 "^104\.1[6-9]\." # CloudFlare 104.16.0.0/12 "^104\.2[0-9]\." "^104\.3[01]\." "^172\.6[4-9]\." # CloudFlare 172.64.0.0/13 "^172\.7[01]\." "^162\.15[89]\." # CloudFlare 162.158.0.0/15 "^198\.41\.12[89]\." # CloudFlare 198.41.128.0/17 "^198\.41\.1[3-5][0-9]\." "^141\.101\.6[4-9]\." # CloudFlare 141.101.64.0/18 "^141\.101\.[7-9][0-9]\." "^141\.101\.1[01][0-9]\." "^141\.101\.12[0-7]\." "^108\.162\.19[2-9]\." # CloudFlare 108.162.192.0/18 "^108\.162\.2[0-9]+\." "^173\.245\.4[89]\." # CloudFlare 173.245.48.0/20 "^173\.245\.5[0-9]\." "^173\.245\.6[0-3]\." "^190\.93\.2[45][0-9]\." # CloudFlare 190.93.240.0/20 "^188\.114\.9[6-9]\." # CloudFlare 188.114.96.0/20 "^188\.114\.10[0-9]\." "^188\.114\.111\." "^103\.21\.24[4-7]\." # CloudFlare 103.21.244.0/22 "^103\.22\.20[0-3]\." # CloudFlare 103.22.200.0/22 "^197\.234\.24[0-3]" # CloudFlare 197.234.240.0/22 "^131\.0\.7[2-5]\." # CloudFlare 131.0.72.0/22 "^2602:ffdb:100:1:fc89:b2ff:fe77:1664$" # Shared IPs: pleroma.site, "^172\.93\.5\.40$" # juche.town "^91\.121\.101\.53$" # spacebear.ee shared IPs "^198\.245\.60\.57$" "^163\.172\.141\.145$" ) # Read and apply user modifications to ignorelist. local extra_ignores="$(get_config_dir)/ignorelist.user" if [[ -e "${extra_ignores}" ]]; then source ${extra_ignores} fi for ignore in ${ignorelist}; do if [[ "${ip}" =~ "${ignore}" ]]; then return fi done if grep -q ":" <<<${ip}; then echo " ip6 saddr ${ip} drop comment \"${domain}\"" else echo " ip saddr ${ip} drop comment \"${domain}\"" fi } function get_config_dir() { local name="firewall_pleroma_rejects" if [[ -d "${XDG_CONFIG_HOME}/${name}" ]]; then echo "${XDG_CONFIG_HOME}/${name}" elif [[ -d "${HOME}/.config/${name}" ]]; then echo "${HOME}/.config/${name}" else echo "${HOME}/.${name}" fi } function main() { local instance="${1}" if [[ -z "${instance}" ]]; then echo "Usage: ${ZSH_ARGZERO} instance" >&2 echo " ${ZSH_ARGZERO} instance | nft -f -" >&2 return 1 fi if ! command -v dig > /dev/null; then echo "Error: dig not found. Please install bind-tools." >&2 return 2 fi if ! command -v curl > /dev/null; then echo "Error: curl not found. Please install it." >&2 return 2 fi if ! command -v jq > /dev/null; then echo "Error: jq not found. Please install it." >&2 return 2 fi echo "#!/sbin/nft -f" echo "flush table inet fediverse" echo "table inet fediverse {" echo " chain input {" echo " type filter hook input priority 0; policy accept;" for domain in $(get_domains "${instance}"); do for ip in $(get_ips "${domain}"); do [[ -n "${ip}" ]] && gen_rule "${ip}" "${domain}" done done echo " }" echo "}" } main "${1}"