Files
oopen-server/roles/ipt-server/files/etc/ipt-firewall/include_functions.conf
T
chrisandClaude Opus 5.5 402a6c9e89 Role ipt-server: sync with ipt-server repo (93d9af8); adapt extract-fw-host-vars.py.
- Update firewall scripts and shared conf files from firewall/ipt-server
  (HTTP connection limits, IPv6 TCP MSS clamping, ban_ipv6_mask,
  mattermost TCP fallback, rename 'stansard_*' -> 'standard_*').
- New role variables: fw_set_max_new_http_conn_per_ip_per_second,
  fw_max_http_new_conn_per_ip_per_second, fw_set_max_concurrent_http_conn_per_ip,
  fw_max_concurrent_http_conn_per_ip, fw_set_max_new_http_conn_per_second,
  fw_max_new_http_conn_per_second, fw_throttle_tcp6_mss_for_https,
  fw_tcp6_mss_for_https_val, fw_mm_tcp_ports_in, fw_ban_ipv6_mask.
- Split fw_blocked_ips into fw_blocked_ips_v4 / fw_blocked_ips_v6 and
  rename it in host_vars accordingly.
- Restart firewall when scripts or shared conf files change.
- extract-fw-host-vars.py: map new variables, normalize '${var}' and renamed
  bash variables, fall back to IPv6 for shared variables and warn if IPv4
  and IPv6 values differ.
- README.md / MIGRATION.md: document host_vars naming
  (<short-hostname>-ipt-server.yml) and new variables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:01:28 +01:00

317 lines
8.4 KiB
Bash

