☰
Linux服务器实战安全加固:账号、引导、登录、端口与弱口令闭环控制
2026/10/15 23:35:31 网站建设 项目流程

1. 项目概述:这不是教科书里的安全课,是我在三台生产服务器上反复摔打出来的实战清单

“Linux——系统安全及应用——账号安全控制——系统引导和登录控制——弱口令检测及端口扫描——都是干货——你懂得——”这个标题看着像随手敲出来的碎词堆砌,但恰恰是它最真实。我见过太多人把Linux安全当成PPT里的几个名词:SELinux、防火墙、密码策略……讲得头头是道,一上真机就卡在root密码忘了怎么进单用户模式,或者扫出22端口开着,却不知道sshd_config里哪一行才是真正决定谁有资格敲回车的命门。这根本不是理论题,是生存题。

我用这个标题串起的整套动作,在某高校数据中心、某物联网设备厂商的边缘网关集群、以及一个小型SaaS服务后台三类完全不同的环境里跑过至少17轮。不是为了“加固”,而是因为某次凌晨三点告警:一台对外提供API的CentOS 7服务器被横向渗透,攻击者没动代码,只改了/etc/passwd里一个UID为0的隐藏账户,然后用这个账户反向SSH连回内网数据库服务器——整个过程没触发任何IDS规则,因为所有操作都走的是合法shell通道。事后复盘,问题不在漏洞,而在基础控制链的断裂:引导阶段没设GRUB密码,登录阶段没限制tty会话,密码策略形同虚设,而端口扫描工具扫出的开放端口列表,居然没人对照着服务配置逐条验证权限收敛程度。

所以这篇内容不讲“什么是PAM”,不画“安全模型金字塔”,只告诉你:当你的服务器已经在线运行,你只有30分钟窗口期,该按什么顺序、用哪几条命令、改哪几个文件、验证哪几个关键点,才能把最可能被利用的入口堵死。核心关键词——账号安全控制、系统引导控制、登录控制、弱口令检测、端口扫描——每一个都不是孤立模块,而是环环相扣的锁链。比如,你设了强密码策略,但如果GRUB没有密码,攻击者重启进单用户模式就能直接passwd -d root清空密码;你禁用了root远程登录,但如果/etc/ssh/sshd_config里PermitEmptyPasswords yes还开着,一个空密码账户就能绕过所有限制;你扫出了8080端口开放,但如果不检查netstat -tulnp | grep :8080背后是哪个进程、该进程以哪个用户身份运行、该用户是否拥有不必要的sudo权限,那扫描结果就是一张废纸。

适合谁看?运维工程师、DevOps人员、刚接手线上服务的开发同学,甚至是有自建NAS或家用服务器的爱好者。不需要你背熟所有参数,但必须清楚每一步操作在系统底层触发了什么机制——比如usermod -L锁账户,本质是往shadow密码字段前加!,而usermod -s /sbin/nologin则是改shell路径,两者防爆破效果差一个数量级;再比如systemctl mask getty@tty1.service和systemctl disable getty@tty1.service,前者是物理性删除符号链接,后者只是取消开机启动,重启后tty1依然能被暴力尝试。这些细节,决定了你是在修墙,还是在墙上画砖。

2. 账号安全控制:从创建到锁定,每一步都在对抗自动化脚本

2.1 创建账户时的“第一道防线”:别让默认配置成为后门

很多人创建新用户只敲useradd username,这埋下了第一个雷。默认情况下,useradd会做几件危险的事:给用户分配一个可登录的shell(通常是/bin/bash),不设置密码过期时间,不限制密码重用次数,甚至可能分配一个UID在1000以下的“系统账户范围”(某些发行版默认行为)。攻击者拿到一个低权限账户,第一件事就是查/etc/passwd,找UID<1000且shell不是/sbin/nologin的账户,这类账户往往被遗忘、权限松散,是提权跳板。

我现在的标准操作是:

