1. 系统安全不是什么黑科技,先理清防线在哪
做运维这些年,我发现很多同行对"系统安全"这件事有两个极端的误解:要么觉得装了防火墙和杀毒软件就万事大吉,要么觉得安全是安全团队的事,跟日常应用部署没关系。但真正被攻破的系统,绝大多数不是被什么高深漏洞打穿,而是栽在一些最基础、最无聊的疏漏上——弱口令、未授权端口、失效的备份、没人看的日志。
这里说的"系统安全及应用",指的是从操作系统层面到上层业务应用的一整套加固和防御实践。它不是单一工具能解决的问题,而是一套组合动作:账户体系怎么管、权限怎么收敛、不必要的服务怎么关、异常行为怎么被发现、出了问题怎么快速止血。这篇文章就以我在某业务系统上做的一次完整安全加固为线索,把整个思路和操作拆开讲清楚,适合刚接手服务器运维、打算系统梳理一遍安全配置的同行参考。
我始终觉得,安全工作的核心逻辑就一句话:把攻击者的成本抬高到他不愿意继续尝试的程度。你不需要做到绝对无法攻破,那是伪命题,但你可以做到让系统变成整条街最麻烦的那一户——盗贼不会专门挑你家撬锁,因为有更好下手的邻居。下面所有操作,都是围绕这个目标展开的。
2. 账户与权限:最容易忽视的第一道门
2.1 root直接登录这件事,必须从根源上禁掉
几乎所有Linux服务器在刚装好的时候,都默认允许root直接通过SSH登录。这个默认配置在早期网络环境里还算够用,但在如今端口扫描和密码爆破如此普及的背景下,等于把防盗门钥匙挂在门口。我先说结论:禁止root直接SSH登录,是所有加固动作里性价比最高的一个。
具体操作不复杂,修改/etc/ssh/sshd_config:
# 禁止root通过SSH直接登录 PermitRootLogin no # 建议同时关闭密码登录,改用密钥 PasswordAuthentication no PubkeyAuthentication yes改完记得systemctl restart sshd。这里有两个容易踩的坑:
- 第一个坑:有些发行版的
sshd_config里会有Include /etc/ssh/sshd_config.d/*.conf这类追加配置,如果你只改主配置文件而那个覆盖文件里写着PermitRootLogin yes,重启后照样是允许的。我习惯改完之后用sshd -T | grep -i permitrootlogin确认实际生效值。 - 第二个坑:在没确认密钥能正常登录之前就把密码认证关掉,很可能把自己锁在门外。稳妥顺序是:先把自己的公钥加到
authorized_keys,测试密钥登录成功后再关密码认证和root登录。
2.2 最小权限原则:普通用户不该有sudo,root更不该常驻
禁掉root直连之后,日常操作要走普通用户加sudo的方式。但这里又引出一个更隐蔽的问题:sudo权限给得太散。我在某次审计中发现,一个只负责部署静态页面的同事账户,居然在sudoers里被赋予了ALL=(ALL) ALL,这相当于他随时可以用root身份执行任意命令。一旦他的笔记本密码泄露或中木马,攻击者直接就拿到了系统最高控制权。
我的建议是把sudo权限精确到命令级别:
# 只允许某人重启Nginx和检查服务状态 deployuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx见过太多团队为了省事,直接给开发同学一把sudo的全量权限,理由是"反正都是自己人"。这个逻辑在安全审计面前站不住脚。权限的粒度越细,出问题时排查范围就越小,这个观念一定要在团队里立住。
另外,长期使用root干活是大忌。不只是安全上的问题,误操作的成本也极高,rm -rf跑在root下和跑在普通用户下的后果完全不是一个量级。我给自己和团队立了个规矩:能用普通用户做的操作绝不用root,必须提权时看一眼命令确认无误再执行。
2.3 账户生命周期:离职账号清理与弱口令扫描
安全审计里还有一个经常被忽略却风险极高的点:离职员工的账号没有及时禁用或删除。我在一次内部演练中,用了一个已经离职三个月的同事的账户,密码还是他工位便签上写的那串,居然直接登进了生产服务器。这件事让我意识到,账户生命周期的管理必须制度化。
具体来说,我这边落地了两件事:
- 每季度做一次
/etc/passwd和/etc/shadow的排查,把超过90天未登录的账户列出来,确认是否需要保留。对离职人员,当天就执行usermod -L锁定并归档其home目录。 - 用
john这类工具做一次内部弱口令自查。别以为所有人都设置了强密码,实际扫描结果往往让人大跌眼镜,123456、Admin@123这类密码在真实环境里的出现率远比你想象的高。自查的目的不是为难同事,而是把风险提前暴露在内部。
3. 服务与端口:攻击面瘦身实操
3.1 先搞清楚服务器上到底开了哪些服务
攻击面这个词听起来抽象,落到实操层面就是一句话:所有在监听端口的进程,都是潜在的入侵入口。在加固某业务系统时,我先跑了一遍端口和服务盘点,结果发现一台只跑Web应用的服务器上,居然开着FTP、Telnet和一堆莫名的Java进程。
盘点命令不复杂:
# 查看所有监听端口和对应进程 ss -tlnp # 查看系统中所有运行的服务单元 systemctl list-units --type=service --state=running拿到的清单就是你的"攻击面地图"。凡是清单里出现但没人说得清用途的服务,一律先停掉再观察。我当时顺手处理掉了一个跑在6100端口的旧监控agent,后来查证确实是某个项目废弃后忘了卸载的遗留物。这类"僵尸服务"在真实环境里非常普遍,它们大多是不知名的端口、过期的组件版本、无人维护的脚本,攻击者最喜欢扫描这类入口。
3.2 防火墙不是摆设:默认拒绝才是正确姿势
很多服务器的防火墙策略是"默认允许,按需阻止",这个思路本身就反了。正确的姿势是默认拒绝所有入站流量,再按业务需求放行特定端口。
以我常用的firewalld为例,一个Web服务器的典型配置长这样:
# 设置默认zone为drop,丢弃所有未显式放行的入站流量 firewall-cmd --set-default-zone=drop # 仅放行SSH和Web端口 firewall-cmd --permanent --zone=drop --add-service=ssh firewall-cmd --permanent --zone=drop --add-port=80/tcp firewall-cmd --permanent --zone=drop --add-port=443/tcp firewall-cmd --reload这里有个容易被忽略的细节:drop和block是有区别的。drop是直接丢弃数据包,客户端会一直卡在连接超时;block会返回icmp-host-prohibited,等于告诉对方"我拒绝你"。对攻击者来说,drop让端口扫描器误以为主机不可达,信息暴露最少,所以我推荐用drop作为默认策略。
3.3 不必要的组件和服务:少一个组件就少一块被攻击的地
服务端安全里有个铁律:每多装一个组件,就多一份被攻击的风险。比如你的机器上如果装了PHP,但实际业务是纯Java应用,那PHP就是多余的攻击面;如果装了MySQL但实际数据库用的是PostgreSQL,那MySQL的3306端口就是一块纯粹的风险暴露。
在加固时,我通常会做一次"组件瘦身":
- 用
rpm -qa或dpkg -l列出所有安装包,逐个确认用途,确认不了的先标记再查。 - 移除无用服务:
systemctl disable --now <服务名>,不仅停掉,还要禁止开机自启。 - 对实在无法移除的组件(比如操作系统自带的某些库),至少要做到升级到官方维护版本,避免使用已停止维护的旧版本。
这一步做的时候要敢于做减法。很多运维不敢动服务器上的东西,怕搞坏业务,这可以理解,但"保持原样不动的服务器更安全"是错觉,恰恰是那些常年没人动的服务器,最可能带着一堆早已被公开的CVE漏洞在裸奔。
4. 日志与审计:事后追查的底气
4.1 日志不集中,出了事就是大海捞针
在一次攻防演练中,我亲眼见到一个同事排查入侵痕迹时,逐台机器登录上去翻/var/log/messages,翻了两个多小时毫无头绪。问题很明显:日志分散在各台机器上,没有统一收集,也没有时间轴概念,想还原攻击路径几乎不可能。
后来我搭了一套基于rsyslog的集中日志方案。原理很简单:各服务器把日志实时转发到一台专门的日志服务器上,按来源主机和日期分目录存储。客户端配置如下:
# /etc/rsyslog.d/50-remote.conf *.info;mail.none;authpriv.none;cron.none @192.168.1.100:514服务端跑一个rsyslog接收即可,默认监听514端口,为了避免明文传输被截获,也考虑过用rsyslog的TLS加密模块,但对于内网环境可以先用明文,后续再升级。集中日志的价值在于:攻击者清理单台机器的日志记录没有用,原始日志早已被转发走了,这个追查底气的价值在应急响应时无可替代。
4.2 auditd:比常规日志更硬核的审计利器
除了常规系统日志,auditd是我强烈推荐启用的组件。它做的事情是记录"谁在什么时间执行了什么系统调用、访问了哪个文件",粒度可以精细到每次/etc/shadow文件的读取操作。
启动审计的流程:
systemctl enable auditd systemctl start auditd # 监控关键文件的访问行为 auditctl -w /etc/passwd -p wa -k passwd_changes auditctl -w /etc/shadow -p wa -k shadow_changes auditctl -w /etc/sudoers -p wa -k sudoers_changes配置完之后,查看审计记录用:
ausearch -k passwd_changes -ts recent为什么要用auditd而不仅仅是看系统日志?因为常规系统日志记录的是"应用层发生了什么",而auditd记录的是"内核层发生了哪些系统调用"。攻击者即使删掉了应用日志,auditd的记录也会保留,前提是你没被人直接把审计日志清掉(所以日志服务器要单独收一份,双保险)。当然,auditd如果规则配得太多,对系统性能会有影响,建议只对敏感文件和目录加规则,不要无脑全盘审计。
4.3 日志里的异常行为怎么快速发现
日志收集起来了,如果没人看,那也就是个"合规摆设"。我见过不少系统,日志文件堆了几十GB,但审计员从没打开过。这里的核心问题是:怎么在大量的正常日志里挑出异常。
我的常用做法是给关键行为做特征匹配。比如SSH登录日志里,Accepted publickey for是正常,但Failed password如果短时间内出现几十次,基本可以断定在爆破。这类特征可以直接写进监控脚本,用crontab每五分钟跑一次:
#!/bin/bash # /usr/local/bin/check_ssh_attack.sh LAST_MINUTE=$(date -d '5 minutes ago' '+%b %e %H:%M') ATTEMPTS=$(journalctl --since="$LAST_MINUTE" | grep "Failed password" | wc -l) if [ "$ATTEMPTS" -gt 20 ]; then echo "$(date) SSH failed attempts: $ATTEMPTS" >> /var/log/attack_alerts.log # 这里可以接入企业微信/钉钉/Slack等webhook通知 fi这类脚本写的多了之后,可以整理成一套"异常行为规则库",比如:短时间大量404可能是扫描器在探测,短时间内相同IP频繁访问某个接口可能是撞库,非工作时间大量sudo操作可能是账户被盗。规则不需要一开始就做得很全,从最近一次真实攻击或演练暴露出的问题开始,一条条加进去,逐步完善就够了。
5. 文件备份与数据恢复:安全体系的最后一道保险
5.1 备份不是"有就行",而是要验证"确实能恢复"
很多团队对备份的态度是"每天有个定时任务在跑,文件都到备份目录了,应该没问题"。但真正的考验在灾难发生时,如果真的遇到勒索软件加密或者误删除,你才会发现备份要么是空的、要么是坏的、要么是恢复流程根本没验证过。
我的做法是每月做一次真实的恢复演练。不是跑个脚本看看备份文件存在,而是真的把备份拉到一台干净的机器上,启动服务、验证数据完整性、确认业务能跑起来。演练过程中发现的问题,认真记录并回填到备份策略里,比如发现之前备份的数据库文件用mysqldump导出时漏掉了某些存储过程,这种问题只有恢复演练才能暴露。
5.2 备份策略的3-2-1原则
备份界有个经典的3-2-1原则,实操中非常好用:
3份数据副本(生产数据本身算一份,总共至少三份),2种不同存储介质(比如本地磁盘一份、异地对象存储一份),1份存放在异地。
具体到某业务系统的落地:
# 每周日凌晨通过crontab定时任务,将关键目录打包后同步到异地备份机 30 3 * * 0 tar czf /backup/data_$(date +\%F).tar.gz /var/lib/mysql /opt/app/config && rsync -avz /backup/data_*.tar.gz backup-server:/backup/加密也很重要,备份数据在传输和存储过程中建议加密,尤其涉及用户敏感数据的场景。用gpg加密打包文件:
gpg --encrypt --recipient backup-key /backup/data_$(date +%F).tar.gz rm -f /backup/data_$(date +%F).tar.gz这样做有个好处,即使备份服务器被攻破或介质被盗,拿到手的也只是一堆无法解密的密文,不会直接导致数据泄露。
5.3 勒索软件场景下的备份注意事项
说到勒索软件,备份可能是唯一有效且不交赎金的路径。但要注意:如果备份系统和被攻击的生产系统在同一网络、同一账户体系下,攻击者通常会把备份一起加密掉。
所以备份的权限隔离是硬性要求。我在实践中做了三个动作:
- 备份存储位置禁止挂载到生产系统的文件系统里,防止横向加密。
- 备份服务器使用独立的账户体系,和生产服务器互不相同,密码策略独立。
- 备份数据保留多个历史版本,至少保留90天。因为有些勒索软件加密后不会立刻触发加密动作,而是潜伏几周甚至几个月,如果只保留最近一两天的备份,可能会把已感染期间的数据也恢复出来。
6. 日常安全运维的落地节奏与工具选型
6.1 定期巡检不是嘴里说说,要有清单和频率
安全运维最怕"想起来才做"。我之前给自己团队定了一个巡检节奏,效果不错,分享出来供参考:
| 巡检项 | 频率 | 具体动作 |
|---|---|---|
| 账户与权限审查 | 每月 | 检查离职账户、sudoers变更、SSH密钥变化 |
| 漏洞扫描 | 每月 | 对公网IP做一次端口和组件版本扫描 |
| 系统更新 | 每周 | 安全补丁优先安装,评估后执行 |
| 日志抽查 | 每周 | 查看异常登录、异常执行记录 |
| 备份恢复演练 | 每月 | 真实拉起备份验证服务可启动 |
| 弱口令自查 | 每季度 | 对系统账户和业务应用账号做扫描 |
这个频率不是拍脑袋定的,每个周期都和实际风险出现的概率有关。比如漏洞扫描,公网IP如果每天都在被扫描器探测,一个月一次的频率已经算保守;而备份恢复演练因为成本高,一个月一次也是绝大多数团队能接受的节奏。
6.2 补丁管理:低垂的果实最容易拿
我遇到过不少"业务系统不敢打补丁,因为一更新就出问题"的团队。这个问题其实可以拆开来解决:不是所有补丁都需要在生产环境立即打,也不是所有组件都值得冒险升级。关键是把风险和稳定性分开考量。
我的做法是给补丁分优先级:
- 涉及远程代码执行、权限提升的漏洞补丁,属于最高优先级,评估后尽快打,必要时先在一个副本环境验证再灰度到生产。
- 涉及功能增强的常规更新,攒到月度维护窗口统一处理,降低频繁变更带来的稳定性风险。
- 对于已停止维护的组件版本,尽快排期升级到受支持版本,不给自己留"已知漏洞无人修"的烂账。
补丁打完之后的验证同样重要,不能"打完就完事"。我一般会在补丁部署后24小时内盯一下核心指标日志,确认没有异常报错或资源占用异常,才算真正完成闭环。
6.3 安全工具链的选型思路:够用就好,别堆砌
很多人一提到做安全,第一反应就是上一堆商业设备,但实际用起来才发现大部分功能是闲置的。我的经验是:安全工具链的选型遵守"从现状出发,够用就好,逐步增加"的原则。
在最开始的加固阶段,一套防火墙规则、一个日志审计工具、一份备份策略就已经能挡住80%的基础攻击。等业务规模和技术能力提升后,再逐步引入入侵检测、漏洞管理平台、安全运营中心这类更重的组件。盲目堆砌工具不但增加维护成本,还会让真正的告警淹没在大量误报里。
另外,工具的安全本身也要在考虑范围内。像监控类的agent、日志收集器,如果配置不严,反而会成为攻击者横向移动的跳板,这类工具占用的服务器权限尽可能收敛,通信加密、访问控制都要跟上。
7. 写在最后:一次完整加固的复盘心得
整套加固流程走完,我把某业务系统从最初的"裸奔"状态调整到安全基线覆盖状态,前后用了一个多月。整个过程最深的体会有三点:
第一,先做减法再做加法。先把不需要的账户、服务、端口、组件砍掉,再去做加密、审计、监控这些加法,顺序反了会事倍功半。因为攻击面最小化是其他一切防护措施有效的前提。
第二,安全是持续的过程,不是一次性的项目。做完加固那一刻,系统的安全状态是最好的,之后就一直在缓慢变差——新加的账户、新开的端口、新部署的组件,每一个都在侵蚀当初的安全基线。所以需要定期回归基线,把这些漂移校准回来,这也是巡检清单存在的意义。
第三,别追求零风险,要追求可恢复。任何人跟你说"绝对安全"都可以直接拉黑,真正的底气来自"即使被攻破,也能快速发现、快速隔离、快速恢复"。备份、日志、应急演练,这些能在最坏情况下兜底的能力,恰恰是安全体系里最值得投入的部分。
最后再分享一个小技巧:每次做完一个加固动作,随手记录到团队的安全加固文档里,写明操作原因、操作步骤、验证方式和回滚方案。这份文档的价值会在半年后体现——那时候再有同事问"当初为什么要禁掉这个端口",你翻出记录就能直接给出答案,而不是让对方对着一个冷冰冰的配置项发懵。