The function used a chain of `(( NEED_X == 1 )) && { ... }` statements.
When the last flag is 0 the trailing arithmetic evaluates to false and
bash returns 1 from the function. Under the main script's `set -e` that
propagates up to bootstrap which exits with the misleading "line 60"
ERR trap right after the installer announces it is going to create the
state directory.
Rewrite the body as plain `if` blocks and return 0 explicitly. Add two
regression assertions that exercise the "only some flags set" and
"nothing to do" paths so the class of bug is caught in CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
200 lines
5.6 KiB
Bash
200 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
||
|
||
if [[ -n ${IPF_ENV_CHECK_SH_LOADED:-} ]]; then
|
||
return 0
|
||
fi
|
||
IPF_ENV_CHECK_SH_LOADED=1
|
||
|
||
: "${IPF_CHECK_IPTABLES_CMD:=iptables}"
|
||
: "${IPF_CHECK_IP6TABLES_CMD:=ip6tables}"
|
||
: "${IPF_CHECK_PERSIST_CMD:=netfilter-persistent}"
|
||
: "${DPKG_BIN:=dpkg}"
|
||
: "${APT_GET_BIN:=apt-get}"
|
||
: "${DEBCONF_SET_SELECTIONS_BIN:=debconf-set-selections}"
|
||
: "${SYSCTL_BIN:=sysctl}"
|
||
: "${SYSTEMCTL_BIN:=systemctl}"
|
||
: "${IPF_SYSCTL_FILE:=/etc/sysctl.d/99-iptables-forward.conf}"
|
||
: "${IPF_IPV4_FORWARD_FILE:=/proc/sys/net/ipv4/ip_forward}"
|
||
: "${IPF_IPV6_FORWARD_FILE:=/proc/sys/net/ipv6/conf/all/forwarding}"
|
||
: "${IPF_ASSUME_YES:=0}"
|
||
|
||
ENV_CHECK_ISSUES=()
|
||
ENV_CHECK_WARNINGS=()
|
||
ENV_CHECK_NEED_PACKAGES=0
|
||
ENV_CHECK_NEED_SYSCTL=0
|
||
ENV_CHECK_NEED_STORAGE=0
|
||
|
||
_env_add_issue() {
|
||
ENV_CHECK_ISSUES+=("$1")
|
||
}
|
||
|
||
_env_add_warning() {
|
||
ENV_CHECK_WARNINGS+=("$1")
|
||
}
|
||
|
||
_env_require_cmd() {
|
||
local cmd=$1 issue=$2
|
||
command_is_available "${cmd}" && return 0
|
||
_env_add_issue "${issue}"
|
||
ENV_CHECK_NEED_PACKAGES=1
|
||
}
|
||
|
||
_env_require_forward() {
|
||
local file=$1 issue=$2
|
||
[[ $(_env_file_value "${file}" 2>/dev/null || printf '0') == 1 ]] && return 0
|
||
_env_add_issue "${issue}"
|
||
ENV_CHECK_NEED_SYSCTL=1
|
||
}
|
||
|
||
_env_file_value() {
|
||
local file=$1
|
||
[[ -f ${file} ]] || return 1
|
||
tr -d '[:space:]' <"${file}"
|
||
}
|
||
|
||
_env_package_installed() {
|
||
if ! command_is_available "${DPKG_BIN}"; then
|
||
return 1
|
||
fi
|
||
"${DPKG_BIN}" -s iptables-persistent >/dev/null 2>&1
|
||
}
|
||
|
||
env_check_collect_issues() {
|
||
ENV_CHECK_ISSUES=()
|
||
ENV_CHECK_WARNINGS=()
|
||
ENV_CHECK_NEED_PACKAGES=0
|
||
ENV_CHECK_NEED_SYSCTL=0
|
||
ENV_CHECK_NEED_STORAGE=0
|
||
|
||
_env_require_cmd "${IPF_CHECK_IPTABLES_CMD}" '缺少 iptables 命令。'
|
||
_env_require_cmd "${IPF_CHECK_IP6TABLES_CMD}" '缺少 ip6tables 命令。'
|
||
if ! _env_package_installed || ! command_is_available "${IPF_CHECK_PERSIST_CMD}"; then
|
||
_env_add_issue '缺少 iptables-persistent / netfilter-persistent。'
|
||
ENV_CHECK_NEED_PACKAGES=1
|
||
fi
|
||
_env_require_forward "${IPF_IPV4_FORWARD_FILE}" 'IPv4 转发未开启。'
|
||
_env_require_forward "${IPF_IPV6_FORWARD_FILE}" 'IPv6 转发未开启。'
|
||
|
||
if [[ ! -d ${IPF_STORAGE_DIR} || ! -w ${IPF_STORAGE_DIR} ]]; then
|
||
_env_add_issue "状态目录不可用: ${IPF_STORAGE_DIR}"
|
||
ENV_CHECK_NEED_STORAGE=1
|
||
fi
|
||
|
||
if command_is_available "${SYSTEMCTL_BIN}"; then
|
||
if "${SYSTEMCTL_BIN}" is-active --quiet ufw 2>/dev/null; then
|
||
_env_add_warning '检测到 ufw 正在运行,可能会影响转发规则。'
|
||
fi
|
||
if "${SYSTEMCTL_BIN}" is-active --quiet firewalld 2>/dev/null; then
|
||
_env_add_warning '检测到 firewalld 正在运行,可能会影响转发规则。'
|
||
fi
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
env_check_print_report() {
|
||
local issue
|
||
if ((${#ENV_CHECK_ISSUES[@]} == 0)); then
|
||
log_ok '环境检查通过。'
|
||
else
|
||
log_warn '发现以下待修复项:'
|
||
for issue in "${ENV_CHECK_ISSUES[@]}"; do
|
||
printf ' - %s\n' "${issue}" >&2
|
||
done
|
||
fi
|
||
|
||
if ((${#ENV_CHECK_WARNINGS[@]} > 0)); then
|
||
log_warn '附加警告:'
|
||
for issue in "${ENV_CHECK_WARNINGS[@]}"; do
|
||
printf ' - %s\n' "${issue}" >&2
|
||
done
|
||
fi
|
||
}
|
||
|
||
_env_install_packages() {
|
||
command_is_available "${APT_GET_BIN}" || { log_err '缺少 apt-get,无法自动安装依赖。'; return 1; }
|
||
command_is_available "${DEBCONF_SET_SELECTIONS_BIN}" || {
|
||
log_err '缺少 debconf-set-selections,无法预置安装选项。'
|
||
return 1
|
||
}
|
||
|
||
printf 'iptables-persistent iptables-persistent/autosave_v4 boolean true\n' | "${DEBCONF_SET_SELECTIONS_BIN}"
|
||
printf 'iptables-persistent iptables-persistent/autosave_v6 boolean true\n' | "${DEBCONF_SET_SELECTIONS_BIN}"
|
||
DEBIAN_FRONTEND=noninteractive "${APT_GET_BIN}" update -qq
|
||
DEBIAN_FRONTEND=noninteractive "${APT_GET_BIN}" install -y -qq iptables iptables-persistent
|
||
}
|
||
|
||
_env_write_sysctl() {
|
||
local backup=''
|
||
if [[ -f ${IPF_SYSCTL_FILE} ]]; then
|
||
backup=$(cat "${IPF_SYSCTL_FILE}")
|
||
fi
|
||
|
||
cat >"${IPF_SYSCTL_FILE}" <<SYSCTL
|
||
net.ipv4.ip_forward=1
|
||
net.ipv6.conf.all.forwarding=1
|
||
SYSCTL
|
||
|
||
if ! command_is_available "${SYSCTL_BIN}"; then
|
||
log_err '缺少 sysctl,无法应用转发设置。'
|
||
if [[ -n ${backup} ]]; then
|
||
printf '%s' "${backup}" >"${IPF_SYSCTL_FILE}"
|
||
else
|
||
rm -f "${IPF_SYSCTL_FILE}"
|
||
fi
|
||
return 1
|
||
fi
|
||
|
||
if ! "${SYSCTL_BIN}" --system >/dev/null; then
|
||
if [[ -n ${backup} ]]; then
|
||
printf '%s' "${backup}" >"${IPF_SYSCTL_FILE}"
|
||
else
|
||
rm -f "${IPF_SYSCTL_FILE}"
|
||
fi
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
env_check_apply_fixes() {
|
||
if (( ENV_CHECK_NEED_PACKAGES == 1 )); then
|
||
log_info '正在安装缺失软件包...'
|
||
_env_install_packages || return 1
|
||
fi
|
||
if (( ENV_CHECK_NEED_STORAGE == 1 )); then
|
||
log_info '正在创建状态目录...'
|
||
mkdir -p "${IPF_STORAGE_DIR}"
|
||
chmod 750 "${IPF_STORAGE_DIR}"
|
||
fi
|
||
if (( ENV_CHECK_NEED_SYSCTL == 1 )); then
|
||
log_info '正在写入 sysctl 配置并启用转发...'
|
||
_env_write_sysctl || return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
env_check_all() {
|
||
env_check_collect_issues
|
||
env_check_print_report
|
||
|
||
((${#ENV_CHECK_ISSUES[@]} == 0)) && return 0
|
||
if [[ ${IPF_ASSUME_YES} == 1 ]]; then
|
||
log_info '已启用自动确认,开始修复。'
|
||
elif ! prompt_confirm '是否自动修复以上问题?' n; then
|
||
log_err '用户取消自动修复,请根据提示手动安装依赖并重试。'
|
||
return 3
|
||
fi
|
||
|
||
env_check_apply_fixes || return 1
|
||
env_check_collect_issues
|
||
env_check_print_report
|
||
|
||
((${#ENV_CHECK_ISSUES[@]} == 0)) && return 0
|
||
log_err '自动修复后仍存在未解决的问题。'
|
||
return 1
|
||
}
|
||
|
||
env_status_summary() {
|
||
env_check_collect_issues
|
||
((${#ENV_CHECK_ISSUES[@]} == 0)) && printf '就绪\n' || printf '待修复(%d)\n' "${#ENV_CHECK_ISSUES[@]}"
|
||
}
|