#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: check_sec_baseline.sh
# 功能描述: 自动检测公网服务器安全基线配置与端口暴露面 (纯只读)
# 使用方式: 在宿主机以 root 权限执行
# ==============================================================================
set -o pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'
echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE} 云主机底层安全基线与公网暴露面自动化巡检 ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"
# 1. 统计近期遭受暴力破解尝试的次数
echo -e "\n${YELLOW}[1/4] 正在统计近期登录失败记录 (/var/log/btmp)...${PLAIN}"
if command -v lastb &>/dev/null; then
# 修复了这里的管道符乱码
FAIL_COUNT=$(lastb 2>/dev/null | grep -v "^$" | grep -v "^btmp begins" | wc -l)
if [ "$FAIL_COUNT" -gt 1000 ]; then
echo -e " -> ${RED}[ALERT] 检测到累计 ${FAIL_COUNT} 次登录失败记录,主机正面临高频暴力破解探测!${PLAIN}"
else
echo -e " -> ${GREEN}[OK] 近期登录失败记录为 ${FAIL_COUNT} 次,探测频率处于可控范围。${PLAIN}"
fi
else
echo -e " -> ${YELLOW}[INFO] 未找到 lastb 命令,跳过登录失败次数统计。${PLAIN}"
fi
# 2. 检查 SSH 服务安全基线配置
echo -e "\n${YELLOW}[2/4] 检查 SSH 服务安全基线配置 (/etc/ssh/sshd_config)...${PLAIN}"
SSH_PORT=$(grep -iE "^[[:space:]]*Port[[:space:]]+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' | head -n 1)
PWD_AUTH=$(grep -iE "^[[:space:]]*PasswordAuthentication[[:space:]]+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' | tail -n 1)
# 修复了这里的逻辑或乱码
if [ -z "$SSH_PORT" ] || [ "$SSH_PORT" == "22" ]; then
echo -e " -> 端口配置: ${RED}22 (默认高危端口)${PLAIN}"
echo -e " -> ${YELLOW}[建议] 默认 22 端口极易遭受全网自动化扫描,建议修改为高位端口。${PLAIN}"
else
echo -e " -> 端口配置: ${GREEN}${SSH_PORT} (非默认端口,合规)${PLAIN}"
fi
# 修复了这里的逻辑或乱码
if [[ "$PWD_AUTH" == "no" || "$PWD_AUTH" == "No" ]]; then
echo -e " -> 密码认证: ${GREEN}已禁用 (仅允许密钥登录,合规)${PLAIN}"
else
echo -e " -> 密码认证: ${RED}已开启 (存在字典暴力破解风险)${PLAIN}"
fi
# 3. 检查公网监听端口暴露面
echo -e "\n${YELLOW}[3/4] 扫描全网监听 (0.0.0.0 / :::) 的服务端口暴露面...${PLAIN}"
PUBLIC_PORTS=$(ss -tulnp | awk 'NR>1 && ($5 ~ /^0\.0\.0\.0:/ || $5 ~ /^\[::\]:/ || $5 ~ /^\*:/) {print $1, $5, $7}' | sort -u)
if [ -n "$PUBLIC_PORTS" ]; then
echo -e " -> ${YELLOW}[WARN] 检测到以下端口处于全网监听状态,请核实是否需要对外暴露:${PLAIN}"
echo "$PUBLIC_PORTS" | while read -r proto addr_port proc; do
p_name=$(echo "$proc" | grep -o '("[^"]*"' | head -n 1 | tr -d '"(')
[ -z "$p_name" ] && p_name="未知/内核进程"
# 修复了这里的输出字符串管道符乱码
echo -e " 协议: ${proto} | 监听地址: ${YELLOW}${addr_port}${PLAIN} | 进程: ${RED}${p_name}${PLAIN}"
done
echo -e " -> ${GREEN}[收敛建议] 内部数据库、缓存及后端应用建议修改为监听 127.0.0.1,统一通过网关对外暴露。${PLAIN}"
else
echo -e " -> ${GREEN}[OK] 未检测到监听在 0.0.0.0 的服务端口。${PLAIN}"
fi
# 4. 检查动态封禁机制 (Fail2ban)
echo -e "\n${YELLOW}[4/4] 检查动态防爆破组件状态 (Fail2ban)...${PLAIN}"
if command -v fail2ban-client &>/dev/null; then
F2B_STATUS=$(systemctl is-active fail2ban 2>/dev/null)
if [ "$F2B_STATUS" == "active" ]; then
JAILS=$(fail2ban-client status 2>/dev/null | grep "Jail list:" | sed 's/.*Jail list://' | tr -d '\t')
echo -e " -> 运行状态: ${GREEN}运行中 (active)${PLAIN}"
echo -e " -> 启用策略:${GREEN}${JAILS}${PLAIN}"
else
echo -e " -> 运行状态: ${RED}未运行 ($F2B_STATUS)${PLAIN}"
fi
else
echo -e " -> 组件状态: ${RED}未安装 Fail2ban${PLAIN}"
echo -e " -> ${YELLOW}[建议] 建议部署 Fail2ban 联动防火墙实现对高频恶意探测 IP 的自动封禁。${PLAIN}"
fi
echo -e "\n${BLUE}================================================================${PLAIN}"