useradd -m -s /bin/bash -c "WebApp Service Account" -U -K UID_MIN=10000 -K UID_MAX=60000 webapp

拆解一下每个参数的实战意义:

  • -m:强制创建家目录(避免后续手动mkdir权限混乱);
  • -s /bin/bash:明确指定shell,不依赖发行版默认(有些精简版默认是/bin/sh,功能受限);
  • -c:添加注释,方便后期审计时一眼识别账户用途;
  • -U:自动创建同名用户组(避免usermod -aG漏加);
  • -K UID_MIN=10000 -K UID_MAX=60000:这是关键!覆盖系统默认的UID范围(通常0-999为系统账户,1000+为普通用户),把业务账户UID严格限定在10000-60000之间。为什么?因为很多提权EXP会遍历/etc/passwd中UID<1000的账户,试图利用其遗留的sudoers配置或SUID二进制文件。把业务账户UID拉高,等于在攻击者扫描列表里主动隐身。

提示:-K参数需要/etc/login.defs中USERGROUPS_ENAB yes开启,否则-U无效。执行前先grep USERGROUPS_ENAB /etc/login.defs确认。

2.2 密码策略:不是越复杂越好,而是要让爆破成本高于收益

“密码必须包含大小写字母、数字、特殊字符,长度≥12位,90天强制更换”——这套策略在桌面环境合理,但在服务器上可能是灾难。我见过某金融客户因密码策略太严,运维人员为记住密码,在/root/.bash_history里明文记录passwd命令和新密码,反而制造更大风险。真正的密码安全,核心是增加攻击者的时间成本和失败率,而非单纯提高记忆难度。

我的实践方案分三层:
第一层:PAM模块硬约束(防弱口令)
编辑/etc/pam.d/common-password(Debian/Ubuntu)或/etc/pam.d/system-auth(RHEL/CentOS),在password [success=1 default=ignore] pam_unix.so行上方插入:

password requisite pam_pwquality.so retry=3 minlen=10 difok=3 maxrepeat=2 reject_username enforce_for_root

参数详解:

  • retry=3:输错3次密码后退出,防止脚本连续试探;
  • minlen=10:最低10位,比12位更务实(兼顾安全与可用性);
  • difok=3:新密码必须与旧密码至少3个字符不同,防“Password1→Password2”式简单递增;
  • maxrepeat=2:禁止同一字符连续出现3次(如aaa),大幅降低字典生成效率;
  • reject_username:禁止密码包含用户名,堵死最常见弱口令;
  • enforce_for_root:root账户也强制执行,避免最高权限账户成短板。

第二层:密码生命周期管理(防长期未更新)
用chage命令精准控制:

chage -M 90 -m 7 -W 7 -I 30 webapp
  • -M 90:密码最长使用90天;
  • -m 7:密码最短使用7天(防刚改完立刻再改);
  • -W 7:到期前7天开始每天登录时提醒;
  • -I 30:过期后30天内未改密码,账户自动锁定(注意:不是禁用,是锁定,仍可被sudo启用)。

注意:chage -l username可查看当前策略,chage -d 0 username可强制用户下次登录时修改密码(适用于新员工入职场景)。

第三层:SSH密钥替代密码(根除密码传输风险)
这是最有效的弱口令防御。在客户端生成密钥对:

ssh-keygen -t ed25519 -C "webapp-deploy@company.com" -f ~/.ssh/webapp_deploy

ed25519算法比RSA更安全、更快,且密钥更短。将公钥部署到服务器:

ssh-copy-id -i ~/.ssh/webapp_deploy.pub -p 2222 webapp@server_ip

关键配置/etc/ssh/sshd_config:

PubkeyAuthentication yes PasswordAuthentication no # 彻底关闭密码登录 PermitEmptyPasswords no

重启SSH:systemctl restart sshd。此时webapp账户只能用密钥登录,即使密码是123456也毫无意义。

2.3 账户锁定与禁用:理解-L、-s、-e的本质区别

