西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页 → 安全中心 → 系统安全 → 开启 SELinux 后 sshd 连不上:我改了5处策略才恢复

开启 SELinux 后 sshd 连不上:我改了5处策略才恢复

相关软件相关文章发表评论 来源:本站整理时间:2026/11/4 7:31:48字体大小:A-A+

作者:xiix点击:0次评论:0次标签: SELinux AppArmor 强制访问控制 audit.log Linux安全策略

  • 类型:行业软件大小:31.5M语言:中文 评分:10.0
  • 标签:
立即下载

凌晨两点,客户急电来说 SSH 连不上他自己的云主机,密码没错、端口没改、防火墙也放行了。我第一反应是云商那边限流或者宿主机抽风,telnet 22 端口却是通的TCP 握手成功,说明网络没问题,但服务端不回 SSH-2.0 握手包,直接把连接掐掉了。这种「端口通但服务不响应」的场面,我后来在 /var/log/audit/audit.log 里找到了真凶:type=AVC msg=audit(...): avc: denied { connectto } for pid=1234 comm="sshd" path="/run/sshd.pid" scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:init_t:s0。也就是说,SELinux 把 sshd 自己给拦了。那台机器用的是某云镜像的默认策略,我为了图省事,只在 /etc/selinux/config 里把 SELINUX=enforcing 改成了 permissive,然后 reboot,就以为完事了。结果机器重启后,新装的 fail2ban、自编的 logrotate 规则、放在 /opt 下的业务脚本,全都在策略里没有对应的标签,一个一个被拒。

服务器访问控制多层防护环等距插画

AppArmor 路径规则与文件权限标签示意

运维排查 sshd 连接被拒绝的终端场景

SELinux 强制访问控制下的服务器分层安全策略示意

如果你也是自己管云服务器、主机上跑着各种自建服务,这台机器的选型值得先看一眼:lightlayer(服务器与主机) 提供的是可以完全自定义系统策略的裸机与 VPS,SELinux、AppArmor 这类强制访问控制在上面能从头测到尾,而不是被厂商的镜像策略锁死。真正要落地权限管控、想把日志收口的场景,机器本身就是你策略的第一个边界。

真到了要验证策略的场景,你可能需要几台不同系统、不同节点的机器做对照Debian 系、RHEL 系各一台,或者同一条策略在澳洲和欧洲节点各跑一遍看差异。这类多地区、多系统的对照测试,可以看看 wepc(服务器与主机) 这类同时提供多个地区节点 VPS 的商家,一次把测试矩阵铺开,比来回换机器省事得多。

一、MAC 和 DAC 的区别,先搞清楚你在防谁

很多人一说 SELinux 就觉得是「防火墙的加强版」,其实不是。传统的 DAC(自主访问控制)是 Linux 默认那套:进程的属主是 root,那它就能读任何文件,属主是你,你就只能动你自己的文件。DAC 的判断只看「你是谁」,不看「你想干什么」root 跑着的任何一个脚本被利用了,它就有全盘读写权限,这叫「权限继承」。

MAC(强制访问控制)多了一层:除了「你是谁」,还要看「你想访问的东西是什么类型」。SELinux 给每个进程打一个安全上下文标签(比如 system_u:system_r:sshd_t:s0),给每个文件也打一个(比如 system_u:object_r:etc_t:s0),然后拿一张策略表去比对:sshd_t 这个域,允许对 etc_t 做 read 吗?允许吗?表里没有的组合,默认一律拒绝。这套逻辑的好处是:哪怕 sshd 被塞进一个 root 权限的 webshell,它手里那个域标签依然没变,能干的事情依然被策略框死。所以 SELinux 常被描述成「即使 root 被攻破,损失也被限制在一个域里」。

AppArmor 是另一套实现思路,Debian/Ubuntu 上默认用的就是它。它不做标签,而是给每个程序配一个路径 + 权限的配置文件,比如 /etc/apparmor.d/usr.sbin.sshd,里面写着「允许读 /etc/ssh、允许写 /run/sshd.pid」。直观、易懂、和 SELinux 的策略语言完全不是一个世界。两者不能同时管一个进程,选一个就行。

二、先分清三种模式,不要上来就关掉

getenforce 一下就出来了:

Enforcing 违规就拦,日志里记 denied,这是生产该有的状态。
Permissive 只记录不拦,日志里还是 denied,这是调试模式。
Disabled 完全不加载,需要改配置文件并重启。

我那次犯的错,是把 permissive 当成了「关掉」。语义完全相反:permissive 下业务照跑不误,但它一直在默默记录所有违规,等你哪天改成 enforcing 再重启,这些积攒的违规一次性全部变成拦截。这正是很多人在生产上「一开 enforcing 就大面积炸锅」的真实原因问题不在 enforcing,是之前 permissive 攒下的问题从来没解决过。

顺带一句,把 SELINUX=disabled 写进 /etc/selinux/config 是彻底关掉,代价是标签可能残留在文件系统上,重新打开时 semanage 会报一堆 fix 建议,比较麻烦。真想长期关,也不推荐用这种方式。

三、查日志:audit.log 才是真正的入口

很多人第一反应是去看 /var/log/messages 或者 dmesg,里面什么都没有,因为 SELinux 的拒绝记录走的是独立通道:/var/log/audit/audit.log。注意 auditd 没起来的时候,内核会用 printk 把 AVC 消息打到 dmesg,很多人以为 dmesg 里也没有,其实是没重启过日志服务。

日志长这样,重点看三段:scontext 是发起方(进程)的标签,tcontext 是被访问对象(文件)的标签,{ connectto } 是被拒绝的权限。

