fediverse-scripts/firewall_pleroma_rejects.sh

182 lines
6.2 KiB
Bash
Executable File

#!/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}"