一台新装的 Linux 服务器,默认往往跑着几十个甚至上百个 systemd 服务单元。这里面很大一部分你用不上,比如蓝牙、打印、图形界面相关的服务,它们却照样占着内存、占着端口、占着进程号,更重要的是——每一个监听端口都是一个可能被利用的入口。攻击者扫描一台机器时,第一步就是找开放端口,然后依次试探每个端口背后跑的是什么服务、版本是多少、有没有已知漏洞。你少开一个服务,攻击者就少一条路。
这篇文章要解决的问题是:如何系统地梳理一台服务器上"到底在跑什么",判断哪些服务该关、哪些必须留,然后安全地把它们关掉,并且在关闭之前做好防呆,避免把自己关在登录之外。内容分成三块——先讲攻击面这个概念和它怎么对应到具体的服务上,再讲一套"盘点、分类、关停、验证"的实操流程,最后给出常见的默认服务清单和取舍建议。
需要提前说明的是,关服务这件事本身不难,难在"判断"。同样一个 avahi-daemon,在一台对外提供内网发现的办公机上可能是必需,在一台纯 Web 服务器上就是纯多余的攻击面。所以本文不会武断地说"某个服务一定要关",而是教你怎么自己判断,最后再给一份常见的参考清单。文中命令默认以 root 或 sudo 身份执行。
一、先理解"攻击面"到底指什么
攻击面可以粗略地理解为:一台机器上所有"能被外部或本地触达、可能被利用"的东西的总和。它大致包含几个层次。第一层是网络攻击面,也就是监听在 TCP/UDP 端口上的服务,这是最直观也最危险的一层,因为全世界的扫描器都在盯着它们。第二层是本地攻击面,比如 SUID 程序、可写目录、计划任务、内核模块,攻击者在拿到低权限 shell 之后靠这些提权。第三层是账号与凭据攻击面,比如弱密码、过多可登录账号、sudo 免密配置。
本文聚焦的是第一层里最容易控制的那部分——不必要的网络监听服务和后台守护进程。原因很实际:这一层的收益最高、风险最低。关掉一个用不上的服务,几乎不会影响生产,但能实实在在地减少暴露面。而像 SUID 这类本地攻击面的清理,风险就高得多,容易把系统搞坏,要另开专门的话题。
把攻击面和端口对应起来看会更清楚。一个服务只要在监听,就等于在系统上开了一扇门。ss -tulnp 这条命令能把所有监听端口和对应的进程列出来,这是排查的起点。很多新手以为"我没装服务就没端口",实际上默认安装的系统往往已经开了 SSH、时钟同步、甚至一些厂商的监控代理,这些都要逐个确认。
二、盘点:这台机器到底在监听什么
动手关之前,必须先把现状摸清楚。这一步的目标是产出一张"端口—进程—服务—用途"的对应表。
# 1. 列出所有处于监听状态的 TCP/UDP 端口及其进程
ss -tulnp
# 只看 TCP,加上 -4 只看 IPv4,输出更清爽
ss -tlnp
# 老系统可能没有 ss,用 netstat 替代
netstat -tulnp
# 2. 列出所有正在运行的服务单元,只看 service 类型
systemctl list-units --type=service --state=running
# 3. 列出所有设为开机自启的服务(这是关键,运行中不代表开机自启)
systemctl list-unit-files --type=service | grep enabled
# 4. 找出监听端口对应的"真凶",比如看到 0.0.0.0:111
ss -tlnp | grep ':111'
第一条命令建议多跑几次,因为端口可能被临时进程占用。输出的 Local Address:Port 一列里,0.0.0.0:22 表示监听所有网卡,127.0.0.1:25 表示只监听本地回环。这个区别至关重要:只监听 127.0.0.1 的服务外部访问不到,攻击面小很多;监听 0.0.0.0 的才需要重点审视。
把盘点结果整理成一张表,看起来一目了然:
| 监听地址 | 常见端口 | 常见服务 | 风险评估 |
|---|---|---|---|
| 0.0.0.0 | 22 | SSH | 必需,但建议限制来源 IP |
| 0.0.0.0 | 80/443 | Nginx/Apache | 业务必需 |
| 0.0.0.0 | 3306 | MySQL/MariaDB | 危险,除非业务需要远程连接 |
| 0.0.0.0 | 6379 | Redis | 极危险,默认无密码,务必只监听本机 |
| 0.0.0.0 | 111 | rpcbind | 通常多余,可关 |
| 0.0.0.0 | 25 | Postfix 等 MTA | 多数 Web 服务器不需要,可关 |
| 127.0.0.1 | 各种 | 本地代理/监控 | 风险低,按需保留 |
这张表是经验总结,实际以你的 ss 输出为准。看到不认识的端口,先用进程号反查属于哪个包,再判断用途。
# 已知进程号 1234,查它属于哪个软件包
rpm -qf /proc/1234/exe # RHEL/CentOS/Rocky 系
dpkg -S $(readlink -f /proc/1234/exe) # Debian/Ubuntu 系
# 或者直接看进程的启动命令
ps -fp 1234
cat /proc/1234/cmdline | tr '\0' ' '
三、分类:关、留、看的三种处置
盘点完,把所有服务分成三类,处置策略完全不同。
第一类是业务必需,绝对不能碰。最典型的是 sshd。远程服务器上关掉 sshd 等于把自己锁在门外,除非你有带外管理(云控制台的 VNC、机房 KVM、IPMI)。Web 服务器上的 nginx/httpd、数据库、应用运行时的服务(如 php-fpm、Java 应用)也属于这一类。
第二类是明确无用,可以放心关。比如纯文本服务器上的图形界面相关服务(gdm、lightdm)、打印服务(cups)、蓝牙(bluetooth)、光盘自动挂载(udisks2 在某些场景)、avahi-daemon(除非你依赖 mDNS 做服务发现)。这些服务在服务器上基本没有存在意义。
第三类是视情况保留,需要判断。比如 postfix 只有在你确实要发系统邮件、接收 cron 邮件时才需要;rpcbind 只有 NFS 挂载才需要;chronyd 一般保留(时间同步很重要);firewalld 一般保留或替换为 nftables/iptables。
一个重要的纠结点是:关闭方式选哪种。这里有四种状态,含义差别很大:
| 操作 | 命令 | 立即生效 | 重启后 | 适用场景 |
|---|---|---|---|---|
| 停止 | systemctl stop x | 是 | 会自启 | 临时测试 |
| 禁用自启 | systemctl disable x | 否 | 不启动 | 配合 stop 使用 |
| 停止并禁用 | stop + disable | 是 | 不启动 | 永久关闭(推荐) |
| 屏蔽 | systemctl mask x | 是 | 不启动且不可启动 | 防止被依赖拉起 |
推荐的做法是 systemctl disable --now 服务名,它等价于先 stop 再 disable 一步到位。而当你发现某个服务被其他服务"依赖拉起"、怎么关都会重新出现时,用 mask 把它彻底屏蔽——mask 的原理是在 /etc/systemd/system/ 下建一个指向 /dev/null 的软链接,让系统无法加载它。
四、实操:安全关闭服务的完整流程
下面以关闭一个典型多余服务为例,走完整流程。假设我们要处理 rpcbind。
# 步骤1:确认它当前状态
systemctl status rpcbind
systemctl is-enabled rpcbind
# 步骤2:确认没有其他服务依赖它(关键防呆步骤)
systemctl list-dependencies --reverse rpcbind
# 如果输出里除了它自己还有别的服务,说明有关联,要慎重
# 步骤3:关闭并禁止自启
systemctl disable --now rpcbind
# 步骤4:验证——端口应该已释放,状态应为 inactive/dead
ss -tlnp | grep ':111'
systemctl is-active rpcbind
对 RHEL/CentOS/Rocky 和 Debian/Ubuntu 系,systemd 命令是一致的。但老系统(CentOS 6 等)用的是 sysvinit,命令不同,这里也一并给出,因为不少老机器还在跑:
# CentOS 6 / 老式 SysV 服务
service rpcbind status
chkconfig rpcbind off # 禁止开机自启
service rpcbind stop # 立即停止
chkconfig --list | grep on # 查看所有自启服务
如果在关闭过程中发现某个服务需要保留但不必对所有网卡开放,更好的做法不是关掉,而是收窄监听地址。很多服务的配置文件里可以指定 bind 地址。比如 MySQL 只允许本机连接:
# MySQL/MariaDB 配置文件通常在这些位置之一
# /etc/my.cnf(RHEL系) /etc/mysql/mysql.conf.d/mysqld.cnf(Debian系)
[mysqld]
bind-address = 127.0.0.1
Redis 同理,在 /etc/redis/redis.conf 或 /etc/redis.conf 中设置 bind 127.0.0.1 ::1,并确认 protected-mode yes。这类"收窄而非关闭"的处置,往往比直接关服务更符合业务需要,也把攻击面降到了最低。
五、防护:关服务时别把自己关在门外
这是本文最想强调的一点。安全加固翻车的经典案例,就是在远程服务器上关错了服务或改错了连接配置,导致 SSH 断连后再也连不上。规避方法有几条硬性纪律。
第一,动手前先确认自己有带外通道。云服务器要确认控制台 VNC 能登录、能用密码或密钥;物理机要确认有 KVM/IPMI。没有带外通道时,任何可能影响远程连接的改动都要极端谨慎。
第二,改 SSH 相关配置先测试而非直接重启。比如调整 /etc/ssh/sshd_config 后,先用 sshd -t 校验语法,再开一个新的终端会话验证能登录,确认无误后再退出老会话。绝不要做完改动就把唯一一个 SSH 会话关掉。
# 校验 sshd 配置语法,有错会报出来
sshd -t
# 不重启服务,用新会话验证;确认能登录后再重启
systemctl restart sshd # Debian/Ubuntu 服务名可能是 ssh
第三,防火墙规则用"定时回滚"兜底。很多云厂商的安全组和 iptables 规则改错了会立刻切断续连接,可以在改之前先挂一个定时任务,几分钟后自动恢复。
# 危险示例:给 iptables 加一条"5分钟后无论如何清空并恢复默认"的保险
# 先备份当前规则
iptables-save > /root/iptables.bak.$(date +%F)
# 挂一个5分钟后自动恢复的定时任务(用完记得删掉)
echo "iptables-restore < /root/iptables.bak.$(date +%F)" | at now + 5 minutes
如果服务器上没装 at,可以临时用 sleep 加后台任务替代。核心思想是:给危险操作留下自动回滚的时间窗,这样即使改错,也还有救。
风险点还有一处:关闭服务可能连带关闭依赖它的东西。比如关了 dbus 在某些发行版上会影响图形和管理工具;关了 systemd-logind 可能影响会话管理。所以 systemctl list-dependencies --reverse 这一步不能省。
常见问题
Q:systemctl disable 之后,服务重启还会自己起来吗?
一般不会。但要注意有的服务是被其他单元"唤醒"的(socket 激活、依赖关系),这种情况下要用 mask 彻底屏蔽,或者找出拉起它的父单元一并处理。
Q:怎么判断一个不认识的端口是不是恶意的?
先用 ss -tlnp 拿到进程 PID,再用 ls -l /proc/PID/exe 看可执行文件的真实路径。如果路径在 /tmp、/dev/shm、/var/tmp 这类临时目录,或者文件名看起来是随机字符串,基本可以判定是可疑程序,要按安全事件流程处理,而不是简单关掉。
Q:关掉服务会不会影响系统稳定性?
只关闭明确无用的服务(打印、蓝牙、图形、mDNS 这类)不会。越是底层的基础组件(dbus、logind、systemd 自身)越要谨慎,不确定就保留。
Q:云服务器的安全组和系统里关端口,是一回事吗?
不是。安全组是云平台层面的虚拟防火墙,在数据包到达系统之前就拦了;系统内的 firewalld/ufw 是主机层面的。两层都要配。安全组拦住了,系统里即使开着端口外部也访问不到,但本机之间仍可访问,所以两层不重复。
Q:怎么记录我改了哪些服务,方便以后回溯?
养成习惯:改动前 systemctl list-unit-files --type=service > /root/services_before.txt,改完后导出一份对比,并写一句说明为什么关这个服务、什么时候关的。运维交接时这份记录能救命。
总结
- 攻击面的第一层就是网络监听服务,关掉一个用不上的服务,收益高、风险低,是最划算的加固动作。
- 排查从
ss -tulnp和systemctl list-unit-files --type=service | grep enabled开始,重点区分"监听 0.0.0.0"和"只监听 127.0.0.1"。 - 关闭用
systemctl disable --now 服务名,被依赖拉起时用systemctl mask;关闭前务必跑systemctl list-dependencies --reverse检查依赖。 - 能用"收窄监听地址"(如 bind 到 127.0.0.1)解决的,优先收窄而不是关闭,对业务影响更小。
- 所有涉及 SSH、防火墙的改动,都要先验证语法、保留带外通道、设置自动回滚时间窗,避免把自己锁在服务器外。
- 变更留痕:改动前后各导出一次服务清单,写清楚原因和日期,方便回溯与交接。
企业QQ咨询




