fix: 修正 server-security-check 的 SSH 误报并标注高风险
- PermitRootLogin: 接受 without-password 别名(OpenSSH -T 会输出该旧别名), 避免把仅允许密钥的 root 登录误报为“无法确定安全状态” - 检查未通过时列出配置文件中的相关设置行:sshd 只采用先读到的一条, 主配置 Include 之前的 drop-in 以及 Match 块内的设置都会造成“改过但不生效” - sshd -T 读取失败时同样给出配置文件设置,便于定位 - 参数匹配改为大小写不敏感 - 高风险整行红色、警告黄色,汇总同步着色;非 TTY 或 NO_COLOR 时禁用
This commit is contained in:
@@ -58,6 +58,17 @@ if [[ $NO_SUDO -eq 0 && $EUID -ne 0 ]]; then
|
||||
exec sudo -- "$0"
|
||||
fi
|
||||
|
||||
# 仅在输出到终端时着色;重定向到文件或设置 NO_COLOR 时保持纯文本
|
||||
if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
|
||||
COLOR_WARN=$'\033[1;33m'
|
||||
COLOR_HIGH=$'\033[0;31m'
|
||||
COLOR_RESET=$'\033[0m'
|
||||
else
|
||||
COLOR_WARN=""
|
||||
COLOR_HIGH=""
|
||||
COLOR_RESET=""
|
||||
fi
|
||||
|
||||
PASS_COUNT=0
|
||||
WARN_COUNT=0
|
||||
HIGH_COUNT=0
|
||||
@@ -70,12 +81,12 @@ pass() {
|
||||
|
||||
warn() {
|
||||
WARN_COUNT=$((WARN_COUNT + 1))
|
||||
printf '[警告] %s\n' "$*"
|
||||
printf '%s[警告] %s%s\n' "$COLOR_WARN" "$*" "$COLOR_RESET"
|
||||
}
|
||||
|
||||
high() {
|
||||
HIGH_COUNT=$((HIGH_COUNT + 1))
|
||||
printf '[高风险] %s\n' "$*"
|
||||
printf '%s[高风险] %s%s\n' "$COLOR_HIGH" "$*" "$COLOR_RESET"
|
||||
}
|
||||
|
||||
info() {
|
||||
@@ -103,11 +114,42 @@ find_sshd() {
|
||||
}
|
||||
|
||||
check_sshd_setting() {
|
||||
local setting="$1" value
|
||||
value=$(printf '%s\n' "$SSHD_EFFECTIVE_CONFIG" | awk -v key="$setting" '$1 == key { print $2; exit }')
|
||||
local setting value
|
||||
setting=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
|
||||
value=$(printf '%s\n' "$SSHD_EFFECTIVE_CONFIG" | awk -v key="$setting" 'tolower($1) == key { print tolower($2); exit }')
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
SSHD_CONFIG_FILE=/etc/ssh/sshd_config
|
||||
SSHD_CONFIG_DIR=/etc/ssh/sshd_config.d
|
||||
|
||||
# 列出配置文件中显式设置某个参数的行,用于解释“明明改过了为何还不生效”。
|
||||
# sshd 对同一参数只采用先读到的一条:Include /etc/ssh/sshd_config.d/*.conf
|
||||
# 通常位于主配置前部,因此 drop-in 会覆盖主配置后部的同名设置;Match 块内的设置
|
||||
# 也不作用于全局配置。
|
||||
check_ssh_setting_sources() {
|
||||
local key file matches files
|
||||
for key in "$@"; do
|
||||
files=()
|
||||
[[ -r "$SSHD_CONFIG_FILE" ]] && files+=("$SSHD_CONFIG_FILE")
|
||||
if [[ -d "$SSHD_CONFIG_DIR" ]]; then
|
||||
for file in "$SSHD_CONFIG_DIR"/*.conf; do
|
||||
[[ -r "$file" ]] && files+=("$file")
|
||||
done
|
||||
fi
|
||||
((${#files[@]} > 0)) || continue
|
||||
|
||||
matches=$(grep -inHE "^[[:space:]]*${key}([[:space:]]|=)" "${files[@]}" 2>/dev/null || true)
|
||||
if [[ -z "$matches" ]]; then
|
||||
info "${key} 未在配置文件中显式设置,生效值可能来自 sshd 默认值"
|
||||
continue
|
||||
fi
|
||||
|
||||
info "${key} 的配置行(sshd 只采用先读到的一条;Match 块内的设置不作用于全局):"
|
||||
printf '%s\n' "$matches" | sed 's/^/ /'
|
||||
done
|
||||
}
|
||||
|
||||
check_ssh() {
|
||||
local sshd_bin root_login password_login ssh_ports
|
||||
|
||||
@@ -121,6 +163,7 @@ check_ssh() {
|
||||
SSHD_EFFECTIVE_CONFIG=$("$sshd_bin" -T 2>/dev/null)
|
||||
if [[ -z "$SSHD_EFFECTIVE_CONFIG" ]]; then
|
||||
warn "无法读取 sshd 生效配置;请使用 sudo 重试或检查 sshd 配置"
|
||||
check_ssh_setting_sources permitrootlogin passwordauthentication
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -132,14 +175,17 @@ check_ssh() {
|
||||
no|forced-commands-only)
|
||||
pass "SSH 已禁止 root 交互式登录(PermitRootLogin: $root_login)"
|
||||
;;
|
||||
prohibit-password)
|
||||
pass "SSH root 登录仅允许密钥认证(PermitRootLogin: prohibit-password)"
|
||||
prohibit-password|without-password)
|
||||
# without-password 是 prohibit-password 的旧别名,含义相同
|
||||
pass "SSH root 登录仅允许密钥认证(PermitRootLogin: $root_login)"
|
||||
;;
|
||||
yes)
|
||||
high "SSH 允许 root 直接登录;建议设为 PermitRootLogin no"
|
||||
check_ssh_setting_sources permitrootlogin
|
||||
;;
|
||||
*)
|
||||
warn "无法确定 PermitRootLogin 的安全状态(当前: ${root_login:-未设置})"
|
||||
check_ssh_setting_sources permitrootlogin
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -149,9 +195,11 @@ check_ssh() {
|
||||
;;
|
||||
yes)
|
||||
warn "SSH 允许密码登录;公网服务器建议改用密钥认证"
|
||||
check_ssh_setting_sources passwordauthentication
|
||||
;;
|
||||
*)
|
||||
warn "无法确定 PasswordAuthentication 的安全状态(当前: ${password_login:-未设置})"
|
||||
check_ssh_setting_sources passwordauthentication
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -381,11 +429,16 @@ check_privileged_accounts
|
||||
check_recent_logins
|
||||
|
||||
printf '\n== 汇总 ==\n'
|
||||
printf '通过: %d 警告: %d 高风险: %d 信息: %d\n' "$PASS_COUNT" "$WARN_COUNT" "$HIGH_COUNT" "$INFO_COUNT"
|
||||
if ((HIGH_COUNT > 0)); then
|
||||
echo "请优先处理“高风险”项。"
|
||||
SUMMARY_COLOR="$COLOR_HIGH"
|
||||
SUMMARY_ADVICE='请优先处理“高风险”项。'
|
||||
elif ((WARN_COUNT > 0)); then
|
||||
echo "建议逐项确认“警告”项是否符合服务器的实际部署方式。"
|
||||
SUMMARY_COLOR="$COLOR_WARN"
|
||||
SUMMARY_ADVICE='建议逐项确认“警告”项是否符合服务器的实际部署方式。'
|
||||
else
|
||||
echo "未发现需要立即处理的项。"
|
||||
SUMMARY_COLOR=""
|
||||
SUMMARY_ADVICE='未发现需要立即处理的项。'
|
||||
fi
|
||||
printf '%s通过: %d 警告: %d 高风险: %d 信息: %d%s\n' \
|
||||
"$SUMMARY_COLOR" "$PASS_COUNT" "$WARN_COUNT" "$HIGH_COUNT" "$INFO_COUNT" "$COLOR_RESET"
|
||||
printf '%s%s%s\n' "$SUMMARY_COLOR" "$SUMMARY_ADVICE" "$COLOR_RESET"
|
||||
|
||||
Reference in New Issue
Block a user