很多教程说“用usermod -L username锁账户”,但没说清-L只是在/etc/shadow密码字段前加!,而passwd -l username效果相同。攻击者只要知道/etc/shadow路径,用sed -i 's/^username:!.*$/username:$6$...' /etc/shadow就能暴力替换回一个有效哈希——前提是他们已有root权限。所以-L本质是防未授权的正常登录,不是防root下的恶意篡改。

真正需要区分的三种状态:

操作命令/etc/passwd变化/etc/shadow变化实际效果适用场景
usermod -s /sbin/nologin usernameshell字段改为/sbin/nologin无变化用户可登录但立即被nologin程序退出,无法获得shell服务账户(如nginx、mysql),需系统调用但不应交互登录
usermod -L username无变化密码字段前加!(如$6$...→!$6$...)密码验证永远失败,但账户仍可被其他认证方式(如SSH密钥)使用临时停用人工账户,保留密钥登录权限
usermod -e "2025-01-01" username无变化expire字段设为日期到期后任何认证方式均失败外包人员、实习生等有明确服务期限的账户

我处理服务账户的黄金组合是:

usermod -s /sbin/nologin -L -e "2099-12-31" nginx

既禁用交互shell,又锁定密码(防意外启用),还设超长有效期(避免到期自动失效影响服务)。

实操心得:usermod -s /bin/false和/sbin/nologin效果类似,但/sbin/nologin会向用户显示友好提示(如“This account is currently not available”),而/bin/false静默退出,不利于排查。优先选/sbin/nologin。

3. 系统引导与登录控制:守住重启后的第一道门

3.1 GRUB引导菜单加密:让物理接触不再等于root权限

服务器被拖走硬盘?概率低。但机房管理员误操作、外包人员调试时重启进单用户模式?非常现实。GRUB密码是防止此类事件的最后屏障。重点不是“设密码”,而是确保密码生效且无法被绕过。

RHEL/CentOS 7+ 和 Debian 11+ 的GRUB2配置逻辑不同,我以CentOS 7为例:
第一步:生成加密密码
不要用明文!用grub2-mkpasswd-pbkdf2生成PBKDF2哈希:

grub2-mkpasswd-pbkdf2 # 输入密码后得到类似:pbkdf2.sha512.10000.1234567890ABCDEF...

第二步:编辑GRUB配置
修改/etc/grub.d/40_custom,在末尾添加:

set superusers="admin" password_pbkdf2 admin pbkdf2.sha512.10000.1234567890ABCDEF...

注意:admin是超级用户名称,可自定义;password_pbkdf2后跟用户名和哈希值。

第三步:重建GRUB配置

grub2-mkconfig -o /boot/grub2/grub.cfg

关键验证:重启服务器,在GRUB菜单界面按e编辑启动项,如果看到Press 'c' for a command-line但无法输入命令,或输入c后提示Authentication required,说明成功。若仍能直接编辑,检查/boot/grub2/grub.cfg中是否包含set superusers和password_pbkdf2行,且/etc/default/grub中GRUB_DISABLE_RECOVERY="true"已设置(禁用恢复模式菜单)。

3.2 单用户模式防护:从源头掐断rd.break和init=/bin/bash

GRUB密码防的是菜单编辑,但攻击者仍可能通过内核参数注入绕过。最常见的是在GRUB启动时按e,找到linux16或linux行,末尾添加rd.break(RHEL)或init=/bin/bash(Debian),从而在init阶段获得root shell。

防护方案分两步:
1. 锁定内核参数(GRUB级)
在/etc/grub.d/40_custom中,为每个启动项添加--unrestricted参数的反向限制:

menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --unrestricted { ... }

--unrestricted表示此菜单项无需认证即可启动,但必须配合GRUB超级用户设置。原理是:只有认证用户才能看到并选择带--unrestricted的菜单项;未认证用户只能看到默认菜单,且无法编辑。因此,所有非默认菜单项(如旧内核、救援模式)都应不加--unrestricted,使其默认受保护。