#!/usr/bin/env bash
# - Set firewall command (either iptables or ip6tables)
#
if [[ -x "${ip6t}" ]] ; then
fw_command="${ip6t}"
elif [[ -x "${ipt}" ]] ; then
fw_command="${ipt}"
fi
# -------------
# --- Some functions
# -------------
echononl(){
echo X\\c > /tmp/shprompt$$
if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then
echo -e -n "$*\\c" 1>&2
else
echo -e -n "$*" 1>&2
fi
rm /tmp/shprompt$$
}
echo_done() {
echo -e "\033[75G[ \033[32mdone\033[m ]"
}
echo_ok() {
echo -e "\033[75G[ \033[32mok\033[m ]"
}
echo_warning() {
echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]"
}
echo_failed(){
echo -e "\033[75G[ \033[1;31mfailed\033[m ]"
}
echo_skipped() {
echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]"
}
fatal (){
echo ""
echo -e "fatal Error: $*"
echo ""
echo -e "\t\033[31m\033[1mScript will be interrupted..\033[m\033[m"
echo ""
exit 1
}
error(){
echo ""
echo -e "\t[ \033[31m\033[1mFehler\033[m ]: $*"
echo ""
}
warn (){
echo ""
echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*"
echo ""
}
info (){
echo ""
echo -e "\t[ \033[32m\033[1mInfo\033[m ]: $*"
echo ""
}
## - Check if a given array (parameter 2) contains a given string (parameter 1)
## -
containsElement () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
## - Expand a given IPv6 address (parameter 1) to 32 hex digits without colons.
## - Returns 1 if the address cannot be parsed.
## -
ipv6_expand() {
local _addr="${1,,}"
local _head _tail _group _n _out=""
local -a _h=() _t=()
if [[ "$_addr" == *::* ]] ; then
_head="${_addr%%::*}"
_tail="${_addr#*::}"
[[ "$_tail" == *::* ]] && return 1
[[ -n "$_head" ]] && IFS=':' read -ra _h <<< "$_head"
[[ -n "$_tail" ]] && IFS=':' read -ra _t <<< "$_tail"
_n=$(( 8 - ${#_h[@]} - ${#_t[@]} ))
[[ $_n -lt 1 ]] && return 1
for (( ; _n > 0; _n-- )) ; do
_h+=("0")
done
_h+=("${_t[@]}")
else
IFS=':' read -ra _h <<< "$_addr"
fi
[[ ${#_h[@]} -eq 8 ]] || return 1
for _group in "${_h[@]}" ; do
[[ "$_group" =~ ^[0-9a-f]{1,4}$ ]] || return 1
_out+="$(printf '%04x' "0x$_group")"
done
echo "$_out"
}
## - Check if a given IPv6 address (parameter 1) lies within the network
## - given by address (parameter 2) and prefix length (parameter 3).
## -
ipv6_in_net() {
local _a _n _full _rest
_a="$(ipv6_expand "$1")" || return 1
_n="$(ipv6_expand "$2")" || return 1
_full=$(( $3 / 4 ))
_rest=$(( $3 % 4 ))
[[ "${_a:0:_full}" == "${_n:0:_full}" ]] || return 1
[[ $_rest -eq 0 ]] && return 0
(( (0x${_a:_full:1} >> (4 - _rest)) == (0x${_n:_full:1} >> (4 - _rest)) ))
}
is_number() {
return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);
# - also possible
# -
#[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1
#return $([[ ! -z "${1##*[!0-9]*}" ]])
}
trim() {
local var="$*"
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
is_container() {
command -v systemd-detect-virt >/dev/null 2>&1 && systemd-detect-virt --container >/dev/null 2>&1
}
# -------------
# - IPv6 handling
# -------------
ENABLE_IPV6="auto" # auto | yes | no
IPV6_ACTIVE=0
ipv6_sysctl_enabled() {
sysctl -n net.ipv6.conf.all.disable_ipv6 2>/dev/null | grep -qx 0
}
has_ipv6_addr() {
ip -6 addr show scope global 2>/dev/null | grep -q "inet6"
}
detect_ipv6() {
case "$ENABLE_IPV6" in
yes) return 0 ;;
no) return 1 ;;
auto) ipv6_sysctl_enabled ;;
*) return 1 ;;
esac
}
# -------------
# - Network Device Stuff
# -------------
# get virtual ethernet interfaces and the master of the given bridge
#
get_vth_ports() {
local br="$1"
# lists virtual interfaces (veth*)) and the master interface of the given bridge
ip -o link show master "$br" 2>/dev/null | awk -F': ' '{print $2}'
}
# -------------
# - Fail2ban
# -------------
FAIL2BAN_CONFIG_FILE="/etc/fail2ban/jail.local"
FAIL2BAN_WAS_RUNNING=false
fail2ban_client="$(command -v fail2ban-client 2>/dev/null)"
has_fail2ban() {
command -v fail2ban-client >/dev/null 2>&1
}
fail2ban_running() {
systemctl is-active --quiet fail2ban >/dev/null 2>&1
}
# -------------
# - Debian 12/13 compatibility helpers (best effort)
# -------------
ensure_mod() {
# ---
# Load a kernel module if possible (no hard failure).
# NOTE: In containers module loading is not possible; modules must be loaded on the host.
# ---
local m="$1"
# Already loaded?
if lsmod 2>/dev/null | awk '{print $1}' | grep -qx "$m" ; then
return 0
fi
# Skip in containers/guests without module loading capability
#
is_container && return 0
# Best effort modprobe
/sbin/modprobe "$m" >/dev/null 2>&1 || warn "Loading module '$m' failed (ok if not needed on this host)."
}
# --- Feature detection helpers (Debian 12/13 + containers)
module_loaded() {
lsmod 2>/dev/null | awk '{print $1}' | grep -qx "$1"
}
can_use_recent() {
# xt_recent is the kernel module behind "-m recent"
# In containers lsmod may be restricted; also accept presence of /proc/net/xt_recent.
module_loaded xt_recent && return 0
[ -d /proc/net/xt_recent ] && return 0
# As a last resort, ask iptables to parse the match (works if userspace has it)
"$ipt" -m recent -h >/dev/null 2>&1 && return 0
return 1
}
can_use_hashlimit() {
# xt_hashlimit is the kernel module behind "-m hashlimit"
module_loaded xt_hashlimit && return 0
[ -d /proc/net/xt_hashlimit ] && return 0
"${fw_command}" -m hashlimit -h >/dev/null 2>&1 && return 0
return 1
}
can_use_connlimit() {
# xt_connlimit is the kernel module behind "-m connlimit"
module_loaded xt_connlimit && return 0
"${fw_command}" -m connlimit -h >/dev/null 2>&1 && return 0
return 1
}
can_use_owner() {
# xt_owner is the kernel module behind "-m owner"
module_loaded xt_owner && return 0
"${fw_command}" -m owner -h >/dev/null 2>&1 && return 0
return 1
}
can_use_ct_target() {
# Check if iptables CT target exists (iptables-nft should support it when kernel has nf_tables CT support)
"${fw_command}" -t raw -j CT -h >/dev/null 2>&1 && return 0
return 1
}
can_use_helper_match() {
# Check if helper match exists
"${fw_command}" -m helper -h >/dev/null 2>&1 && return 0
return 1
}
can_use_nft() {
command -v nft >/dev/null 2>&1 && return 0
return 1
}
setup_ftp_conntrack_helper_output() {
# Prefer explicit helper assignment (safe with nf_conntrack_helper=0)
if can_use_ct_target ; then
"${fw_command}" -A OUTPUT -t raw -p tcp --dport "$standard_ftp_port" -j CT --helper ftp
return 0
fi
# nft fallback (nft-native helper assignment); keeps us "nft-ready"
if can_use_nft ; then
# Best-effort; may fail in containers without CAP_NET_ADMIN
nft add table ip fwhelper >/dev/null 2>&1 || true
nft add chain ip fwhelper output '{ type filter hook output priority raw; policy accept; }' >/dev/null 2>&1 || true
nft add ct helper ip fwhelper ftp '{ type "ftp" protocol tcp; }' >/dev/null 2>&1 || true
nft add rule ip fwhelper output tcp dport "$standard_ftp_port" ct helper set "ftp" >/dev/null 2>&1 && return 0
fi
warn "No CT helper assignment available (iptables CT target and nft fallback both unavailable). FTP active/passive may fail; FTPS workaround relies on recent/port rules."
return 1
}
setup_ftp_conntrack_helper_prerouting() {
# Prefer explicit helper assignment (safe with nf_conntrack_helper=0)
if can_use_ct_target ; then
"$ipt" -A PREROUTING -t raw -p tcp --dport "$standard_ftp_port" -j CT --helper ftp
return 0
fi
# nft fallback (nft-native helper assignment); keeps us "nft-ready"
if can_use_nft ; then
nft add table ip fwhelper >/dev/null 2>&1 || true
nft add chain ip fwhelper prerouting '{ type filter hook prerouting priority raw; policy accept; }' >/dev/null 2>&1 || true
nft add ct helper ip fwhelper ftp '{ type "ftp" protocol tcp; }' >/dev/null 2>&1 || true
nft add rule ip fwhelper prerouting tcp dport "$standard_ftp_port" ct helper set "ftp" >/dev/null 2>&1 && return 0
fi
warn "No CT helper assignment available (iptables CT target and nft fallback both unavailable). FTP server traffic may fail; consider enabling passive port ranges."
return 1
}