avc: denied { write } for pid=2345 comm="mysqld" name="data.db" scontext=system_u:system_r:mysqld_t:s0 tcontext=unconfined_u:object_r:default_t:s0

读这条日志的顺序是:先看 comm 确认是哪个程序被拒(mysqld),再看 { write } 知道它想干什么(想写),最后看 tcontext 的 default_t这才是问题的核心。default_t 是「默认类型」,意味着这个文件是手工创建的、没被任何策略文件贴过标签的流浪文件。把业务数据放在 /data 这种没人管过的目录里,又不跑 semanage fcontext 打标签,在 SELinux 眼里就是一群无身份的文件。

查日志有几个提速技巧:ausearch -m avc -ts recent 直接看最近的 AVC 记录;sealert -a /var/log/audit/audit.log 会把生硬的 AVC 语句翻译成人话,还给出建议命令,这是排查时最好用的一个工具。另外要确认 auditd 真的在跑,systemctl status auditd 一看便知,很多机器上它是 disabled 状态,SELinux 拒绝了你却完全看不见记录,这才是最费时间的坑。

四、五处策略,我到底改了哪五处

回到那台机器,我把 permissive 攒下的违规一条条捞出来,按需打标签、放行,前后改了五处,下面按踩坑顺序说。

第一处:给业务目录打标签,而不是 blanket 放行。 问题出在 /opt/app/data 目录,tcontext 是 default_t。正确做法是给它一个专门的类型,而不是图省事写成 allow mysqld_t default_t:file *; 这种一放就全开的规则那等于把 SELinux 辛苦建立的边界自己拆了。具体操作用 semanage fcontext -a -t mysqld_db_t "/opt/app/data(/.*)?",然后 restorecon -Rv /opt/app/data 让文件上实际生效。这一步是最容易做错的地方:semanage fcontext 只是往策略库里写规则,不改磁盘上文件的现有标签,必须配 restorecon 才能落地。

第二处:端口和 socket 文件的标签。 denied { connectto } 指向 /run/sshd.pid 这类运行时文件。/run 是 tmpfs,重启就没了,靠 semanage 打标签会被 restorecon 恢复默认值,得用 semanage fcontext -a -t sshd_var_run_t "/run/sshd\.pid" 写规则,同时给 /etc/selinux/targeted/ 下的默认规则补一条。这个坑我踩了两次才明白:restorecon 之后标签又被打回去了,才意识到 tmpfs 上的持久规则要走另一套机制。

第三处:目录权限和 SELinux 标签,两套系统要一起改。 这是最容易漏的。文件权限上 chmod 600 只挡普通用户,但 SELinux 的判定是独立的它只看标签和域,chmod 再严格它也照拦不误;反过来 semanage 放行了域到 default_t 的访问,chmod 000 的文件照样读不了。两个都过,才算真的能访问。养成习惯:改完策略用 ls -Z 看标签、用 namei -l 看路径上每一级目录的权限。

第四处:容器和自定义端口。 我有个服务监听在 18080,SELinux 里非标准端口需要显式声明端口类型:semanage port -a -t http_port_t -p tcp 18080。否则就会出现「进程明明在 listen,客户端就是连不上,firewalld 也放行了」的诡异现象firewalld 管的是网络包,SELinux 管的是进程能不能绑定这个端口,两个是完全独立的开关。同理,容器跑在 Docker 上,容器内挂载的目录标签也会带来额外的坑,:z、:Z 这两个挂载后缀就是在做标签重打。

第五处:别忘了写进配置文件。 我改完策略重启一次,全好了,结果下个月 yum update 或者 dnf update 之后策略被覆盖,又回到 denied。原因是直接用 semanage 或者往 *.te 里改的东西,如果没有 semodule 安装或者写进持久化的地方,重启就没了。稳妥做法是把定制策略写成 .te 文件、checkmodule 编译、semodule -i 安装,这样它就进了系统的模块库,升级也不会丢。

五、给新服务器的正确姿势

踩完这一圈,我给团队定了三条规矩,贴在工位上:

其一,新机器装完第一件事是 auditd 开机自启,不然出了问题连日志都没有;其二,上线前用 sestatus 确认模式是 enforcing,别在 permissive 上自欺欺人;其三,permissive 只在调试窗口里用,调完立刻改回 enforcing,然后观察 24 小时的 AVC 日志,把违规清零。

另外几条命令值得存进肌肉记忆:sestatus 看全局状态,ls -Z 文件 看标签,chcon -t 类型 文件 临时改标签(重启失效,别用它做持久化),restorecon -Rv 路径 把标签恢复成策略里的默认值,semanage fcontext -l 看当前所有持久化路径规则。用 semanage 之前记得先装 policycoreutils-python-utils,不然命令直接 not found。

说实话,SELinux 不是「装上就安全」的东西,它的价值在于:当你某个服务被拿下、某个进程被提权时,攻击者能做的事情有明确的边界。要不要为这份边界付出一晚上的配置成本,取决于你的机器上跑着什么、要防的是谁。如果就是一台跑着几个静态站点的轻量机器,说实话,装不装差别不大;但如果上面跑着数据库、有多用户、有对外接口,那就值得花这个时间把它配对。

那台机器后来我重装了一遍系统,这次从一开始就把 /opt 下的业务目录类型规划好,把 auditd 加进开机启动清单,把三条规矩写进了运维文档。两个月过去,再没出现过策略拦人的告警。真正让我记住的,不是那五处策略怎么改,而是「问题被拦下来的时候,日志得在」这件事没有 audit.log 的 SELinux,和没开是一样的。

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶顶
    • 1 难过难过
    • 5 囧囧
    • 3 围观围观
    • 2 无聊无聊

    发表评论

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