2. 内核级加固(终极防护)
编辑/etc/default/grub,在GRUB_CMDLINE_LINUX中添加:

GRUB_CMDLINE_LINUX="... audit=1 lockdown=confidentiality"
  • audit=1:启用内核审计,记录所有特权操作;
  • lockdown=confidentiality:启用内核锁定模式,禁止在运行时修改内核内存、加载未签名模块、修改内核参数(包括rd.break)。此模式下,即使进入单用户模式,也无法执行mount -o remount,rw /sysroot等关键操作。

更新配置:grub2-mkconfig -o /boot/grub2/grub.cfg,重启生效。

注意:lockdown=confidentiality可能影响某些需要动态加载驱动的硬件(如某些GPU),生产环境启用前务必在测试机验证。

3.3 登录控制:TTY、SSH、图形界面的三重过滤网

登录控制不是“开/关”开关,而是分层过滤。我按访问路径梳理:

TTY终端(Ctrl+Alt+F1~F6)
这是最原始的登录入口,常被忽略。默认所有6个TTY都启用,攻击者可直接在机房键盘上尝试。关闭非必要TTY:

# 查看当前启用的TTY loginctl list-seats # 禁用tty2-tty6,只留tty1 systemctl mask getty@tty2.service getty@tty3.service getty@tty4.service getty@tty5.service getty@tty6.service

mask比disable更彻底,它创建指向/dev/null的符号链接,无法被systemctl unmask以外的方式启用。

SSH远程登录
/etc/ssh/sshd_config是主战场,我的最小化安全配置:

Port 2222 # 非标准端口,减少自动化扫描 Protocol 2 # 禁用SSHv1 PermitRootLogin no # 绝对禁止root远程登录 AllowUsers webapp deploy # 只允许指定用户(用空格分隔) AllowGroups ssh-users # 或按组控制(需提前创建groupadd ssh-users) MaxAuthTries 3 # 认证失败3次后断开连接 ClientAliveInterval 300 # 5分钟无活动自动断开,防会话劫持 UsePAM yes # 启用PAM,使密码策略生效

重启生效:systemctl restart sshd。

图形界面(GNOME/KDE)
若服务器装了桌面环境(不推荐,但存在),需额外控制:

  • 编辑/etc/gdm3/custom.conf(GDM3)或/etc/lightdm/lightdm.conf(LightDM),设置:
    [daemon] TimedLoginEnable=false AutomaticLoginEnable=false
  • 禁用图形登录服务:systemctl set-default multi-user.target。

实操心得:AllowUsers和AllowGroups是白名单机制,比DenyUsers更安全。曾有个案例,运维误将DenyUsers root写成DenyUsers roo,导致root账户畅通无阻。

4. 弱口令检测与端口扫描:从发现到验证的闭环工作流

4.1 弱口令检测:不用商业工具,用Linux原生命令组合拳

商业弱口令扫描器(如John the Ripper)强大,但部署复杂、易被误报。我用三步原生命令法,10分钟内完成全量检测:

第一步:导出所有账户的密码哈希(需root)

awk -F: '($2 != "*" && $2 != "!" && $2 != "") {print $1 ":" $2}' /etc/shadow > /tmp/hashes.txt

解释:$2是密码字段,*和!表示账户被锁定或无密码,空字符串表示空密码(极度危险!),只导出有效哈希。

第二步:用hashcat离线爆破(本地机执行,不上传服务器)
在本地Linux机器安装hashcat:

# 下载rockyou.txt字典(需自行获取) wget https://github.com/brannondorsey/naive-hashcat/releases/download/data/rockyou.txt # 爆破(-m 1800表示sha512crypt,对应大多数现代Linux) hashcat -m 1800 -a 0 /tmp/hashes.txt rockyou.txt --force

--force忽略NVIDIA驱动警告。爆破结果存于hashcat.potfile,用hashcat --show /tmp/hashes.txt查看明文密码。

