diff --git a/vps_init.sh b/vps_init.sh index 846087d..3e90af7 100644 --- a/vps_init.sh +++ b/vps_init.sh @@ -225,6 +225,59 @@ handle_error() { # 设置错误跟踪 trap 'handle_error $LINENO' ERR +# =========================================== +# 安全更新软件源:自动检测并禁用失效的软件源 +# (例如 Debian 版本归档后失效的 *-backports 源)后重试 +# =========================================== +apt_update_safe() { + local tmp retry=0 + tmp=$(mktemp) + while :; do + # 执行更新,忽略退出码(失效的源会让 apt 返回非0) + apt-get update > "$tmp" 2>&1 || true + cat "$tmp" >> "$LOG_FILE" + cat "$tmp" + + # 没有检测到失效源,更新成功 + if ! grep -qiE "no longer has a Release file|404 +Not Found|Failed to fetch" "$tmp"; then + rm -f "$tmp" + return 0 + fi + + # 已尝试修复过一次,仍有问题则忽略并继续,避免死循环 + if [ "$retry" -ge 1 ]; then + log "${YELLOW}部分软件源仍不可用,将忽略失效源并继续${NC}" + rm -f "$tmp" + return 0 + fi + + log "${YELLOW}检测到失效的软件源,正在自动禁用...${NC}" + + # 从 apt 输出中提取失效的套件名(位于 " Release" 中) + local suites suite + suites=$(grep -oE "The repository '[^']+ Release'" "$tmp" \ + | sed -E "s/The repository '(.*) Release'/\1/" \ + | awk '{print $NF}' | sort -u) + + if [ -z "$suites" ]; then + log "${YELLOW}无法解析失效套件名,将忽略失效源并继续${NC}" + rm -f "$tmp" + return 0 + fi + + for suite in $suites; do + [ -n "$suite" ] || continue + log "${YELLOW} - 禁用失效套件: ${suite}${NC}" + for f in /etc/apt/sources.list $(ls /etc/apt/sources.list.d/*.list 2>/dev/null); do + [ -f "$f" ] || continue + # 注释掉引用该失效套件的未注释行 + sed -i -E "s|^([^#].*[[:space:]]${suite}[[:space:]].*)$|#\1|g" "$f" + done + done + retry=$((retry + 1)) + done +} + # =========================================== # 1. 系统更新 # =========================================== @@ -238,7 +291,7 @@ fi # 更新系统包 if [ "$OS_TYPE" = "debian" ]; then - apt update -y || log "${RED}更新软件源失败${NC}" + apt_update_safe DEBIAN_FRONTEND=noninteractive apt full-upgrade -y || log "${RED}系统升级失败${NC}" apt install -y wget curl sudo vim git ufw net-tools htop iftop || log "${RED}安装基础软件包失败${NC}" log "${GREEN}系统更新完成,安装了常用工具${NC}" @@ -378,22 +431,32 @@ if command -v docker &> /dev/null; then else # 安装Docker if [ "$OS_TYPE" = "debian" ]; then + # 先确保软件源可用(官方脚本内部也会执行 apt update) + apt_update_safe + # 使用官方安装脚本 curl -fsSL https://get.docker.com -o get-docker.sh - sh get-docker.sh - rm get-docker.sh - systemctl enable docker - - # 安装Docker Compose - if ! command -v docker-compose &> /dev/null; then - curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - log "${GREEN}Docker Compose 安装完成${NC}" + sh get-docker.sh || log "${RED}Docker安装脚本执行出现问题${NC}" + rm -f get-docker.sh + + # 校验Docker是否真正安装成功 + if command -v docker &> /dev/null; then + systemctl enable docker 2>/dev/null || log "${YELLOW}无法设置Docker开机自启${NC}" + systemctl start docker 2>/dev/null || true + + # 安装Docker Compose + if ! command -v docker-compose &> /dev/null; then + curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + log "${GREEN}Docker Compose 安装完成${NC}" + fi + + log "${GREEN}Docker安装完成,版本信息:${NC}" + docker --version + docker-compose --version 2>/dev/null || docker compose version 2>/dev/null || true + else + log "${RED}Docker安装失败,请检查网络或软件源后重试,可手动执行: curl -fsSL https://get.docker.com | sh${NC}" fi - - log "${GREEN}Docker安装完成,版本信息:${NC}" - docker --version - docker-compose --version else log "${YELLOW}非Debian系统,请手动安装Docker${NC}" fi @@ -406,7 +469,8 @@ log "${BLUE}[6/10] 防火墙设置开始...${NC}" # 安装UFW if [ "$OS_TYPE" = "debian" ]; then - apt update -y && apt install -y ufw net-tools lsof + apt_update_safe + apt install -y ufw net-tools lsof || log "${RED}安装防火墙相关软件包(ufw/net-tools/lsof)失败${NC}" # 确保防火墙默认策略 ufw default deny incoming @@ -441,8 +505,8 @@ if [ "$OS_TYPE" = "debian" ]; then LISTENING_PORTS=$(netstat -tlnp 2>/dev/null | grep "LISTEN" | awk '{print $4}' | awk -F: '{print $NF}' | sort -n | uniq) # 使用lsof作为备选方法 - if [ -z "$LISTENING_PORTS" ]; then - LISTENING_PORTS=$(lsof -i -P -n | grep LISTEN | awk '{print $9}' | awk -F: '{print $NF}' | sort -n | uniq) + if [ -z "$LISTENING_PORTS" ] && command -v lsof &> /dev/null; then + LISTENING_PORTS=$(lsof -i -P -n 2>/dev/null | grep LISTEN | awk '{print $9}' | awk -F: '{print $NF}' | sort -n | uniq) fi # 如果仍然为空,提示手动检查 @@ -457,7 +521,10 @@ if [ "$OS_TYPE" = "debian" ]; then "$PORT" -lt "65535" && "$PORT" -gt "1024" ]]; then # 尝试找出服务名称 - SERVICE=$(lsof -i:$PORT -sTCP:LISTEN | grep -v "COMMAND" | awk '{print $1}' | head -1) + SERVICE="" + if command -v lsof &> /dev/null; then + SERVICE=$(lsof -i:$PORT -sTCP:LISTEN 2>/dev/null | grep -v "COMMAND" | awk '{print $1}' | head -1) + fi if [ -z "$SERVICE" ]; then SERVICE=$(netstat -tlnp 2>/dev/null | grep ":$PORT" | awk '{print $7}' | cut -d"/" -f2 | head -1) fi @@ -579,18 +646,26 @@ log "${BLUE}[9/10] Fail2ban安装开始...${NC}" if [ "$OS_TYPE" = "debian" ]; then # 安装Fail2ban - apt update -y && apt install -y fail2ban - systemctl start fail2ban - systemctl enable fail2ban - - # 配置Fail2ban - cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local - - # 清理任何现有配置 - rm -rf /etc/fail2ban/jail.d/* 2>/dev/null || true - - # 创建SSH防护配置 - cat < /etc/fail2ban/jail.d/sshd.local + apt_update_safe + apt install -y fail2ban || log "${RED}安装Fail2ban失败${NC}" + + # 校验是否真正安装成功 + if ! command -v fail2ban-client &> /dev/null && ! dpkg -l fail2ban 2>/dev/null | grep -q "^ii"; then + log "${RED}Fail2ban未成功安装,跳过其配置(请检查软件源后重试)${NC}" + else + systemctl enable fail2ban 2>/dev/null || log "${YELLOW}无法设置Fail2ban开机自启${NC}" + + # 配置Fail2ban + mkdir -p /etc/fail2ban/jail.d + if [ -f /etc/fail2ban/jail.conf ]; then + cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local + fi + + # 清理任何现有配置 + rm -rf /etc/fail2ban/jail.d/* 2>/dev/null || true + + # 创建SSH防护配置 + cat < /etc/fail2ban/jail.d/sshd.local [sshd] enabled = true mode = normal @@ -601,45 +676,46 @@ maxretry = 5 bantime = 1h findtime = 10m EOF - - # 重启Fail2ban - log "${YELLOW}重启Fail2ban服务...${NC}" - systemctl restart fail2ban - - # 等待服务启动完成 - log "${YELLOW}等待Fail2ban服务完全启动...${NC}" - sleep 5 - - # 检查服务状态 - if systemctl is-active --quiet fail2ban; then - log "${GREEN}Fail2ban服务已成功启动${NC}" - - # 显示Fail2ban状态(使用错误处理避免脚本终止) - log "${YELLOW}获取Fail2ban状态信息...${NC}" - - # 尝试获取fail2ban状态,忽略可能的错误 - fail2ban-client status >/dev/null 2>&1 || log "${YELLOW}无法获取fail2ban综合状态,但这不影响功能${NC}" - - # 尝试获取sshd监狱状态 - if fail2ban-client status sshd >/dev/null 2>&1; then - log "${GREEN}SSH防护已成功配置${NC}" - # 只有在前面成功的情况下才显示详细信息 - fail2ban-client status sshd + + # 重启Fail2ban + log "${YELLOW}重启Fail2ban服务...${NC}" + systemctl restart fail2ban 2>/dev/null || systemctl start fail2ban 2>/dev/null || log "${RED}Fail2ban服务启动失败${NC}" + + # 等待服务启动完成 + log "${YELLOW}等待Fail2ban服务完全启动...${NC}" + sleep 5 + + # 检查服务状态 + if systemctl is-active --quiet fail2ban; then + log "${GREEN}Fail2ban服务已成功启动${NC}" + + # 显示Fail2ban状态(使用错误处理避免脚本终止) + log "${YELLOW}获取Fail2ban状态信息...${NC}" + + # 尝试获取fail2ban状态,忽略可能的错误 + fail2ban-client status >/dev/null 2>&1 || log "${YELLOW}无法获取fail2ban综合状态,但这不影响功能${NC}" + + # 尝试获取sshd监狱状态 + if fail2ban-client status sshd >/dev/null 2>&1; then + log "${GREEN}SSH防护已成功配置${NC}" + # 只有在前面成功的情况下才显示详细信息 + fail2ban-client status sshd + else + log "${YELLOW}无法获取SSH监狱状态,这可能是因为服务刚刚启动或配置需要更多时间生效${NC}" + log "${YELLOW}如果在重启后仍有问题,请检查 /var/log/fail2ban.log${NC}" + fi + + # 显示服务状态 + systemctl status fail2ban --no-pager || true else - log "${YELLOW}无法获取SSH监狱状态,这可能是因为服务刚刚启动或配置需要更多时间生效${NC}" - log "${YELLOW}如果在重启后仍有问题,请检查 /var/log/fail2ban.log${NC}" + log "${RED}Fail2ban服务启动失败,请检查错误日志${NC}" + log "${YELLOW}尝试查看Fail2ban日志获取错误详情:${NC}" + tail -n 20 /var/log/fail2ban.log 2>/dev/null || log "${RED}无法读取Fail2ban日志${NC}" fi - - # 显示服务状态 - systemctl status fail2ban --no-pager || true - else - log "${RED}Fail2ban服务启动失败,请检查错误日志${NC}" - log "${YELLOW}尝试查看Fail2ban日志获取错误详情:${NC}" - tail -n 20 /var/log/fail2ban.log 2>/dev/null || log "${RED}无法读取Fail2ban日志${NC}" + + log "${GREEN}Fail2ban安装和配置完成${NC}" + log "${YELLOW}如果出现临时错误,服务器重启后通常会正常工作${NC}" fi - - log "${GREEN}Fail2ban安装和配置完成${NC}" - log "${YELLOW}如果出现临时错误,服务器重启后通常会正常工作${NC}" else log "${YELLOW}非Debian系统,请手动安装Fail2ban${NC}" fi