前言

把三年里写过的安全内容(SSH 加固、用户管理、防火墙……)整理成一份上线前必过、每季复查的清单。每项都给出检查命令和整改动作——直接对着服务器打勾即可。

一、账号与认证(10 项)

# 1. 无空密码账号(期望: 无输出)
awk -F: '($2==""){print $1}' /etc/shadow

# 2. UID=0 只有 root
awk -F: '$3==0 {print $1}' /etc/passwd

# 3. 禁止 root 直接 SSH(PermitRootLogin no)
sshd -T | grep permitrootlogin

# 4. SSH 禁用密码(密钥登录)
sshd -T | grep passwordauthentication

# 5. 密码策略: 90天/复杂度
grep PASS_MAX_DAYS /etc/login.defs
cat /etc/security/pwquality.conf | grep -E "minlen|minclass"

# 6. 登录失败锁定(fail2ban 运行中)
systemctl is-active fail2ban

# 7. 无遗留僵尸账号(90天未登录清单)
lastlog -b 90 | awk 'NR>1 && $2!="" {print $1}' | head

# 8. sudo 白名单化(NOPASSWD: ALL 全量盘点)
grep -rn "NOPASSWD" /etc/sudoers /etc/sudoers.d/ 2>/dev/null

# 9. 离职流程有记录(制度项: 删号/锁号/清crontab/清sudoers)
# 10. 默认 umask 022(不为 000)
grep -i umask /etc/profile /etc/bashrc | head -3

二、攻击面收敛(8 项)

# 1. 监听端口清单化(每个端口都能说清是谁)
ss -tlnpu

# 2. 无 0.0.0.0 的调试/管理端口(mysql/redis es 等)
ss -tln | awk '$4 ~ /0.0.0.0|:::/ {print $4}'

# 3. Redis 不裸奔: bind 内网 + requirepass + 禁高危命令
redis-cli CONFIG GET bind
redis-cli CONFIG GET requirepass

# 4. MySQL 不开放公网: bind-address 内网/localhost
mysql -e "SHOW VARIABLES LIKE 'bind_address';"

# 5. 防火墙默认拒绝(firewalld zone 明确/安全组最小放行)
firewall-cmd --list-all

# 6. 关闭不需要的服务(bluetooth/cups/avahi...)
systemctl list-unit-files --state=enabled | wc -l

# 7. 内网服务不出公网(安全组层面确认)
# 8. 不用的老服务下线(nfs/rsh/telnet 检查)
rpm -qa | grep -E "^(rsh|telnet|ypbind)" 

三、系统与内核(7 项)

# 1. 及时打安全补丁
dnf updateinfo list sec --available | head
dnf needs-restarting -r

# 2. 内核安全参数(呼应 sysctl 篇)
sysctl net.ipv4.conf.all.rp_filter net.ipv4.tcp_syncookies

# 3. 时间同步(chrony, 证书/审计的前提)
timedatectl | grep -E "synchronized|NTP"

# 4. 磁盘水位与日志清理策略(logrotate/journald 限额)
journalctl --disk-usage

# 5. 内核不随意外加载模块(对高危模块黑名单)
# 6. SELinux 状态有明确决策(启用或"评估后豁免")
getenforce
# 7. reboot 后内核参数/服务自启均持久化(sysctl --system 无报错)

四、数据与凭据(6 项)

# 1. 私钥权限 600 / .ssh 目录 700
find /root /home -name "id_*" -exec ls -l {} \;

# 2. 代码/配置仓库无明文密码
grep -rnE "(password|secret|token)\s*=" /opt/app/config/ | head

# 3. 敏感文件权限收紧(ssh config、备份脚本里的凭据)
stat -c "%a %n" /etc/shadow

# 4. 备份策略验证(恢复演练过!)
ls -lh /backup/$(hostname)/ | tail -3

# 5. 数据库账号最小权限(无 % 通配 root)
mysql -e "SELECT user,host FROM mysql.user;"

# 6. 云上 AK/SK 不落盘(用角色/实例元数据)
find / -maxdepth 4 -name "credentials" -path "*aws*" 2>/dev/null

五、可观测与响应(5 项)

# 1. 登录审计有人看
grep "Accepted" /var/log/secure | tail -5

# 2. 告警通道畅通(钉钉/邮件测试消息)
# 3. 高危操作留痕(sudo 日志纳入采集)
journalctl -t sudo | tail -3

# 4. 指标监控覆盖(CPU/内存/磁盘/进程/端口)
curl -s http://localhost:9100/metrics | head -1

# 5. 应急预案文档化(断网/隔离/取证流程谁会做?)

六、容器与 K8s 专项(6 项)

# 1. 无 privileged 容器
docker ps --format '{{.Names}}' | while read c; do
  docker inspect -f '{{.HostConfig.Privileged}}' "$c"; done

# 2. 容器非 root 运行
docker inspect -f '{{.Config.User}}' $(docker ps -q) | sort -u

# 3. 不挂载 docker.sock
docker inspect $(docker ps -q) | grep -c docker.sock

# 4. 镜像来源受控(私有仓库 + Trivy 扫描)
trivy image --severity HIGH,CRITICAL --exit-code 0 myapp:latest | tail -3

# 5. K8s: RBAC 最小权限(can-i '*' '*' 应为 no)
kubectl auth can-i '*' '*'
kubectl get clusterrolebindings -o name | wc -l

# 6. Pod 安全基线(不共享 hostNetwork/hostPath 滥用)
kubectl get pods -A -o wide | grep -vE "calico|flannel" | wc -l

七、一键自查脚本(节选)

#!/usr/bin/env bash
# security-baseline.sh —— 输出: [FAIL]/[PASS] + 整改建议
check() {  # $1=描述 $2=检查命令 $3=期望输出包含
    result=$(eval "$2" 2>/dev/null)
    if echo "$result" | grep -q "$3"; then
        printf "[PASS] %s\n" "$1"
    else
        printf "[FAIL] %s  →  %s\n" "$1" "$4"
    fi
}

check "root SSH 已禁用"    "sshd -T | grep -i permitrootlogin" "no"      "改 /etc/ssh/sshd_config"
check "SSH 密码已禁用"     "sshd -T | grep -i passwordauth"    "no"      "改 sshd_config 后 -t + reload"
check "fail2ban 运行中"    "systemctl is-active fail2ban"      "active"  "dnf install fail2ban 并启动"
check "无空密码账号"        "awk -F: '(\$2==\"\")' /etc/shadow | wc -l" "0" "passwd 锁定相关账号"
check "私钥权限正确"        "stat -c %a /root/.ssh/id_* | grep -vc 600" "0" "chmod 600"
check "时间已同步"          "timedatectl | grep -c synchronized"  "1"   "启用 chronyd"
# 接入 cron 每周一 9 点跑, FAIL 项发告警
0 9 * * 1 root /opt/scripts/security-baseline.sh | grep FAIL | mail -s "安全基线告警" ops@corp

八、加固的元原则

  1. 分层防御:单点失守不等于全线失守(账号层+网络层+应用层+数据层)
  2. 最小权限:默认全关,按需打开,定期回收
  3. 可审计:没有日志的加固等于没加固
  4. 自动化:人盯清单必遗漏,脚本+告警才是正道
  5. 演练:预案没演练过 = 没有预案

小结

这份清单不是"做完就安全"的许愿池,而是持续运营的仪表盘:季度复查、新机器过基线、FAIL 项进工单。安全没有毕业,只有坚持。


本文是「安全」系列第 2 篇。