第三步:定位高危账户并修复

# 找出空密码账户(紧急!) awk -F: '($2 == "" || $2 == "!") {print $1}' /etc/shadow # 找出密码过期账户(可能被遗忘) chage -l $(cut -d: -f1 /etc/shadow | head -10) 2>/dev/null | grep -E "(Password expires|Account expires)" | grep "never"

注意:/etc/shadow权限必须是600,否则awk读取会失败。检查:ls -l /etc/shadow。

4.2 端口扫描:不只是nmap -sV,更要关联服务与权限

nmap -sS -p- server_ip能扫出开放端口,但真正的风险在于端口背后的服务配置。我的扫描-分析-加固闭环:

1. 全端口扫描(基础发现)

nmap -sS -T4 -p- --min-rate 1000 -oA full_scan 192.168.1.100
  • -sS:TCP SYN扫描,隐蔽;
  • -T4:提速;
  • --min-rate 1000:每秒至少1000个包,加快速度;
  • -oA:输出为多种格式(nmap, gnmap, xml)。

2. 服务深度分析(关键步骤)
对扫描出的每个开放端口,执行:

# 查看监听进程及用户 ss -tulnp | grep ':22' # 输出示例:tcp LISTEN 0 128 *:22 *:* users:(("sshd",pid=1234,fd=3),("sshd",pid=1235,fd=3)) uid:0 # 检查该进程的配置文件 ls -l /etc/ssh/sshd_config # 检查该用户是否有sudo权限 getent group sudo | grep webapp

3. 权限收敛验证(最终加固)
根据分析结果,制定收敛策略:

扫描结果风险分析加固操作
22/tcp open ssh+uid:0root用户运行sshd,权限过大修改/etc/ssh/sshd_config:UsePrivilegeSeparation sandbox(启用沙箱)+User nobody(降权运行)
8080/tcp open http+uid:1000普通用户运行Web服务,可能提权用systemd服务文件指定User=www-data,并设置NoNewPrivileges=yes
3306/tcp open mysql+0.0.0.0:3306MySQL监听所有IP,暴露内网修改/etc/my.cnf:bind-address = 127.0.0.1,仅本地访问

实操心得:ss -tulnp比netstat更快更准,且-p参数需root权限,普通用户看不到进程名。若ss不可用,lsof -i -P -n -sTCP:LISTEN是备选。

4.3 自动化巡检脚本:把重复劳动变成定时任务

把上述检测固化为脚本,加入crontab每日执行:

#!/bin/bash # /usr/local/bin/security-audit.sh DATE=$(date +%Y%m%d) LOG="/var/log/security-audit-$DATE.log" echo "=== Security Audit Start $(date) ===" >> $LOG # 检查空密码 echo ">>> Empty password accounts:" >> $LOG awk -F: '($2 == "" || $2 == "!") {print $1}' /etc/shadow >> $LOG # 检查SSH配置 echo ">>> SSH config check:" >> $LOG grep -E "^(PermitRootLogin|PasswordAuthentication|Port)" /etc/ssh/sshd_config >> $LOG # 检查开放端口及进程 echo ">>> Listening ports and processes:" >> $LOG ss -tulnp 2>/dev/null | grep -E ":(22|2222|80|443|3306|5432)" >> $LOG # 发送报告(需配置mailx) if [ -s "$LOG" ]; then mailx -s "Security Audit Report $DATE" admin@company.com < $LOG fi

加入crontab:

# 每日凌晨2点执行 0 2 * * * /usr/local/bin/security-audit.sh

5. 常见问题与排查技巧实录:那些文档里不会写的坑

5.1 GRUB密码设了但不起作用?检查这四个致命点

