Files
IPTables-Management/lib/iptables_ops.sh

262 lines
8.3 KiB
Bash

#!/usr/bin/env bash
if [[ -n ${IPF_IPTABLES_OPS_SH_LOADED:-} ]]; then
return 0
fi
IPF_IPTABLES_OPS_SH_LOADED=1
: "${IPTABLES_BIN:=iptables}"
: "${IP6TABLES_BIN:=ip6tables}"
: "${IPTABLES_SAVE_BIN:=iptables-save}"
: "${IP6TABLES_SAVE_BIN:=ip6tables-save}"
ipt_comment_tag() {
printf 'MGMT:%s\n' "$1"
}
ipt_protocols_for() {
case ${1-} in
tcp|udp) printf '%s\n' "$1" ;;
both) printf 'tcp\nudp\n' ;;
*) return 1 ;;
esac
}
ipt_families_for() {
case ${1-} in
4|6) printf '%s\n' "$1" ;;
both) printf '4\n6\n' ;;
*) return 1 ;;
esac
}
ipt_bin_for_family() {
case ${1-} in
4) printf '%s\n' "${IPTABLES_BIN}" ;;
6) printf '%s\n' "${IP6TABLES_BIN}" ;;
*) return 1 ;;
esac
}
ipt_save_bin_for_family() {
case ${1-} in
4) printf '%s\n' "${IPTABLES_SAVE_BIN}" ;;
6) printf '%s\n' "${IP6TABLES_SAVE_BIN}" ;;
*) return 1 ;;
esac
}
ipt_target_for_family() {
local raw=${1-}
local family=${2-}
if [[ ${raw} == *,* ]]; then
case ${family} in
4) printf '%s\n' "${raw%%,*}" ;;
6) printf '%s\n' "${raw#*,}" ;;
*) return 1 ;;
esac
return 0
fi
printf '%s\n' "${raw}"
}
ipt_to_destination() {
local tip_raw=${1-}
local tport=${2-}
local family=${3-}
local tip
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
if [[ ${family} == 6 ]]; then
printf '[%s]:%s\n' "${tip}" "${tport}"
else
printf '%s:%s\n' "${tip}" "${tport}"
fi
}
_ipt_exec_rule() {
local bin=$1
local table=$2
local action=$3
local chain=$4
shift 4
if [[ -n ${table} ]]; then
"${bin}" -t "${table}" "-${action}" "${chain}" "$@"
else
"${bin}" "-${action}" "${chain}" "$@"
fi
}
_ipt_check_rule() {
local bin=$1
local table=$2
local chain=$3
shift 3
if [[ -n ${table} ]]; then
"${bin}" -t "${table}" -C "${chain}" "$@" >/dev/null 2>&1
else
"${bin}" -C "${chain}" "$@" >/dev/null 2>&1
fi
}
_ipt_apply_expected_rules() {
local action=$1
local uuid=$2
local proto=$3
local lport=$4
local tip_raw=$5
local tport=$6
local ipver=$7
local family protocol bin tip comment destination
comment=$(ipt_comment_tag "${uuid}")
while IFS= read -r family; do
[[ -n ${family} ]] || continue
bin=$(ipt_bin_for_family "${family}")
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
destination=$(ipt_to_destination "${tip_raw}" "${tport}" "${family}")
while IFS= read -r protocol; do
[[ -n ${protocol} ]] || continue
if ! _ipt_exec_rule "${bin}" nat "${action}" PREROUTING \
-p "${protocol}" --dport "${lport}" \
-j DNAT --to-destination "${destination}" \
-m comment --comment "${comment}"; then
return 1
fi
if ! _ipt_exec_rule "${bin}" nat "${action}" POSTROUTING \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-j MASQUERADE \
-m comment --comment "${comment}"; then
return 1
fi
if ! _ipt_exec_rule "${bin}" '' "${action}" FORWARD \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}"; then
return 1
fi
if ! _ipt_exec_rule "${bin}" '' "${action}" FORWARD \
-p "${protocol}" -s "${tip}" --sport "${tport}" \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}"; then
return 1
fi
done < <(ipt_protocols_for "${proto}")
done < <(ipt_families_for "${ipver}")
}
ipt_apply_rule() {
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
if ! _ipt_apply_expected_rules A "${uuid}" "${proto}" "${lport}" "${tip_raw}" "${tport}" "${ipver}"; then
ipt_remove_rule "${uuid}" "${proto}" "${lport}" "${tip_raw}" "${tport}" "${ipver}" >/dev/null 2>&1 || true
return 1
fi
}
ipt_rule_healthy() {
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
local family protocol bin tip comment destination
comment=$(ipt_comment_tag "${uuid}")
while IFS= read -r family; do
[[ -n ${family} ]] || continue
bin=$(ipt_bin_for_family "${family}")
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
destination=$(ipt_to_destination "${tip_raw}" "${tport}" "${family}")
while IFS= read -r protocol; do
[[ -n ${protocol} ]] || continue
_ipt_check_rule "${bin}" nat PREROUTING \
-p "${protocol}" --dport "${lport}" \
-j DNAT --to-destination "${destination}" \
-m comment --comment "${comment}" || return 1
_ipt_check_rule "${bin}" nat POSTROUTING \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-j MASQUERADE \
-m comment --comment "${comment}" || return 1
_ipt_check_rule "${bin}" '' FORWARD \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}" || return 1
_ipt_check_rule "${bin}" '' FORWARD \
-p "${protocol}" -s "${tip}" --sport "${tport}" \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}" || return 1
done < <(ipt_protocols_for "${proto}")
done < <(ipt_families_for "${ipver}")
}
ipt_remove_rule() {
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
local -a families protocols
local family protocol bin tip comment destination
local missing=0
comment=$(ipt_comment_tag "${uuid}")
mapfile -t families < <(ipt_families_for "${ipver}")
mapfile -t protocols < <(ipt_protocols_for "${proto}")
for ((family_index=${#families[@]} - 1; family_index >= 0; family_index--)); do
family=${families[family_index]}
[[ -n ${family} ]] || continue
bin=$(ipt_bin_for_family "${family}")
tip=$(ipt_target_for_family "${tip_raw}" "${family}")
destination=$(ipt_to_destination "${tip_raw}" "${tport}" "${family}")
for ((proto_index=${#protocols[@]} - 1; proto_index >= 0; proto_index--)); do
protocol=${protocols[proto_index]}
[[ -n ${protocol} ]] || continue
if _ipt_check_rule "${bin}" '' FORWARD \
-p "${protocol}" -s "${tip}" --sport "${tport}" \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}"; then
_ipt_exec_rule "${bin}" '' D FORWARD \
-p "${protocol}" -s "${tip}" --sport "${tport}" \
-m conntrack --ctstate ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}" || return 1
else
missing=1
fi
if _ipt_check_rule "${bin}" '' FORWARD \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}"; then
_ipt_exec_rule "${bin}" '' D FORWARD \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-m conntrack --ctstate NEW,ESTABLISHED,RELATED \
-j ACCEPT \
-m comment --comment "${comment}" || return 1
else
missing=1
fi
if _ipt_check_rule "${bin}" nat POSTROUTING \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-j MASQUERADE \
-m comment --comment "${comment}"; then
_ipt_exec_rule "${bin}" nat D POSTROUTING \
-p "${protocol}" -d "${tip}" --dport "${tport}" \
-j MASQUERADE \
-m comment --comment "${comment}" || return 1
else
missing=1
fi
if _ipt_check_rule "${bin}" nat PREROUTING \
-p "${protocol}" --dport "${lport}" \
-j DNAT --to-destination "${destination}" \
-m comment --comment "${comment}"; then
_ipt_exec_rule "${bin}" nat D PREROUTING \
-p "${protocol}" --dport "${lport}" \
-j DNAT --to-destination "${destination}" \
-m comment --comment "${comment}" || return 1
else
missing=1
fi
done
done
if (( missing == 1 )); then
return 0
fi
}
ipt_find_by_uuid() {
local uuid=${1-}
local family save_bin found=1 line
[[ -n ${uuid} ]] || return 1
for family in 4 6; do
save_bin=$(ipt_save_bin_for_family "${family}")
if ! command_is_available "${save_bin}"; then
continue
fi
while IFS= read -r line || [[ -n ${line} ]]; do
[[ ${line} == *"MGMT:${uuid}"* ]] || continue
printf '%s|%s\n' "${family}" "${line}"
found=0
done < <("${save_bin}")
done
return "${found}"
}