262 lines
8.3 KiB
Bash
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}"
|
|
}
|