问题现象:重启后GRUB菜单仍可随意编辑,无密码提示。
排查顺序:

  1. 确认GRUB版本与配置文件位置

    • RHEL/CentOS 7+:配置在/etc/grub.d/40_custom,重建用grub2-mkconfig;
    • Debian 11+:配置在/etc/grub.d/40_custom,但重建命令是update-grub;
    • 若混淆,grub2-mkconfig在Debian会报错,update-grub在RHEL不存在。
  2. 检查/boot/grub2/grub.cfg是否包含密码行

    grep -A5 -B5 "password_pbkdf2" /boot/grub2/grub.cfg

    若无输出,说明grub2-mkconfig未正确执行,或40_custom语法错误(如缺少})。

  3. 验证/etc/default/grub中GRUB_DISABLE_RECOVERY="true"
    若为false,恢复模式菜单会绕过密码保护。修改后必须grub2-mkconfig。

  4. BIOS/UEFI启动模式不匹配
    Legacy BIOS下配置GRUB2,但服务器实际是UEFI启动,配置文件路径应为/boot/efi/EFI/redhat/grub.cfg。检查:ls /sys/firmware/efi,存在则为UEFI。

我踩过的坑:某次在UEFI服务器上按Legacy流程配置,grub2-mkconfig生成的配置在/boot/grub2/,但UEFI实际读取/boot/efi/EFI/centos/grub.cfg,导致密码失效。解决方案:grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg。

5.2usermod -L锁账户后,为什么SSH密钥还能登录?

这是经典误解。usermod -L只影响密码认证,即/etc/shadow中的密码哈希验证。SSH密钥认证走的是/home/username/.ssh/authorized_keys文件校验,与shadow无关。所以:

  • usermod -L username→ 密码登录失败,密钥登录成功;
  • usermod -s /sbin/nologin username→ 密码和密钥登录都会失败(因为shell被禁用);
  • usermod -e "1970-01-01" username→ 所有认证方式均失败(账户过期)。

验证方法:

# 锁定账户 usermod -L testuser # 尝试密码登录(应失败) ssh testuser@localhost # 尝试密钥登录(应成功) ssh -i ~/.ssh/testuser_key testuser@localhost

5.3nmap扫出端口开放,但ss -tulnp看不到进程?真相是Docker或容器

问题现象:nmap -sS localhost显示8080/tcp open http,但ss -tulnp | grep 8080无输出。
原因:Docker容器映射了宿主机端口,但进程在容器内运行,ss在宿主机视角看不到容器内进程的详细信息。

排查:

# 查看Docker容器端口映射 docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}" | grep 8080 # 进入容器检查真实进程 docker exec -it <container_id> ps aux | grep -E "(nginx|apache|java)" # 检查容器内用户权限 docker exec -it <container_id> id -u $(ps aux | grep -E "(nginx|apache)" | awk '{print $1}' | head -1)

加固:

  • Docker运行时加--read-only(只读文件系统);
  • 容器内用非root用户运行服务:docker run -u 1001:1001 ...;
  • 宿主机防火墙限制Docker桥接网络:iptables -A FORWARD -i docker0 -o eth0 -j DROP。

5.4 弱口令检测时hashcat报错“OpenCL driver not found”?用CPU模式救急

问题:在无GPU的服务器或虚拟机上,hashcat因缺少OpenCL驱动无法运行。
解决方案:强制使用CPU内核:

hashcat -m 1800 -a 0 --force --opencl-device-types 1 /tmp/hashes.txt rockyou.txt
  • --opencl-device-types 1:指定只用CPU(类型1),跳过GPU(类型2);
  • --force:忽略驱动警告。

性能对比:CPU模式比GPU慢10-50倍,但胜在稳定。对于小规模账户(<100个),仍可在1小时内完成常用字典爆破。

最后分享一个小技巧:定期用lastb命令检查失败登录,lastb | head -20能快速发现暴力破解痕迹。若发现同一IP高频失败,立即用iptables -A INPUT -s xxx.xxx.xxx.xxx -j DROP封禁,并追查来源。安全不是一劳永逸,而是持续狩猎。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询