干了这么多年 Linux 运维,我见过太多“裸奔”的服务器:SSH 跑在默认 22 端口、root 密码简单得离谱、防火墙该开的不该开的全开着、日志几个月没人翻。说实话,每次看到这种状况我都后背发凉,因为中招往往不是运气问题,而是时间问题。Linux 网络安全这个事,说大可以很大,说小其实也小,主要是把基本功做扎实。
这篇文章我从一个运维老兵的角度,把 Linux 系统在网络层面最容易挨打的点挨个梳理一遍,同时给出一套自己能直接抄作业的加固流程,包括账号管理、SSH 防护、防火墙配置、日志审计、异常流量排查这些核心环节。不管你是刚接手第一台服务器的新手,还是在公司扛着几十台机器运维压力的老手,我相信这篇文章里都有能用得上的东西。
1. 先搞明白这台机器容易挨什么打:攻击面梳理
1.1 第一步永远是画一张“暴露面”地图
很多人拿到一台服务器就开始装环境,装完就上线跑业务,但从来没问过一个问题:这台机器到底对外开了什么口子?
我习惯在接手任何一台 Linux 机器时,先执行一条命令:
ss -tlnp这条命令会列出当前所有处于监听状态的 TCP 端口,以及对应的进程。看到的输出会让你意外,很多时候你以为是“干净”的机器,实际上跑着一些你都快忘了的服务。比如某个实验用的后端服务端口 8080 还开着,MongoDB 的 27017 还裸露在公网,Redis 默认配置允许任意来源访问。
拿到端口列表以后,再对照防火墙规则看一遍,确认这些端口到底是只在内网可见,还是真的暴露到了公网。如果你手头有扫描工具,也可以从一台外部机器对自己做一次合法扫描来交叉验证。这里的重点是搞清楚你网络边界上开了哪些口子,才能知道该堵哪里。
除了端口,还有几个地方容易被忽略:网卡上是不是绑定了多余的 IP,会不会有某个服务监听在 0.0.0.0 上,导致内网服务被公网直接访问。这些都要在画暴露面地图的时候一并检查。
画完地图,建议整理成一张简单的表,每一行是一个服务、端口、协议、是否需要对公网开放、负责人是谁。别小看这张表,它是后面做防火墙策略、安全审计的重要依据。
1.2 常见攻击路径和我踩过的坑
暴露面清楚了,接下来要站在攻击者的角度想:如果你是坏人,你会从哪个口子进来?我这些年实际处理过的攻击事件,归纳下来无非下面几条路。
第一条也是频率最高的,是 SSH 暴力破解。只要服务器把 22 端口暴露在公网,扫描器几乎是全天候在尝试账号密码。我之前检查过一台机器的登录日志,一天之内就有上万条失败记录,来源 IP 遍布全球,其中很多是同一个僵尸网络在扫。这类爆破不一定每一次都能成功,但只要你的密码稍微弱一点,字典跑一遍就进去了。
第二条路径是对暴露的高危端口直接攻击。比如 Redis、Docker API、Elasticsearch 这类服务,如果配置不当且直接暴露在公网,就成了攻击者的“提款机”。曾经有同事的 Redis 没设密码,也没绑定内网地址,结果被写入 SSH 公钥,直接登录到了服务器。这个案例我印象极其深刻,所以之后凡是有这类服务的机器,我都强行要求先做访问控制再上线。
第三条路径是漏洞利用。Web 应用层的问题不在 Linux 系统本身,但系统层面的服务也经常有漏洞,比如 OpenSSH 的一些历史 CVE、内核权限提升漏洞、Web 服务器中间件漏洞。这类问题比较考验日常更新的纪律性,不更新就是在赌。
第四条路径是供应链污染。从第三方源下载软件包的时候,如果源被污染或者中间被人动手脚,装上去的东西本身就是个后门。在国内环境里,很多人习惯直接改镜像源,但改完之后从来不校验签名,这也是个隐患。
理解了这些路径,后面的加固动作才有针对性。安全不是做一步看一步,而是从攻击者的整个链条上堵住每一个环节。
2. 从账号和 SSH 开始加固:最便宜也最有效
2.1 SSH 登录防护:堵住最常被敲门的那扇门
SSH 是 Linux 服务器最常用的远程管理入口,也是最常被爆破的服务。加固 SSH,优先级一定是最高的,而且成本极低。
先说最彻底的一步:禁止 root 用户直接远程登录。root 是所有攻击者最想要的目标账号,因为只要拿到 root,整台机器就完全沦陷。禁止 root 远程登录之后,攻击者必须先破解一个普通用户,再通过后续手段提权,难度和可追溯性都会好很多。
接着是把密码登录关掉,只保留密钥登录。密钥登录使用的是非对称加密,私钥只保存在你本地,暴力破解基本无从下手。我在生产环境里已经全面禁用密码登录,只留下密钥这个口子。
配置在 /etc/ssh/sshd_config 文件里,核心内容如下:
# 禁止 root 直接远程登录 PermitRootLogin no # 只允许密钥认证 PasswordAuthentication no PubkeyAuthentication yes # 如果启用了 PAM,也要确保它不会重新放开密码认证 UsePAM yes # 设定空密码用户禁止登录 PermitEmptyPasswords no改完之后先用sshd -t检查语法,再执行systemctl restart sshd。
这里必须提醒一句:改 SSH 配置前,一定确保当前已经成功登录且密钥有效,或者你有带外管理权限。否则一旦密码认证被关掉,而密钥又没配好,你可能会进不了机器。我见过不止一次有人先改了配置重启 sshd,才发现密钥根本没用,结果把自己锁在门外。
另一个常见疑问是“要不要改 SSH 端口”。改端口本质上只是减少被扫到的概率,不是安全手段,但确实能显著减少日志里那些无聊的爆破尝试。我一般会把 SSH 端口改成一个高位端口,再配合 Fail2ban 做动态封禁。注意改端口的时候同样要先放行新端口、测试通过后再停掉旧端口,否则机器就真连不上了。
2.2 最小权限:普通用户加 sudo 白名单
关闭 root 远程登录之后,意味着日常管理和操作都要通过普通用户完成。这引出一个问题:普通用户的权限怎么控制才能既不耽误干活,又不至于失控?
我的原则是做最小授权。日常工作一律用普通用户登录,只有在必要时才通过 sudo 提权。sudo 也不是全命令放行,而是尽量细化到具体的命令白名单。
sudo 的配置存放在 /etc/sudoers 文件,推荐用visudo命令编辑,因为它会在保存前做语法检查。常见的做法是把运维用户加入 wheel 组,这样他们在需要时可以执行 sudo 命令。但是对于更敏感的机器,我会单独配置命令白名单,比如:
# 允许某用户执行 systemctl 管理服务,但禁止修改 sudoers Cmnd_Alias SERVICE_MGMT = /usr/bin/systemctl list-units, /usr/bin/systemctl status user1 ALL=(ALL) NOPASSWD: SERVICE_MGMT每一步提权操作,审计日志里都会留下记录,谁在什么时间以什么身份执行了命令,后续都能追溯。
账号这块还有一个很容易被忽略的安全点:检查有没有多余的、权限过大的账号。定期看一下 /etc/passwd 文件,排查有没有 UID 为 0 的用户。UID 0 意味着超级用户权限,普通用户如果 UID 0,他就拥有了和 root 一样的能力。这条命令我每次巡检必跑:
awk -F: '$3 == 0 {print $1 ":" $3}' /etc/passwd正常情况结果里应该只有 root 一行。如果多出任何其他账号,几乎可以断定是被入侵了。
另外,长期不用的僵尸账号要及时禁用,用usermod -L锁定账号或者删除。密码策略也值得看一眼,虽然现在密钥登录是主流,但系统里总有一些服务账号还在用密码,给它们设一个合理的最小修改周期和复杂度要求,能减少一部分风险。
3. 防火墙层级的门神:从 iptables 到 nftables
3.1 为什么别再裸配 iptables
Linux 网络安全的另一个重头戏是防火墙。早年大家习惯直接操作 iptables,但说实话,手写 iptables 规则维护起来很痛苦,几条链的顺序稍微一乱,可能就连不上机器了。我自己踩过这种坑,特别是在规则很多的时候,排查问题就是地狱模式。
iptables 是内核 netfilter 框架的传统前端,语法是分成 INPUT、OUTPUT、FORWARD 几条静态链来配置的,规则顺序极其敏感,一条 ACCEPT 放错位置在 DROP 后面,效果就完全不对了。这些年我强烈建议新部署的机器直接用 nftables,也就是新一代的 netfilter 前端框架。它的语法更接近人类的阅读习惯,支持规则集原子替换,还允许你像写脚本一样把规则组织成表格和链,理解和维护成本都低很多。
如果你不想手写规则,发行版自带的 firewalld 其实也够用。它把 nftables 包装成更友好的命令,用 zone 的概念来管理不同信任级别的网络区域。对于绝大多数业务场景,firewalld 是绰绰有余的。我通常在快速部署的服务器上用防火墙管理命令,在需要精细控制流量模型的核心机器上直接写 nftables 配置文件。
3.2 一套可落地的 nftables 配置模板
下面给一个我自己常用的 nftables 配置模板,思路很简单:默认策略就是丢弃入站流量,只放行明确需要的服务。这跟很多人习惯的“默认全放,有问题再堵”正好相反,但只有默认拒绝才能真正兜底。
配置文件在 /etc/nftables.conf,基础内容大概长这样:
#!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority filter; policy drop; # 放行回环接口 iif lo accept # 放行已建立连接和关联连接,这是关键 ct state established,related accept # 放行 ICMP,但可以加一点速率限制 ip protocol icmp icmp type echo-request limit rate 5/second accept # 放行 SSH 服务端口,这里假设是 2222 tcp dport 2222 accept # 放行 Web 服务 tcp dport { 80, 443 } accept # 来自内网网段的流量放行 ip saddr 192.168.0.0/16 accept } chain output { type filter hook output priority filter; policy accept; } chain forward { type filter hook forward priority filter; policy drop; } }这段配置的含义是默认丢弃所有从外部进入的新连接,但允许回环、已经建立的连接、以及指定的服务端口。这里重点解释两个细节。
第一个是ct state established,related accept这一条。没有它,你会发现很多正常的响应包回不去,因为 TCP 连接是双向的,入站请求进来之后,出站的响应属于 established 状态,必须在入站方向放行。漏掉这条是新手最容易犯的错,表现为“服务明明在监听,但就是连不上”。
第二个是 ICMP 速率限制。很多人为了防 ping 直接禁掉所有 ICMP,但这样做其实会给排障带来麻烦,因为网络连通性测试都做不了。更合理的做法是限制频率,既能防止被用来做流量放大攻击,又保留了排障手段。
加载配置前,先确认本机已经有一条可用的放行规则,最好是从另一个会话或者另一台机器测试访问。用nft list ruleset查看当前规则,用nft -f /etc/nftables.conf重新加载。 我每次改防火墙都严格遵守一个原则:先开新窗口,测试还能不能连上,确认没问题再关掉旧窗口。不要给自己留“裸奔”的窗口期。
另外,如果机器上还跑着 Docker,Docker 会在 iptables 里动态插入自己的 DNAT 规则,firewalld 的规则经常被它绕过。这是一个老话题了,我的建议是尽量把对外暴露的容器端口控制在明确范围内,并且保证宿主机的防火墙策略仍然生效,不要让 Docker 自己把端口全部映射到公网。
4. 监控和审计:看出问题、抓住证据
4.1 日志是案发现场
加固做得再好,也不可能 100% 拦截所有攻击。所以在防御之外,监控和审计是另一根拐杖。
Linux 系统日志是排查安全问题的最佳起点。多数发行版把系统日志放在 /var/log/,和网络、登录相关的关键日志主要是这几个:
- /var/log/messages:系统整体事件,包含网络、服务状态等
- /var/log/secure:登录认证记录,SSH 爆破的案发现场
- /var/log/cron:定时任务执行日志,排查后门时会用到
- /var/log/firewalld 或 nftables 相关日志:防火墙拦截记录
如果你的系统使用 systemd,那么journalctl可以把很多分散的日志统一检索。我常用的几条是:
# 看最近 1 小时的所有日志 journalctl --since "1 hour ago" # 只看 SSH 服务的日志 journalctl -u sshd # 看所有失败的登录尝试 journalctl | grep "Failed password"不过要提醒一句,journald 的日志默认存在内存环形缓冲区里,重启后很大概率会丢失。生产环境建议把持久化打开,在 /etc/systemd/journald.conf 里设置Storage=persistent。
日志量大的机器,别忘了做日志轮转,否则/var/log会被一波日志打爆,磁盘满了以后服务会莫名挂掉。默认的 logrotate 配置一般够用,但如果你加大过日志级别,最好检查一下轮转策略和时间间隔。
4.2 Fail2ban 和 tcpdump:防与查
日志看多了以后,人不可能 24 小时盯着屏幕,所以自动化封禁工具就很有必要。Fail2ban 的基本逻辑,是去读日志文件,统计匹配规则的失败次数,超过阈值就把来源 IP 加入防火墙封禁名单,过一段时间再自动解除。
Fail2ban 的配置一般写在 /etc/fail2ban/jail.local 里,覆盖默认配置。我常用的一个 SSH 防护配置类似这样:
[sshd] enabled = true port = 2222 filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 3600 findtime = 600这几个参数的意义是:在 10 分钟内(findtime=600)同一来源 IP 的失败次数达到 5 次(maxretry=5),就封禁 3600 秒(bantime=3600)。数值可以根据业务情况调整,但不要设得太极端,比如 1 次失败就封几个小时,反而有可能把正常用户误伤。
除了防爆破,日常排障也离不开抓包工具。tcpdump 是服务器上排查网络流量最直接的工具,它能把网卡上跑的原始数据包dump出来,基于协议、端口、IP 做过滤分析。比如你想确认某个端口到底有没有外部流量进来:
tcpdump -i eth0 -n tcp port 8080 -c 100这条命令抓取网卡 eth0 上端口 8080 的 TCP 流量,-c 100表示抓 100 个包就自动停止。如果你想在发现外发流量异常时快速确认是不是被植入挖矿程序,可以按目的 IP 维度看连接数:
netstat -an | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20把抓到的数据和系统日志结合起来看,很快就能定位异常连接的目标地址、对应进程、以及进程启动方式。
5. 常见故障与排查经验速查表
5.1 加固完连不上服务器:救回访问的几条路
这个话题很尴尬但必须写,因为几乎每个人都至少经历一次:改完防火墙或者 SSH 配置,回车一敲,连接断了,然后心里一凉。
最优先的救援通道是带外管理,比如云控制台里的 VNC、救援模式。如果你用的是物理机,那就只能靠 IPMI 或者串口。这类途径不受操作系统网络栈影响,可以用来临时改配置。
如果带外也用不了,还有一个办法是趁防火墙规则还没持久化,直接重启机器。因为有些发行版的 nftables/iptables 规则没有使用 systemd 服务自动加载,重启后会回到空规则状态,这样你就有机会重新登录了。不过我强烈建议不要依赖这个办法,因为如果规则已经写成持久化配置,重启后照样加载。
关键还是预防:不要在生产环境高峰时段改防火墙;改之前先在本地保存一份已知良好的配置;永远保留一个“后门”窗口。现在我连远程服务器都是开两个SSH会话,一个专门用来改,另一个只用来保底测试,改完确认新会话能连接了才断开保底的那一个。
5.2 被暴力破解后的处置流程
如果你的 SSH 爆破已经成功了,也就是说日志里能看到“Accepted password”的记录,来源 IP 又不是你自己,那这台机器基本可以判定失陷。这时候最忌讳的是手忙脚乱地乱删文件,应该按流程来。
第一步,先隔离。把机器的外网访问断掉,或者至少在防火墙上把可疑来源 IP 封掉。已经建立的异常会话也要强制踢掉,用pkill -9 -u处理可疑账号或者直接结束相关进程。
第二步,收集证据。保存 /var/log/secure、/var/log/messages、/var/log/wtmp、/var/log/btmp 这些日志的副本,先不要清理,方便后面溯源。同时把当前所有登录会话列出来,查清楚哪些账号是新添加的。
第三步,清点后门。检查 cron 任务是否被写入异常脚本、/etc/rc.local 是否有可疑启动项、/root/.ssh/authorized_keys 里有没有多出来的公钥、/etc/passwd 里有没有新用户、有没有 UID 为 0 的账号。这五项我称之为“后门必查五项”。
第四步,重置所有凭据。包括 root 密码、普通用户密码、SSH 密钥、应用系统里的各种 token 和密钥。即使你只怀疑某个账号被攻破,也建议全部重置,因为你不知道攻击者到底掌握了多少信息。
最后一步才是恢复服务。把已知的漏洞补上、确认加固项都已生效,再逐步开放网络。遭受过入侵的机器,我心里是不太愿意继续信任的。如果有条件重建,我会更倾向于重装系统再恢复业务数据,而不是在原系统上反复修补。
5.3 主观判断“有点不对劲”:外发流量异常排查思路
很多时候你突然觉得机器变慢了、带宽占用异常、CPU 飙高,但本地各项业务都看着正常,这时候十有八九是外发流量出了问题。外发流量异常最常见的两个原因,一是被植入挖矿程序,二是被当成跳板攻击别人。
排查可以从三个维度并行。
先看进程维度,用top按 CPU 排序,找到占用高的进程。挖矿程序一般会伪装成看起来很正常的进程名,比如 kworker、systemd-update,所以要留意进程的实际路径是不是在 /tmp、/var/tmp、/dev/shm 这种可疑目录。
再看网络维度,用netstat -an看有没有大量连接指向同一批外部 IP。挖矿进程一般会连接矿池地址,特征比较明显。把可疑 IP 和进程 PID 对应起来,一步定位是哪个程序在发起连接。
最后看持久化维度,因为即使你杀掉进程,如果它被写成定时任务或者守护进程,重启后还会复活。所以必须查 cron、systemd timer、rc.local,把它的自动启动链路全部断掉才能彻底清理。
日常运维里,给外网出口流量监控做个基线也很有用。比如正常情况下这台机器每天的产生流量是多少,某天突然多了几十倍,那肯定有问题。大部分云厂商的控制台自带流量监控,做告警阈值并不难。这一步值得做,因为很多安全事件被发现,往往不是因为你主动看到了攻击,而是因为某个异常指标突破了你的心理底线。
自动化的告警会帮很大忙。我现在会定期写一个巡检脚本,把所有登录失败次数、开放端口、磁盘日志占用、最新可疑登录记录汇总成一份报告,发到工作群。看起来是很枯燥的重复劳动,但真出事的时候,这些东西能让你比别人早一步动手。安全工作的价值,往往就体现在这些不起眼的坚持里。
我自己这些年最大的体会就是:别追求什么花哨的安全方案,先把基础项做好,比什么都强。一个默认拒绝的防火墙、一套只走密钥的 SSH、一条能用的监控告警,这三样就能挡住绝大多数的自动化攻击。剩下的时间,拿来关注业务本身,而不是天天盯着安全日志焦虑。