简介:面向Linux系统管理员、运维工程师及安全初学者的一份安全加固实践指南,聚焦操作系统层面的基线配置与风险收敛,帮助读者在服务器上线前后系统性地排查安全隐患。内容围绕安装阶段的安全设置、用户帐号与密码安全策略、密码shadowing机制、密码管理规范,以及网络服务安全中的服务过滤、/etc/inetd.conf、R服务、Tcp_wrapper、/etc/hosts.equiv等配置文件逐项展开,并延伸至NFS、tftp、Sendmail、finger、UUCP等常见服务的加固要点,目录结构清晰,便于按模块查阅对照。资源包共1个PDF文件,约40KB,篇幅精简、条目分明,适合作为日常巡检与配置核查的速查手册。目前已有1338人学习,可作为搭建安全加固知识框架、梳理检查清单的入门参考。
1. 从一台刚装完的 CentOS 7 说起:LINUX 安全加固手册怎么落地
上周帮朋友收拾一台刚装完的 CentOS 7 测试机,系统装好、SSH 能连、服务能跑,看着一切正常。我随手敲了netstat -an,屏幕上跳出来一堆监听端口:sunrpc、sendmail、rpc.statd,还有几个我压根没让它启动的服务。这就是典型的“装完即裸奔”——默认安装为了兼容性,把大量用不上的服务全打开了,每一个都是潜在的攻击面。LINUX 安全加固这件事,难的不是某一条命令,而是把密码策略、账号清理、网络服务过滤、系统参数、文件权限这几块串成一套能复现的流程。这份《LINUX安全加固手册》正好是按这个顺序组织的,我照着它在一台 CentOS 7.9 上完整走了一遍,把能直接抄的命令、容易翻车的参数和验证方法整理出来,适合刚接手 Linux 服务器、需要做基线加固的开发者照着走。
2. 加固前的环境准备:版本确认与最小化安装
2.1 确认系统版本和包管理器
手册里的命令大量使用rpm -e和/etc/rc.d/init.d/路径,这套写法对应的是 RHEL/CentOS 6 及更早的 SysV init 体系。在 CentOS 7 上service命令还能用,但chkconfig已经被systemctl取代,/etc/inetd.conf也换成了/etc/xinetd.d/目录。所以第一步不是急着敲命令,而是先确认你面对的是哪一代系统:
# 查看发行版和版本号 cat /etc/redhat-release # 查看 init 体系,确认是 SysV 还是 systemd ps -p 1 -o comm= # 查看已安装的包,确认哪些服务存在 rpm -qa | grep -E 'sendmail|rpcbind|ypbind|telnet-server'ps -p 1 -o comm=输出systemd说明是 CentOS 7 及以上,输出init则是 6 及以下。这个判断决定了后面所有服务管理命令的写法:SysV 用/etc/rc.d/init.d/xxx stop加chkconfig xxx off,systemd 用systemctl stop xxx加systemctl disable xxx。手册里写的[root@deep]# /etc/rc.d/init.d/apmd stop在 CentOS 7 上会提示文件不存在,不是命令错了,是体系变了。
2.2 隔离网络再动手
手册第 2 章第一条就是“使系统处于单独(或隔离)的网络中”,这条经常被跳过,但它是有道理的。加固过程中你会停服务、改 PAM 配置、动 SSH 相关设置,任何一步配错都可能导致远程连接断开。我一般会先把机器放在只有管理网可达的隔离网段,或者至少保证有一个带外控制台(云主机的 VNC、物理机的 IPMI)能兜底。没有这个前提,改/etc/pam.d/su改出问题就只能重装了。
2.3 最小化卸载:先停进程再删包
手册列了一批要卸载的包:pump、apmd、lsapnptools、redhat-logos、mt-st、kernel-pcmcia-cs、setserial、redhat-release、eject、linuxconf、kudzu、gd、bc、getty_ps、raidtools、pciutils、mailcap、setconsole、gnupg。这里有个顺序问题,手册也点到了:卸载前先停掉 apmd、sendmail、kudzu 三个进程。原因是这些包的服务正在运行,直接rpm -e可能因为文件被占用或依赖关系报错。
# 先停服务,再卸载,顺序不能反 systemctl stop apmd sendmail kudzu 2>/dev/null # 逐个卸载,-e 是 erase,--nodeps 仅在确认无依赖时使用 rpm -e apmd sendmail kudzu 2>/dev/null # 批量检查还有哪些在列表里但没删掉的 for p in pump lsapnptools redhat-logos mt-st setserial eject linuxconf gd bc raidtools pciutils mailcap setconsole gnupg; do rpm -q "$p" >/dev/null 2>&1 && echo "still installed: $p" donerpm -e后面跟包名,不带版本号。如果报error: Failed dependencies,说明有别的包依赖它,这时候不要硬加--nodeps,先rpm -q --whatrequires 包名看清楚谁在依赖,再决定是保留还是连带处理。手册里redhat-release这个包在 CentOS 7 上叫centos-release,直接照抄会报“未安装”,属于版本差异,跳过即可。pciutils提供lspci,很多排查场景要用,我一般会保留,手册列它可能是针对极简服务器场景,这一点按实际需要取舍。
3. 用户账号与密码策略:shadow、login.defs 和 su 限制
3.1 密码策略落到 login.defs
手册要求口令至少 6 位、含特殊字符、有有效期。前两条靠制度和审计,有效期靠/etc/login.defs里的PASS_MAX_DAYS和PASS_MIN_LEN控制。CentOS 7 默认PASS_MAX_DAYS 99999,等于永不过期,这是加固时要改的第一处:
# 备份原文件,改配置前先留后悔药 cp -a /etc/login.defs /etc/login.defs.bak # 口令最长有效期 90 天,最短长度 8,最短使用间隔 1 天 sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' /etc/login.defs sed -i 's/^PASS_MIN_LEN.*/PASS_MIN_LEN 8/' /etc/login.defs # 确认改动生效 grep -E '^PASS_(MAX|MIN)' /etc/login.defsPASS_MAX_DAYS是密码最长可用天数,超过强制改密;PASS_MIN_DAYS是两次改密的最小间隔,防止用户改完立刻改回去绕过历史记录;PASS_MIN_LEN是最短长度,手册写 6,我按现在的最低要求提到 8。注意PASS_MIN_LEN只对passwd命令生效,不约束 root 用chpasswd直接设密码,真正的复杂度还得靠 PAM 的pam_pwquality模块,那是另一层配置,手册没展开,这里不硬凑。
3.2 检查空密码账号
手册给了一条 awk 命令查 shadow 里密码为空的账号,这条很实用,空密码账号是加固审计的必查项:
# 第二字段长度为 0 说明密码为空,直接列出用户名 awk -F: 'length($2)==0 {print $1}' /etc/shadow # 顺带查 UID 为 0 的账号,除了 root 不该有第二个 awk -F: '$3==0 {print $1}' /etc/passwd-F:指定冒号分隔,$2是 shadow 的密码哈希字段,length($2)==0匹配空值。正常系统上这条应该没有任何输出,如果有输出,说明该账号可以无密码登录,必须立刻passwd 用户名设密码或usermod -L锁定。第二条查 UID 0 是配套动作,攻击者常留一个 UID 0 的后门账号,两个检查一起做才完整。
3.3 su 限制到 wheel 组
手册 3.4 节要求只允许特定用户su成 root,改/etc/pam.d/su,加pam_wheel.so group=wheel。CentOS 7 的/etc/pam.d/su里这两行默认是注释状态,去掉注释即可:
# 去掉 pam_rootok 和 pam_wheel 两行的注释 sed -i 's/^#auth\s*sufficient\s*\/lib\/security\/pam_rootok.so/auth sufficient \/lib\/security\/pam_rootok.so/' /etc/pam.d/su sed -i 's/^#auth\s*required\s*\/lib\/security\/pam_wheel.so\s*use_uid/auth required \/lib\/security\/pam_wheel.so use_uid/' /etc/pam.d/su # 把需要 su 的用户加进 wheel 组,GID 10 在 RHEL 系是 wheel usermod -G10 用户名 # 验证:非 wheel 用户 su 应被拒绝pam_rootok.so让 root 自己 su 不需要密码,pam_wheel.so use_uid限制只有 wheel 组成员能 su。手册写的是group=wheel,CentOS 7 的默认行是use_uid,效果等价,按系统自带的写法去掉注释更稳。改完一定要在另一个终端保持一个 root 会话别断,然后新开终端用普通用户测一次su -,确认被拒或成功,避免把自己锁在外面。
3.4 清理系统账号与文件不可变位
手册 3.5 节列了要删的账号:adm、lp、sync、shutdown、halt、news、uucp、operator、games、gopher、ftp。这里要克制,sync、shutdown、halt这些账号被一些脚本和sudo规则引用,删之前先确认没有服务在用。我的做法是先锁定而不是直接删:
# 先锁定,观察一周无异常再删 for u in adm lp news uucp operator games gopher; do usermod -L "$u" 2>/dev/null && echo "locked: $u" done # 确认无影响后再删 userdel adm 2>/dev/null # 关键文件加不可变位,防止被篡改 chattr +i /etc/passwd /etc/shadow /etc/group /etc/gshadow # 需要改账号时先解锁 chattr -i /etc/passwd /etc/shadowusermod -L在密码哈希前加!,账号无法登录但保留记录,比直接删安全。chattr +i是手册强调的防篡改手段,加了之后连 root 都不能直接改这几个文件,必须chattr -i解锁。这里有个血泪经验:加完不可变位后忘了,后面useradd新用户会报cannot open /etc/passwd,排查半天才想起来是自己锁的。所以加锁后要在运维记录里写一笔,或者干脆只对shadow和gshadow加锁,passwd和group留活口。
4. 网络服务收敛:inetd、xinetd 与 tcp_wrapper
4.1 先看清开了哪些服务
手册 4.1 节给了一张要过滤的端口表:echo 7、systat 11、netstat 15、bootp 67、tftp 69、sunrpc 111、snmp 161、exec 512、login 513、shell 514、printer 515、nfs 2049、x11 6000 等。动手前先用netstat或ss摸清现状:
# 列出所有监听端口及对应进程 ss -tulnp # 只看 TCP 监听 netstat -an | grep LISTEN # 对照手册端口表,标记出需要关的ss -tulnp里-tTCP、-uUDP、-l监听、-n数字端口、-p显示进程。输出里users:(("rpcbind",pid=...))这种就是服务名和 PID,比netstat信息全。把输出和手册的端口表对一遍,echo、systat、tftp、rpcbind 这些如果出现了,就是下一步要处理的。
4.2 inetd 与 xinetd 的版本分叉
手册 4.2 节讲/etc/inetd.conf权限设 600、属主 root、注释掉不需要的服务。CentOS 7 没有inetd.conf,用的是xinetd,配置在/etc/xinetd.d/目录下,每个服务一个文件。处理逻辑一样,只是路径和语法不同:
# CentOS 6 及以下:编辑 inetd.conf,注释服务行 # CentOS 7:进 xinetd.d 目录,把 disable 改成 yes ls /etc/xinetd.d/ # 以 telnet 为例,改 disable = yes 后重启 xinetd sed -i 's/disable.*=.*no/disable = yes/' /etc/xinetd.d/telnet systemctl restart xinetd # 确认端口已关闭 ss -tulnp | grep ':23 'disable = yes表示该服务不启动,no表示启动。改完必须重启xinetd才生效,只改文件不重启是常见漏步。手册里“重新启动 inetd 进程”在 CentOS 7 上对应systemctl restart xinetd。如果xinetd本身没装,ls会报目录不存在,说明系统根本没跑这套超级守护进程,那 4.2 节整节可以跳过,直接看ss结果处理独立服务。
4.3 tcp_wrapper 的 allow/deny 顺序
手册 4.4 节讲 tcp_wrapper,核心是/etc/hosts.deny设ALL: ALL默认拒绝,/etc/hosts.allow放行特定地址。这个顺序不能反,tcp_wrapper 的匹配规则是先查 allow 再查 deny,allow 命中就放行,否则看 deny:
# 默认拒绝所有,PARANOID 拒绝正反解析不一致的主机 cat >> /etc/hosts.deny <<'EOF' ALL: ALL@ALL, PARANOID EOF # 只放行管理网段的 sshd cat >> /etc/hosts.allow <<'EOF' sshd: 192.168.10.0/255.255.255.0 EOF # 用 tcpdchk 检查配置语法 tcpdchk -vALL: ALL@ALL, PARANOID里ALL是所有服务,ALL@ALL是所有用户从所有主机,PARANOID是主机名与 IP 正反解析不一致时拒绝。hosts.allow里sshd: 192.168.10.0/255.255.255.0表示只允许这个网段连 sshd。tcpdchk -v会逐条检查配置并报语法错误,改完必跑。这里有个坑:如果你的 SSH 不是走 tcp_wrapper(CentOS 7 默认 sshd 直接监听,不经过 tcpd),hosts.allow里写 sshd 是不生效的,得靠 firewalld 或 iptables 做源地址限制。手册写这套是针对经过 inetd/xinetd 托管的服务,别指望它管住所有端口。
4.4 关掉 R 服务和 tftp
手册 4.3 节要求关 R 服务(exec 512、login 513、shell 514),4.9 节明确 tftp“无论何种情况下都不应该启动”。CentOS 7 上这些服务如果存在,都在 xinetd.d 下:
# 批量关闭 r 服务和 tftp for s in exec login shell tftp; do [ -f "/etc/xinetd.d/$s" ] && sed -i 's/disable.*=.*no/disable = yes/' "/etc/xinetd.d/$s" done systemctl restart xinetd 2>/dev/null # 确认 512/513/514/69 端口不再监听 ss -tulnp | grep -E ':(512|513|514|69)\b'[ -f ... ]先判断文件存在再改,避免服务没装时报错。grep -E ':(512|513|514|69)\b'里\b是单词边界,防止匹配到 5120 这种端口号。如果输出为空,说明这几个高危端口已经关干净。手册还提到预生成.rhosts和/etc/hosts.equiv并设 0000,防止被写入+ +,这个动作在 CentOS 7 上依然有效:
# 预生成空文件并锁死权限,防止被追加信任规则 touch /root/.rhosts /etc/hosts.equiv chmod 000 /root/.rhosts /etc/hosts.equiv chattr +i /root/.rhosts /etc/hosts.equivchmod 000让任何用户都读不了,chattr +i防止被删改。攻击者拿到低权限后常往.rhosts写+ +实现免密登录,文件存在但不可写,这条路就堵死了。
5. 系统参数与文件权限:Ping、securetty 和日志
5.1 关闭 ICMP Echo
手册 5.2 节要求关 ping,命令是echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all。这条命令重启失效,要持久化得写进sysctl.conf:
# 临时生效 echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all # 持久化,写进 sysctl.conf echo 'net.ipv4.icmp_echo_ignore_all = 1' >> /etc/sysctl.conf sysctl -p # 验证:本机 ping 自己应该不通 ping -c 1 127.0.0.1icmp_echo_ignore_all = 1表示忽略所有 ICMP Echo 请求,外部 ping 不通。sysctl -p重新加载配置。这里要权衡:关 ping 能减少被扫描发现的概率,但排障时自己也 ping 不通,监控系统如果靠 ping 探活会误报。我的做法是内网机器不关,对外暴露的边界机才关,手册这条按机器角色取舍,不是无脑全上。
5.2 securetty 限制 root 登录终端
手册 5.4 节提到/etc/securetty,这个文件列了 root 允许登录的终端。默认包含tty1到tty11和console,如果加了pts/0这类伪终端,root 就能从 SSH 直接登录。加固方向是删掉不必要的条目:
# 备份后只保留物理控制台 cp -a /etc/securetty /etc/securetty.bak grep -vE '^(pts/|tty[0-9]+$)' /etc/securetty.bak > /etc/securetty # 确认内容 cat /etc/securettygrep -vE '^(pts/|tty[0-9]+$)'排除pts/开头的伪终端和纯数字 tty,只留console、ttyS0这类物理或串口终端。改完 root 就不能直接 SSH 登录了,必须普通用户登录再su或sudo,配合前面 wheel 组的限制,等于上了双保险。改之前务必确认有一个可用的普通用户能su到 root,否则真会把自己关在门外。
5.3 日志与脚本权限
手册 5.10 和 5.11 节讲日志系统安全和/etc/rc.d/init.d下脚本权限。日志这块核心是确保/var/log下关键日志不被普通用户读写,/var/log/secure、/var/log/messages默认 600 属主 root 就没问题,检查一遍即可:
# 检查关键日志权限,非 600 的列出来 find /var/log -maxdepth 1 -type f \( -name 'secure' -o -name 'messages' -o -name 'auth.log' \) ! -perm 600 -ls # 启动脚本统一 755,属主 root chmod 755 /etc/rc.d/init.d/* chown root:root /etc/rc.d/init.d/*find ... ! -perm 600 -ls找出权限不是 600 的日志文件,-ls打印详情。启动脚本 755 是标准权限,属主 root 防止被普通用户篡改后随开机执行。CentOS 7 的启动脚本在/usr/lib/systemd/system/下是 unit 文件,/etc/rc.d/init.d/里多是兼容用的 SysV 脚本,两处都检查一遍更稳。
6. 装完怎么验证:三条命令确认加固真的生效
加固做完不等于生效,得用外部视角验证一遍。我习惯用三条命令收尾,分别对应账号、端口、参数三个层面,任何一条不符合预期就回头查对应章节。
第一条查账号和密码状态:
# 空密码账号、UID 0 账号、可登录 shell 的账号一次看全 awk -F: 'length($2)==0 {print "EMPTY PW: "$1}' /etc/shadow awk -F: '$3==0 {print "UID0: "$1}' /etc/passwd awk -F: '$7 !~ /(nologin|false)$/ {print "LOGIN SHELL: "$1}' /etc/passwd第一条输出应为空,第二条只应有root,第三条列出所有能登录的账号,逐个确认是不是业务需要的。多出来的就是该锁没锁的。
第二条从另一台机器扫端口,模拟外部视角:
# 在管理机执行,扫描目标机的常见高危端口 nmap -sT -p 7,11,15,69,111,161,512,513,514,2049,6000 目标IP-sT是 TCP 连接扫描,不需要 root 权限。输出里这些端口都应该是closed或filtered,出现open就说明对应服务没关干净,回第 4 章对照处理。手册 4.1 节也强调“配置完成以后,利用网络扫描器模拟入侵者从外部进行扫描测试”,nmap 是最直接的工具。
第三条验证内核参数和文件锁:
# 确认 ping 已关、关键文件不可变位已加 sysctl net.ipv4.icmp_echo_ignore_all lsattr /etc/passwd /etc/shadow /etc/group /etc/gshadow # 确认 su 限制生效:用非 wheel 用户执行应报权限拒绝 su - root -c 'id' 2>&1sysctl输出= 1说明 ping 已关;lsattr输出里带i标志说明不可变位生效;最后一条用普通用户测su,预期是su: Permission denied或要求输入密码后失败。三条都过了,这套加固才算真正落地。
下面这张表是我这台 CentOS 7.9 加固前后的对照,方便你快速核对预期状态:
| 检查项 | 加固前 | 加固后 | 验证命令 |
|---|---|---|---|
| 空密码账号 | 可能有 | 无 | awk -F: 'length($2)==0' /etc/shadow |
| UID 0 账号 | 仅 root | 仅 root | awk -F: '$3==0' /etc/passwd |
| 512-514 端口 | 可能监听 | 关闭 | ss -tulnp | grep -E ':(512|513|514)' |
| ICMP Echo | 响应 | 忽略 | sysctl net.ipv4.icmp_echo_ignore_all |
| passwd 不可变位 | 无 | 有 i | lsattr /etc/passwd |
| su 限制 | 所有用户 | 仅 wheel | 非 wheel 用户su - |
有次我图快,改完login.defs没验证就交付,结果PASS_MIN_LEN那行前面多了个空格,sed没匹配上,等于没改,还是对方安全扫描报出来才发现。从那以后我每次加固完都强制走一遍上面三条验证,不靠“我觉得改对了”。这套流程不复杂,难的是每一步都留验证、留退路。希望帮到你。
本文还有配套的精品资源,点击获取