189 lines
5.5 KiB
Bash
189 lines
5.5 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() {
|
||
(( ENV_CHECK_NEED_PACKAGES == 1 )) && { log_info '正在安装缺失软件包...'; _env_install_packages || return 1; }
|
||
(( ENV_CHECK_NEED_STORAGE == 1 )) && { log_info '正在创建状态目录...'; mkdir -p "${IPF_STORAGE_DIR}"; chmod 750 "${IPF_STORAGE_DIR}"; }
|
||
(( ENV_CHECK_NEED_SYSCTL == 1 )) && { log_info '正在写入 sysctl 配置并启用转发...'; _env_write_sysctl || return 1; }
|
||
}
|
||
|
||
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[@]}"
|
||
}
|