Harden rule health checks
This commit is contained in:
@@ -69,6 +69,11 @@ sudo ./iptables-forward.sh
|
|||||||
- 立即保存到磁盘
|
- 立即保存到磁盘
|
||||||
- 退出
|
- 退出
|
||||||
|
|
||||||
|
规则列表中的状态列含义:
|
||||||
|
|
||||||
|
- `✓`:该条规则对应的 PREROUTING / POSTROUTING / FORWARD 运行态规则都存在
|
||||||
|
- `!`:至少缺少一条运行态规则,或仅剩数据库记录
|
||||||
|
|
||||||
### 批处理模式
|
### 批处理模式
|
||||||
|
|
||||||
用于自动化测试或脚本调用:
|
用于自动化测试或脚本调用:
|
||||||
|
|||||||
@@ -150,6 +150,36 @@ ipt_apply_rule() {
|
|||||||
fi
|
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
|
||||||
|
done < <(ipt_protocols_for "${proto}")
|
||||||
|
done < <(ipt_families_for "${ipver}")
|
||||||
|
}
|
||||||
|
|
||||||
ipt_remove_rule() {
|
ipt_remove_rule() {
|
||||||
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
|
local uuid=$1 proto=$2 lport=$3 tip_raw=$4 tport=$5 ipver=$6
|
||||||
local -a families protocols
|
local -a families protocols
|
||||||
|
|||||||
@@ -130,8 +130,16 @@ rules_load_lines() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rule_health_mark() {
|
rule_health_mark() {
|
||||||
local uuid=${1-}
|
local line=${1-}
|
||||||
if ipt_find_by_uuid "${uuid}" >/dev/null 2>&1; then
|
local uuid proto lport tip tport ipver
|
||||||
|
uuid=$(rule_field "${line}" uuid)
|
||||||
|
proto=$(rule_field "${line}" proto)
|
||||||
|
lport=$(rule_field "${line}" lport)
|
||||||
|
tip=$(rule_field "${line}" tip)
|
||||||
|
tport=$(rule_field "${line}" tport)
|
||||||
|
ipver=$(rule_field "${line}" ipver)
|
||||||
|
|
||||||
|
if ipt_rule_healthy "${uuid}" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}"; then
|
||||||
printf '✓\n'
|
printf '✓\n'
|
||||||
else
|
else
|
||||||
printf '!\n'
|
printf '!\n'
|
||||||
@@ -156,7 +164,7 @@ render_rules_plain() {
|
|||||||
tport=$(rule_field "${line}" tport)
|
tport=$(rule_field "${line}" tport)
|
||||||
ipver=$(rule_ipver_label "$(rule_field "${line}" ipver)")
|
ipver=$(rule_ipver_label "$(rule_field "${line}" ipver)")
|
||||||
desc=$(rule_field "${line}" desc || true)
|
desc=$(rule_field "${line}" desc || true)
|
||||||
health=$(rule_health_mark "${uuid}")
|
health=$(rule_health_mark "${line}")
|
||||||
printf '%-3s %-10s %-10s %-32s %-10s %-12s %-4s %s\n' \
|
printf '%-3s %-10s %-10s %-32s %-10s %-12s %-4s %s\n' \
|
||||||
"$((idx + 1))" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" "${health}" "${desc}"
|
"$((idx + 1))" "${proto}" "${lport}" "${tip}" "${tport}" "${ipver}" "${health}" "${desc}"
|
||||||
done
|
done
|
||||||
@@ -166,7 +174,7 @@ cmd_list() {
|
|||||||
local pause=${1:-1}
|
local pause=${1:-1}
|
||||||
rules_load_lines
|
rules_load_lines
|
||||||
render_rules_plain "${RULES_CACHE[@]}"
|
render_rules_plain "${RULES_CACHE[@]}"
|
||||||
printf '[✓] 已在 iptables 中找到规则;[!] 仅存在于数据库中。\n'
|
printf '[✓] 运行态规则完整;[!] 至少缺少一条运行态规则或仅存在于数据库中。\n'
|
||||||
if [[ ${pause} == 1 ]]; then
|
if [[ ${pause} == 1 ]]; then
|
||||||
pause_return
|
pause_return
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -85,10 +85,14 @@ export PERSIST_FIXTURE_LOG
|
|||||||
|
|
||||||
# shellcheck source=lib/common.sh
|
# shellcheck source=lib/common.sh
|
||||||
source "${ROOT_DIR}/lib/common.sh"
|
source "${ROOT_DIR}/lib/common.sh"
|
||||||
|
# shellcheck source=lib/storage.sh
|
||||||
|
source "${ROOT_DIR}/lib/storage.sh"
|
||||||
# shellcheck source=lib/persist.sh
|
# shellcheck source=lib/persist.sh
|
||||||
source "${ROOT_DIR}/lib/persist.sh"
|
source "${ROOT_DIR}/lib/persist.sh"
|
||||||
# shellcheck source=lib/iptables_ops.sh
|
# shellcheck source=lib/iptables_ops.sh
|
||||||
source "${ROOT_DIR}/lib/iptables_ops.sh"
|
source "${ROOT_DIR}/lib/iptables_ops.sh"
|
||||||
|
# shellcheck source=lib/rules_mgr.sh
|
||||||
|
source "${ROOT_DIR}/lib/rules_mgr.sh"
|
||||||
|
|
||||||
uuid_v4=$("${ROOT_DIR}/iptables-forward.sh" --batch add tcp 65432 127.0.0.1 22 4 'integration-v4')
|
uuid_v4=$("${ROOT_DIR}/iptables-forward.sh" --batch add tcp 65432 127.0.0.1 22 4 'integration-v4')
|
||||||
assert_contains "$(iptables-save)" "MGMT:${uuid_v4}" 'IPv4 rule should appear in iptables-save output'
|
assert_contains "$(iptables-save)" "MGMT:${uuid_v4}" 'IPv4 rule should appear in iptables-save output'
|
||||||
@@ -96,9 +100,19 @@ assert_contains "$("${ROOT_DIR}/iptables-forward.sh" --batch list)" "uuid=${uuid
|
|||||||
assert_file_contains "${PERSIST_RULES_V4}" "MGMT:${uuid_v4}" 'persist save should write IPv4 rules snapshot'
|
assert_file_contains "${PERSIST_RULES_V4}" "MGMT:${uuid_v4}" 'persist save should write IPv4 rules snapshot'
|
||||||
assert_file_contains "${PERSIST_FIXTURE_LOG}" 'persist-fixture.sh save' 'adding a rule should call persist save'
|
assert_file_contains "${PERSIST_FIXTURE_LOG}" 'persist-fixture.sh save' 'adding a rule should call persist save'
|
||||||
|
|
||||||
|
iptables -t nat -D PREROUTING \
|
||||||
|
-p tcp --dport 65432 \
|
||||||
|
-j DNAT --to-destination 127.0.0.1:22 \
|
||||||
|
-m comment --comment "MGMT:${uuid_v4}"
|
||||||
|
list_output=$(cmd_list 0)
|
||||||
|
[[ ${list_output} =~ [[:space:]]![[:space:]] ]] || fail 'partial runtime loss should show degraded health in cmd_list'
|
||||||
|
|
||||||
|
persist_reload
|
||||||
|
list_output=$(cmd_list 0)
|
||||||
|
[[ ${list_output} =~ [[:space:]]✓[[:space:]] ]] || fail 'persist_reload should restore healthy status in cmd_list'
|
||||||
|
|
||||||
ipt_remove_rule "${uuid_v4}" tcp 65432 127.0.0.1 22 4
|
ipt_remove_rule "${uuid_v4}" tcp 65432 127.0.0.1 22 4
|
||||||
assert_status 1 "$(status_of grep -F "MGMT:${uuid_v4}" <(iptables-save))" 'manual runtime removal should clear managed IPv4 rule'
|
assert_status 1 "$(status_of grep -F "MGMT:${uuid_v4}" <(iptables-save))" 'manual runtime removal should clear managed IPv4 rule'
|
||||||
|
|
||||||
persist_reload
|
persist_reload
|
||||||
assert_file_contains "${PERSIST_FIXTURE_LOG}" 'persist-fixture.sh reload' 'persist_reload should call persistence wrapper'
|
assert_file_contains "${PERSIST_FIXTURE_LOG}" 'persist-fixture.sh reload' 'persist_reload should call persistence wrapper'
|
||||||
assert_contains "$(iptables-save)" "MGMT:${uuid_v4}" 'persist_reload should restore IPv4 rule from snapshot'
|
assert_contains "$(iptables-save)" "MGMT:${uuid_v4}" 'persist_reload should restore IPv4 rule from snapshot'
|
||||||
|
|||||||
@@ -60,11 +60,22 @@ reset_mock_state() {
|
|||||||
|
|
||||||
reset_mock_state
|
reset_mock_state
|
||||||
uuid_v4=$(cmd_add_batch tcp 8080 127.0.0.1 80 4 'web service')
|
uuid_v4=$(cmd_add_batch tcp 8080 127.0.0.1 80 4 'web service')
|
||||||
|
line_v4=$(storage_get "${uuid_v4}")
|
||||||
assert_eq '1' "$(storage_count)" 'cmd_add_batch should persist one rule'
|
assert_eq '1' "$(storage_count)" 'cmd_add_batch should persist one rule'
|
||||||
assert_eq '3' "$(grep -Ec '^iptables ' "${IPTABLES_MOCK_LOG}")" 'tcp/ipv4 add should emit three iptables commands'
|
assert_eq '3' "$(grep -Ec '^iptables ' "${IPTABLES_MOCK_LOG}")" 'tcp/ipv4 add should emit three iptables commands'
|
||||||
assert_contains "$(storage_get "${uuid_v4}")" "uuid=${uuid_v4}" 'stored line should contain generated uuid'
|
assert_contains "${line_v4}" "uuid=${uuid_v4}" 'stored line should contain generated uuid'
|
||||||
assert_eq '1' "$(grep -Ec 'persist-mock\.sh save' "${PERSIST_MOCK_LOG}")" 'successful add should trigger persist_save'
|
assert_eq '1' "$(grep -Ec 'persist-mock\.sh save' "${PERSIST_MOCK_LOG}")" 'successful add should trigger persist_save'
|
||||||
assert_contains "$(ipt_find_by_uuid "${uuid_v4}")" "MGMT:${uuid_v4}" 'ipt_find_by_uuid should locate saved mock rules'
|
assert_contains "$(ipt_find_by_uuid "${uuid_v4}")" "MGMT:${uuid_v4}" 'ipt_find_by_uuid should locate saved mock rules'
|
||||||
|
assert_eq '✓' "$(rule_health_mark "${line_v4}")" 'healthy runtime rule should show ok marker'
|
||||||
|
|
||||||
|
"${IPTABLES_BIN}" -t nat -D PREROUTING \
|
||||||
|
-p tcp --dport 8080 \
|
||||||
|
-j DNAT --to-destination 127.0.0.1:80 \
|
||||||
|
-m comment --comment "MGMT:${uuid_v4}"
|
||||||
|
assert_contains "$(ipt_find_by_uuid "${uuid_v4}")" "MGMT:${uuid_v4}" 'partial runtime loss should still leave uuid-tagged rules'
|
||||||
|
assert_eq '!' "$(rule_health_mark "${line_v4}")" 'partial runtime loss should mark rule unhealthy'
|
||||||
|
list_output=$(cmd_list 0)
|
||||||
|
[[ ${list_output} =~ [[:space:]]![[:space:]] ]] || fail 'cmd_list should expose degraded health marker'
|
||||||
|
|
||||||
reset_mock_state
|
reset_mock_state
|
||||||
uuid_both=$(cmd_add_batch both 5353 '127.0.0.1,::1' 53 both 'dual stack dns')
|
uuid_both=$(cmd_add_batch both 5353 '127.0.0.1,::1' 53 both 'dual stack dns')
|
||||||
|
|||||||
Reference in New Issue
Block a user