erver Security Check Script # 適用: Ubuntu 22.04 / 24.04 LTS # 執行方式: sudo bash security_check.sh # ============================================================ # ---------- 顏色 ---------- GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m' CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' PASS=0; WARN=0; FAIL=0 ok() { echo -e " ${GREEN}[✓ PASS]${NC} $1"; ((PASS++)); } warn() { echo -e " ${YELLOW}[⚠ WARN]${NC} $1"; ((WARN++)); } fail() { echo -e " ${RED}[✗ FAIL]${NC} $1"; ((FAIL++)); } section() { echo -e "\n${CYAN}${BOLD}── $1 ──${NC}"; } [[ $EUID -ne 0 ]] && { echo -e "${RED}請用 sudo 執行${NC}"; exit 1; } clear echo -e "${CYAN}${BOLD}" echo "╔══════════════════════════════════════════════════════╗" echo "║ Ubuntu Server Security Check Report ║" echo "║ $(date '+%Y-%m-%d %H:%M:%S') ║" echo "╚══════════════════════════════════════════════════════╝" echo -e "${NC}" # ============================================================ # 1. 系統更新狀態 # ============================================================ section "1. 系統更新狀態" UPGRADABLE=$(apt list --upgradable 2>/dev/null | grep -c upgradable || true) if [[ "$UPGRADABLE" -le 1 ]]; then ok "系統套件已是最新版本" else warn "有 $((UPGRADABLE - 1)) 個套件尚未更新(執行 apt upgrade 處理)" fi LAST_UPDATE=$(stat -c %Y /var/lib/apt/lists 2>/dev/null) NOW=$(date +%s) DAYS_AGO=$(( (NOW - LAST_UPDATE) / 86400 )) if [[ $DAYS_AGO -le 1 ]]; then ok "apt 快取更新時間:${DAYS_AGO} 天前" elif [[ $DAYS_AGO -le 7 ]]; then warn "apt 快取已 ${DAYS_AGO} 天未更新" else fail "apt 快取已超過 ${DAYS_AGO} 天未更新,建議執行 apt update" fi # ============================================================ # 2. 自動安全更新 # ============================================================ section "2. 自動安全更新 (unattended-upgrades)" if dpkg -l unattended-upgrades &>/dev/null; then ok "unattended-upgrades 已安裝" else fail "unattended-upgrades 未安裝" fi if systemctl is-active --quiet unattended-upgrades; then ok "unattended-upgrades 服務正在執行" else warn "unattended-upgrades 服務未執行" fi AUTO_CFG="/etc/apt/apt.conf.d/20auto-upgrades" if [[ -f "$AUTO_CFG" ]] && grep -q 'Unattended-Upgrade "1"' "$AUTO_CFG"; then ok "自動升級已啟用(20auto-upgrades)" else fail "自動升級未設定(缺少 20auto-upgrades 或設定值為 0)" fi # ============================================================ # 3. SSH 安全設定 # ============================================================ section "3. SSH 安全設定" SSHD="/etc/ssh/sshd_config" # SSH Port SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | awk '{print $2}') if [[ "$SSH_PORT" != "22" ]]; then ok "SSH 使用非標準 Port:$SSH_PORT" else warn "SSH 使用預設 Port 22(建議改為非標準 Port)" fi # Protocol PROTO=$(sshd -T 2>/dev/null | grep "^protocol " | awk '{print $2}' || echo "2") [[ "$PROTO" == "2" ]] && ok "SSH Protocol 2(安全)" || fail "SSH Protocol 不是 2" # Root 登入 ROOT_LOGIN=$(sshd -T 2>/dev/null | grep "^permitrootlogin " | awk '{print $2}') if [[ "$ROOT_LOGIN" == "no" ]]; then ok "PermitRootLogin = no" else fail "PermitRootLogin = $ROOT_LOGIN(建議設為 no)" fi # 密碼登入 PASSWD_AUTH=$(sshd -T 2>/dev/null | grep "^passwordauthentication " | awk '{print $2}') if [[ "$PASSWD_AUTH" == "no" ]]; then ok "PasswordAuthentication = no(僅允許 SSH Key)" else warn "PasswordAuthentication = yes(建議設好 Key 後改為 no)" fi # 空密碼 EMPTY_PASSWD=$(sshd -T 2>/dev/null | grep "^permitemptypasswords " | awk '{print $2}') [[ "$EMPTY_PASSWD" == "no" ]] && ok "PermitEmptyPasswords = no" \ || fail "PermitEmptyPasswords = $EMPTY_PASSWD(應設為 no)" # MaxAuthTries MAX_AUTH=$(sshd -T 2>/dev/null | grep "^maxauthtries " | awk '{print $2}') if [[ "$MAX_AUTH" -le 3 ]]; then ok "MaxAuthTries = $MAX_AUTH" else warn "MaxAuthTries = $MAX_AUTH(建議設為 3 以下)" fi # X11Forwarding X11=$(sshd -T 2>/dev/null | grep "^x11forwarding " | awk '{print $2}') [[ "$X11" == "no" ]] && ok "X11Forwarding = no" \ || warn "X11Forwarding = yes(建議關閉)" # ============================================================ # 4. UFW 防火牆 # ============================================================ section "4. UFW 防火牆" if command -v ufw &>/dev/null; then ok "ufw 已安裝" UFW_STATUS=$(ufw status | head -1) if echo "$UFW_STATUS" | grep -q "active"; then ok "UFW 狀態:active" DEFAULT_IN=$(ufw status verbose 2>/dev/null | grep "Default:" | grep -o "deny (incoming)\|reject (incoming)" || true) if [[ -n "$DEFAULT_IN" ]]; then ok "預設 incoming 規則:deny / reject" else warn "預設 incoming 規則不是 deny(建議設為 deny incoming)" fi else fail "UFW 未啟用(ufw enable 開啟)" fi else fail "ufw 未安裝" fi # ============================================================ # 5. Fail2ban # ============================================================ section "5. Fail2ban" if command -v fail2ban-client &>/dev/null; then ok "fail2ban 已安裝" else fail "fail2ban 未安裝" fi if systemctl is-active --quiet fail2ban; then ok "fail2ban 服務正在執行" SSH_JAIL=$(fail2ban-client status sshd 2>/dev/null | grep "Currently banned" | awk '{print $NF}' || echo "0") ok "sshd jail 目前封鎖 IP 數量:$SSH_JAIL" BANTIME=$(fail2ban-client get sshd bantime 2>/dev/null || echo "N/A") MAXRETRY=$(fail2ban-client get sshd maxretry 2>/dev/null || echo "N/A") ok "sshd bantime = ${BANTIME}s,maxretry = $MAXRETRY" else fail "fail2ban 服務未執行" fi # ============================================================ # 6. Kernel sysctl 強化 # ============================================================ section "6. Kernel sysctl 強化" check_sysctl() { local key=$1 expected=$2 label=$3 local actual actual=$(sysctl -n "$key" 2>/dev/null || echo "N/A") if [[ "$actual" == "$expected" ]]; then ok "$label ($key = $actual)" else fail "$label ($key = $actual,建議 = $expected)" fi } check_sysctl net.ipv4.conf.all.rp_filter 1 "IP Spoofing 防護" check_sysctl net.ipv4.icmp_echo_ignore_broadcasts 1 "ICMP Broadcast 忽略" check_sysctl net.ipv4.tcp_syncookies 1 "SYN Flood 防護" check_sysctl net.ipv4.ip_forward 0 "IP 轉發已停用" check_sysctl net.ipv4.conf.all.accept_redirects 0 "ICMP Redirect 忽略" check_sysctl net.ipv4.conf.all.send_redirects 0 "Send Redirect 停用" check_sysctl kernel.kptr_restrict 2 "Kernel Pointer 隱藏" check_sysctl kernel.dmesg_restrict 1 "dmesg 限制" check_sysctl fs.suid_dumpable 0 "SUID Core Dump 停用" # ============================================================ # 7. 檔案權限與系統 # ============================================================ section "7. 檔案權限與危險套件" # /etc/shadow SHADOW_PERM=$(stat -c "%a" /etc/shadow) [[ "$SHADOW_PERM" == "640" || "$SHADOW_PERM" == "600" ]] \ && ok "/etc/shadow 權限:$SHADOW_PERM" \ || fail "/etc/shadow 權限:$SHADOW_PERM(建議 600)" # /etc/passwd PASSWD_PERM=$(stat -c "%a" /etc/passwd) [[ "$PASSWD_PERM" == "644" ]] \ && ok "/etc/passwd 權限:$PASSWD_PERM" \ || warn "/etc/passwd 權限:$PASSWD_PERM(建議 644)" # root 帳號鎖定 ROOT_STATUS=$(passwd -S root 2>/dev/null | awk '{print $2}') if [[ "$ROOT_STATUS" == "L" || "$ROOT_STATUS" == "LK" ]]; then ok "root 帳號已鎖定($ROOT_STATUS)" else warn "root 帳號未鎖定(狀態:$ROOT_STATUS)" fi # 危險套件 DANGEROUS=(telnet xinetd rsh-server rsh-client nis tftpd) for pkg in "${DANGEROUS[@]}"; do if dpkg -l "$pkg" &>/dev/null 2>&1 | grep -q "^ii"; then fail "危險套件仍已安裝:$pkg(建議移除)" else ok "危險套件未安裝:$pkg" fi done # ============================================================ # 最終報告 # ============================================================ TOTAL=$((PASS + WARN + FAIL)) echo "" echo -e "${CYAN}${BOLD}══════════════════════════════════════════════${NC}" echo -e "${BOLD} 檢查結果摘要(共 $TOTAL 項)${NC}" echo -e "${CYAN}${BOLD}══════════════════════════════════════════════${NC}" echo -e " ${GREEN}[✓ PASS]${NC} $PASS 項" echo -e " ${YELLOW}[⚠ WARN]${NC} $WARN 項" echo -e " ${RED}[✗ FAIL]${NC} $FAIL 項" echo "" if [[ $FAIL -eq 0 && $WARN -eq 0 ]]; then echo -e " ${GREEN}${BOLD}🎉 全部通過!伺服器安全性良好${NC}" elif [[ $FAIL -eq 0 ]]; then echo -e " ${YELLOW}${BOLD}⚠️ 有 $WARN 個警告,建議處理${NC}" else echo -e " ${RED}${BOLD}❌ 有 $FAIL 個問題需要修復${NC}" fi echo ""