mirror of
https://github.com/docker-mailserver/docker-mailserver
synced 2026-11-03 15:00:08 +01:00
* feat(fail2ban): add `setup fail2ban ignore` and `consider` Adding an address to the Fail2Ban ignore list at runtime needed fail2ban-client and a loop over every jail. This carries over #4504 and addresses its review. Co-authored-by: Andrew Cornford <29998536+groupmsl@users.noreply.github.com> * fix(fail2ban): reject invalid addresses and list ignores under `status` fail2ban-client treats anything that is not an IP address as a hostname, so a typo like 192.0.2.300 was accepted silently. `ignore` and `consider` now check the argument and pass it on in the spelling F2B uses, which `delignoreip` needs to find a single IPv6 address again. Plain `setup fail2ban` shows bans only again, as before this PR. --------- Co-authored-by: Andrew Cornford <29998536+groupmsl@users.noreply.github.com>
144 lines
4.0 KiB
Bash
Executable File
144 lines
4.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# shellcheck source=../scripts/helpers/index.sh
|
|
source /usr/local/bin/helpers/index.sh
|
|
|
|
function __usage() {
|
|
echo "Usage: ./setup.sh fail2ban [<ban|unban> <IP>]"
|
|
echo " ./setup.sh fail2ban [<ignore|consider> <IP OR CIDR RANGE>]"
|
|
echo " ./setup.sh fail2ban log"
|
|
echo " ./setup.sh fail2ban status"
|
|
}
|
|
|
|
# F2B takes anything that does not parse as an address for a hostname, and finds
|
|
# a single address for removal only in the spelling it lists it in. The regex keeps
|
|
# out IPv6 zone IDs, which ipaddress accepts but F2B does not.
|
|
function __normalize_address() {
|
|
[[ ${1} =~ ^[[:xdigit:]:.]+(/[[:digit:]]+)?$ ]] || return 1
|
|
python3 -c '
|
|
import ipaddress, sys
|
|
net = ipaddress.ip_network(sys.argv[1], strict=False)
|
|
print(net.network_address if net.prefixlen == net.max_prefixlen else net)
|
|
' "${1}" 2>/dev/null
|
|
}
|
|
|
|
fail2ban-client ping &>/dev/null || _exit_with_error "Fail2ban not running"
|
|
|
|
unset JAILS
|
|
declare -a JAILS
|
|
|
|
for LIST in $(fail2ban-client status | grep "Jail list" | cut -f2- | sed 's/,/ /g'); do
|
|
JAILS+=("${LIST}")
|
|
done
|
|
|
|
if [[ -z ${1} ]]; then
|
|
IPS_BANNED=0
|
|
|
|
for JAIL in "${JAILS[@]}"; do
|
|
BANNED_IPS=$(fail2ban-client status "${JAIL}" | grep -oP '(?<=Banned IP list:\s).+')
|
|
|
|
if [[ -n ${BANNED_IPS} ]]; then
|
|
echo "Banned in ${JAIL}: ${BANNED_IPS}"
|
|
IPS_BANNED=1
|
|
fi
|
|
done
|
|
|
|
[[ ${IPS_BANNED} -eq 0 ]] && _log 'info' "No IPs have been banned"
|
|
else
|
|
|
|
case "${1}" in
|
|
|
|
( 'help' ) __usage ; exit ;;
|
|
|
|
( 'ban' )
|
|
shift
|
|
if [[ -n ${1} ]]; then
|
|
RESULT=$(fail2ban-client set custom banip "${@}")
|
|
if [[ ${RESULT} -gt 0 ]]; then
|
|
echo "Banned custom IP: ${RESULT}"
|
|
else
|
|
_log 'error' "Banning '${*}' failed. Already banned?"
|
|
fi
|
|
|
|
else
|
|
_log 'warn' "You need to specify an IP address: Run './setup.sh fail2ban ban <IP>'"
|
|
exit 0
|
|
fi
|
|
;;
|
|
|
|
( 'unban' )
|
|
shift
|
|
if [[ -n ${1} ]]; then
|
|
|
|
for JAIL in "${JAILS[@]}"; do
|
|
RESULT=$(fail2ban-client set "${JAIL}" unbanip "${@}" 2>&1)
|
|
|
|
[[ ${RESULT} != *"is not banned"* ]] && [[ ${RESULT} != *"NOK"* ]] && echo "Unbanned IP from ${JAIL}: ${RESULT}"
|
|
done
|
|
|
|
else
|
|
_log 'warn' "You need to specify an IP address: Run './setup.sh fail2ban' to get a list of banned IP addresses"
|
|
exit 0
|
|
fi
|
|
;;
|
|
|
|
( 'ignore' )
|
|
shift 1
|
|
if [[ -n ${1} ]]; then
|
|
ADDRESS=$(__normalize_address "${1}") || _exit_with_error "'${1}' is not an IP address or CIDR range"
|
|
|
|
for JAIL in "${JAILS[@]}"; do
|
|
if fail2ban-client set "${JAIL}" addignoreip "${ADDRESS}" >/dev/null; then
|
|
echo "Ignoring IP in ${JAIL}: ${ADDRESS}"
|
|
fi
|
|
done
|
|
else
|
|
_log 'warn' "You need to specify an IP address or CIDR range: Run './setup.sh fail2ban ignore <IP OR CIDR RANGE>'"
|
|
exit 0
|
|
fi
|
|
;;
|
|
|
|
( 'consider' )
|
|
shift 1
|
|
if [[ -n ${1} ]]; then
|
|
ADDRESS=$(__normalize_address "${1}") || _exit_with_error "'${1}' is not an IP address or CIDR range"
|
|
CONSIDERED=0
|
|
|
|
for JAIL in "${JAILS[@]}"; do
|
|
# Jails that do not ignore the address reject the removal
|
|
if fail2ban-client set "${JAIL}" delignoreip "${ADDRESS}" &>/dev/null; then
|
|
echo "No longer ignoring IP in ${JAIL}: ${ADDRESS}"
|
|
CONSIDERED=1
|
|
fi
|
|
done
|
|
|
|
[[ ${CONSIDERED} -eq 0 ]] && _log 'warn' "'${ADDRESS}' is not ignored by any jail"
|
|
else
|
|
_log 'warn' "You need to specify an IP address or CIDR range: Run './setup.sh fail2ban status' to get a list of ignored IP addresses"
|
|
exit 0
|
|
fi
|
|
;;
|
|
|
|
( 'log' )
|
|
cat /var/log/mail/fail2ban.log
|
|
;;
|
|
|
|
( 'status' )
|
|
for JAIL in "${JAILS[@]}"; do
|
|
printf '%s\n' "$(fail2ban-client status "${JAIL}" 2>&1)"
|
|
printf 'Ignored in %s: %s\n\n' "${JAIL}" \
|
|
"$(fail2ban-client get "${JAIL}" ignoreip | grep -oP '(?<=[|`]- ).+' | paste -sd ' ')"
|
|
done
|
|
;;
|
|
|
|
( * )
|
|
__usage
|
|
_exit_with_error "Unknown command '${1}'"
|
|
;;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
exit 0
|