ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SELinux、防火墙与NFS协同实战:从安全原理到故障排查

SELinux、防火墙与NFS协同实战:从安全原理到故障排查 1. SELinux并非非要关闭先弄清它到底在保护什么我刚接触Linux服务器运维那阵子遇到SELinux的第一反应和大多数人一样——直接改配置文件把它关掉。当时觉得这玩意儿就是个拦路虎明明服务配置没问题它就是不让访问关了整个世界都清净了。直到后来有一次生产环境出了点问题我才开始认真琢磨这个东西也才真正理解了为什么很多老工程师反复强调不要轻易sudo setenforce 0。先给对SELinux还不太熟的读者补个基础认知SELinux全称是Security-Enhanced Linux它由美国国家安全局发起开发核心作用是为Linux内核提供强制访问控制MACMandatory Access Control。传统的Linux权限模型是自主访问控制DAC也就是我们熟悉的rwx权限——文件属主可以随意修改自己文件的权限root用户基本拥有所有权限。而SELinux的模型是系统里所有进程和文件都被打上安全上下文标签进程能不能访问某个文件取决于标签之间是否匹配。这个决策过程发生在内核层面用户态程序无权干涉。这意味着就算你的web服务被攻击者拿下拿到了Apache进程的权限它想在服务器上乱翻文件也做不到因为SELinux会把不合规的访问全部拦下来。理解了这个原理你就会明白关键结论SELinux是纵深防御体系里非常靠后的一道防线。它防的不是有人拿root登录服务器乱删文件而是防某个服务进程被攻破后继续横向移动。所以当你问我该不该关闭SELinux时我的建议是——能不关就不关。尤其是面向公网提供服务器的场景你多留一层保险就少几分被一锅端的概率。1.1 三个模式怎么选Enforcing、Permissive与Disabled的真正区别SELinux有 Enforcing、Permissive、Disabled 三种模式很多人在网上找教程时被各种说法绕晕。其实用大白话解释就是Enforcing强制模式违反策略的访问一律拦截并记录审计日志。这是默认的、也是推荐生产环境使用的模式。Permissive宽容模式违反策略的访问不拦截但会记录下来。这个模式价值极大——它相当于模拟演练却不造成实际影响。Disabled关闭模式SELinux内核安全模块直接不加载连审计日志都不记。关闭后想重新开启不光要改配置文件还涉及到重启时重新打标签的问题。这里必须讲一个容易被忽略的点从Enforcing切换到Permissive或者从Permissive切回Enforcing都可以用setenforce即时切换不需要重启。但Disabled是一个单向门——一旦你把它设置成disabled重启系统再想恢复就不能只是改回enforcing因为系统里的文件标签可能已经乱了。新手搞不好还会碰到开机直接进不了系统的情况。所以我个人的经验是如果你想关闭SELinux尽量先调到permissive跑一段时间而不是直接disabled。跑几天之后如果发现系统一切正常、日志里也没有异常拦截再考虑彻底关闭也不迟。1.2 日常服务被拦截时怎样快速定位和处理如果说SELinux难其实难在出了问题还看不懂它在拦什么。最常见的现象是你配置好了一个Nginx或Samba服务从客户端访问时被拒绝而服务端的日志却完全正常没有报任何错误。这种时候有经验的工程师会条件反射地想到SELinux。排查SELinux拦截问题有四条路可以走我按优先级排序先看审计日志。CentOS 6时代看/var/log/audit/audit.logCentOS 7和8之后/var/log/messages里也会带上AVC关键字。执行grep AVC /var/log/audit/audit.log | tail -50能看到被拒的访问记录里面有进程、源上下文、目标上下文信息非常全。用sealert -a /var/log/audit/audit.log自动分析。这个工具会把原始日志翻译成人话比如你遇到了Nginx端口绑定失败这可能是SELinux阻止了访问尝试执行以下命令——它甚至会把解决方案直接列出来。如果系统里没装setroubleshoot就自己看关键字段typeAVC开头的行里scontext是发起方的安全上下文tcontext是目标方的安全上下文tclass是访问类型比如tcp_socket、file、dir。看到这两个上下文就能判断是哪个进程在访问什么东西时被拦了。临时验证法把SELinux先切到permissive模式再去触发一次访问如果服务立刻恢复正常那基本可以确定是SELinux策略拦截。最常用的快速放行工具是audit2allow。比如你查日志发现是httpd进程要访问一个自定义目录被拦可以这样自动生成并加载一个放行模块grep denied /var/log/audit/audit.log | audit2allow -M myhttpd semodule -i myhttpd.pp第一条命令会读取当前所有denied记录生成一个te格式的策略源文件myhttpd.te和一个编译好的模块myhttpd.pp第二条命令把模块加载进SELinux策略库。这相当于精准地告诉SELinux下次httpd的这个访问行为允许通过。不过这里要提醒一句audit2allow是把拦下的行为全部打包放行有些时候会放得太宽而且操作前要确认denied记录确实来自你自己正在排查的服务不然可能把别的问题也一并放行了给后续安全埋坑。1.3 te文件的书写规则与改了没编译进去的根因关于热搜词里那条selinux中te文件的详细书写规则和selinux权限修改以后没被编译进去是为什么这两个问题都很有代表性说明已经有不少人开始认真学习SELinux策略编写了而不只是停留在简单开关层面。一个te文件的基础组成大体是这样module myhttpd 1.0; require { type httpd_t; type etc_t; class file { read open getattr }; } allow httpd_t etc_t:file { read open getattr };核心逻辑就三块module声明模块名、require引入自己需要但不在本文件里定义的符号类型、类别等、allow规则定义主体对客体允许执行的操作。写规则时要注意class的定义file、dir、sock_file、tcp_socket都是不同的类别搞混了规则根本不会生效。而权限修改以后没被编译进去这个问题我起初也踩过坑。后来复盘出几个常见原因第一你改了te文件但没有重新编译模块并加载。te是源文件系统策略库里跑的是编译好的pp文件。修改te之后必须执行make -f /usr/share/selinux/devel/Makefile重新编译然后semodule -i重新安装。少一步都不行。第二模块有依赖冲突或加载顺序问题。执行semodule -l | grep myhttpd确认模块是否在列表里如果列表里有但实际行为没变化再检查是不是和系统自带的base策略冲突了——这种情况往往需要用semodule -d 模块名先禁用再启用试试。第三也是最容易被忽略的你改了自定义模块但默认策略里已经存在同类型的规则防火墙或者别的机制帮了你一把让你误以为规则生效了。我见过一个案例同事修改了sshd的SELinux规则后说没效果结果发现问题出在sshd_config里本身就允许了那个端口SELinux根本没拦自然谈不上修改生效。所以在确认没编译进去之前先确认这个访问本来是否被SELinux拦截。2. 防火墙策略从默认拒绝到精确放行的落地细节网络管理的第二个核心环节是防火墙。这里要讨论的不仅仅是Linux系统自带的iptables/firewalld还包括Windows系统上的防火墙规则、以及企业网络里常见硬件防火墙在配置思路上的一些通用逻辑。我见过太多新手一上来就关防火墙理由无非是开着防火墙这个端口不通、那个服务连不上。但关防火墙这件事相当于把自家大门的锁全拆了只为了省得每次都要用钥匙开门。正确做法应该是学会精确放行。2.1 firewalld与iptables怎么选以CentOS 7.3为分界线从CentOS 7开始firewalld成为默认的防火墙管理工具但底层包过滤规则其实还是iptables框架firewalld只是换了一套动态管理机制。很多老教程还在教iptables -A INPUT -p tcp --dport 80 -j ACCEPT这样直接操作iptables命令的方式这在CentOS 7.3之后的系统上有个问题系统启动时firewalld会把iptables规则覆盖掉你自己加的那些规则在下一次重启后很可能就没影了。我查资料时看到一条热搜词就是centos 7.3 防火墙说明很多人在这个版本上折腾过防火墙。以CentOS 7.3为例我要给出的建议是直接学firewalld就够了。它的核心命令很直观firewall-cmd --state # 查看防火墙状态 firewall-cmd --list-all # 查看当前所有规则 firewall-cmd --zonepublic --add-port8080/tcp --permanent # 放行8080端口 firewall-cmd --zonepublic --add-servicehttp --permanent # 放行http服务 firewall-cmd --reload # 重载配置加了--permanent表示写入持久化配置否则只在当前运行时生效重启就丢。这里的zone区域是个很有意思的设计不同网络接口可以挂到不同区域下每个区域定义一套不同的信任等级。比如public区域默认流量较为保守适合分配在连接外网的接口上internal或trusted区域则适合内网接口。你完全可以做到内网网卡全放行、外网网卡只放行80和443对外的攻击面就大幅缩小了。2.2 黑白名单的落地方式从屏蔽某个程序联网说起热搜词里有一条很典型的场景防火墙建立出入站规则屏蔽acrobat.exe联网。这其实是Windows防火墙的典型应用场景——某个软件总是偷偷联网检查更新或者上传使用数据而你又不想卸载它那就用防火墙规则把它封掉。在Windows上操作路径是控制面板 - Windows Defender 防火墙 - 高级设置 - 出站规则然后新建规则规则类型选程序浏览选择acrobat.exe选择阻止连接。这里的关键细节有两个一是入站和出站必须分开看。很多软件连不上网时你只检查入站规则但其实问题是出站被拦了反过来也一样。如果你要彻底限制某软件联网入站和出站规则最好都建一遍。二是出站规则默认策略是允许还是阻止。Windows默认出站放行、入站除明确允许外一律阻止。所以对于大多数刚装完系统就被各种软件弹窗骚扰的场景新建出站阻止规则是最快的但如果你的机器是拿来当服务器的我强烈建议你把出站也改为默认阻止按需放行这样就算机器被装了后门后门程序想往外传数据也传不出去。改法是在Windows防火墙高级设置属性里把出站连接改成阻止然后逐一放行需要的应用。回到Linux这边黑名单思路也可以实现。比如你想封掉某个恶意IP对SSH端口的暴力破解可以这样firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.100 port port22 protocoltcp reject富规则rich rule是firewalld的一个高级功能支持按源地址、目的地址、端口、协议组合出很精细的放行或拒绝策略。不想封单个IP只想限制并发连接数防暴力破解的话还可以结合fail2ban这类工具它会自动扫描日志、把多次登录失败的IP临时拉黑——防火墙策略配合自动化工具才算是完整的防御闭环。2.3 Windows与Linux防火墙日志被静默丢弃的流量怎么查另一个让我觉得值得拿出来讲的问题是热搜里提到的windows server 2016 防火墙日志如何开启。防火墙默认丢弃流量时客户端拿到的表现常常是连接超时而不是连接被拒绝——这一点是故意的不回应可以让扫描方无法判断端口到底开着还是被防火墙拦了。但在排障的时候超时和拒绝的区别会直接影响你的排查方向如果是REJECT拒绝说明数据包已经到了目标主机是有程序或防火墙在主动拒绝如果是DROP丢弃则要优先确认是不是防火墙规则把包丢了。开启Windows防火墙日志的方法是防火墙高级设置 - 右键属性 - 选择配置文件域/专用/公用 - 日志 - 自定义 - 选择保存路径、大小限制等。默认历史日志记录被丢弃的数据包和成功的连接都是关着的。日志文件默认叫%systemroot%\system32\LogFiles\Firewall\pfirewall.log你可以用记事本或文本分析工具打开每行记录包含时间、动作ALLOW/BLOCK、协议、源IP和目的IP、源端口和目的端口等字段。Linux下的排查思路同理。CentOS 7系统上firewalld的日志默认记录在/var/log/firewalld但更底层的信息要看内核的dmesg或journalctl -k -f很多防火墙drop的流量会在这里出现DROP字样的记录。要明确看到iptables的命中次数可以执行iptables -L -nv --line-numbers-v会显示出每个规则累计匹配的包数和字节数。如果某条规则的计数在增长说明流量确实被它拦住了如果计数不动说明流量可能在更早的链上被处理了或者根本没到达这台机器。这个从底层看计数器的习惯是区分防火墙拦了和网络路径不通的最可靠方法。3. 共享存储实战NFS从部署到排障的完整链路网络管理的第三个模块是共享存储。为什么要把共享存储和SELinux、防火墙放在一起讲因为这三者简直是联合作案的黄金搭档——你配置了一个NFS共享客户端挂载不上最常见的三个原因正好就是SELinux拦截、防火墙没放行、exports配置写错。这三道坎互相叠加的时候新手往往会被折腾得怀疑人生。共享存储的方案很多从最朴素的NFS、Samba到分布式存储如Ceph、GlusterFS再到商业的SAN/NAS各有用武之地。对于中小型环境、或者只是实验性质的内部共享需求NFS依然是性价比最高、最易于掌控的方案。它基于RPC协议Linux、macOS、很多NAS设备都能直接支持。3.1 服务端的安装与exports文件核心规则NFS服务端在CentOS上安装很简单yum install -y nfs-utils rpcbind systemctl enable --now rpcbind nfs-server注意先把rpcbind起起来因为NFS依赖rpcbind来做端口映射端口没映射好客户端连不上nfs服务端的时候报错会非常诡异。真正决定共享行为的是/etc/exports文件。我见过不少新手直接照抄网上配置但没搞明白每一列的含义结果就是各种诡异的权限问题。一个标准条目长这样/data/share 192.168.10.0/24(rw,sync,no_root_squash,no_all_squash) 10.0.0.1(ro)逐个解释一下这些参数的坑rw/ro读写 / 只读。这里要注意即便你写了rw如果客户端文件系统挂载选项用了ro最终还是会以最严格的权限为准。sync数据同步写入磁盘。很多人为了性能会改成async但async在主机宕机时有丢数据的风险生产环境我一般建议用sync除非你明确知道自己在做什么。root_squash/no_root_squash这是新手最容易踩的坑。NFS的设计哲学是客户端root不能对服务端为所欲为所以默认会把客户端root压缩成匿名用户nobody这叫root_squash。如果关闭它写no_root_squash那么客户端root就等同于服务端root——这个选项在部分旧教程里被推荐觉得方便但这在生产环境是个安全大坑。我一般要求内网场景也必须保留默认的root_squash。no_all_squash保留普通用户的UID/GID。这里的保留指的是客户端用户和服务端用户按UID匹配比如UID 1000的用户无论客户端用户名是什么以服务端UID 1000的权限来算。这里又牵出另一个常见问题两边的UID必须一致否则客户端文件属主会在服务端显示成乱码或nobody。如果你没法保证多台机器UID统一可以考虑NFSv4的idmapd来做用户名映射但那又是另一套配置了。磁盘空间规划上有个很实在的建议共享目录所在的文件系统最好单独分区或使用LVM预留足够扩展空间。别到时候想要扩容结果发现和系统盘共用一个分区一扩容就要动根分区风险就大了。3.2 客户端挂载与自动挂载fstab的正确写法服务端配置好后客户端只需要安装nfs-utils并执行mount命令yum install -y nfs-utils mount -t nfs 192.168.10.5:/data/share /mnt/share能手工挂上说明基本链路通了。但服务器重启之后手工挂载会失效这时就要写/etc/fstab做开机自动挂载。fstab里NFS的写法有不少讲究192.168.10.5:/data/share /mnt/share nfs4 defaults,_netdev,noatime,nofail 0 0_netdev这个选项非常关键。它告诉系统这是网络设备必须等网络就绪后再挂载。不加这个选项开机时系统可能会在网络还没起来时就尝试挂载结果挂载失败更糟的情况是系统起不来因为挂载超时。nofail即使挂载失败也不要阻塞开机流程。这对于那些共享存储只是辅助、不是必须的场景特别友好——服务器不会因为远端NFS没起来就卡死。noatime减少每次读取文件时的元数据更新时间戳操作对网络文件系统来说能减少不少不必要的网络IO提升性能。验证fstab是否写对的方法是不重启直接测试先umount /mnt/share然后执行mount -a如果没有任何报错并且df能看到挂载这个fstab就基本没问题。3.3 权限、SELinux与防火墙三方交叉的排障顺序很多NFS挂载问题的难点不在配置本身而在多个原因叠加在一起。我遇到过的最典型情况客户端可以ping通服务端showmount能看到共享列表但实际访问目录时权限被拒。排查时我建议按下面这个顺序来能最快定位问题。第一步先确认网络层通不通rpcinfo -p 服务端IP能看到RPC服务列表即可。第二步确认服务端exports生效了没有exportfs -v查看当前导出的共享和参数。第三步查看客户端的挂载选项和服务端的export参数是否匹配特别是rw和no_root_squash这些关键参数。第四步看SELinux。这里有个历史坑在RHEL/CentOS 7和8上SELinux针对NFS的布尔值配置和旧版本不同。我建议执行getsebool -a | grep nfs重点关注nfs_export_all_rw和nfs_export_all_ro如果这两个是off客户端的读写就会被拦截。设置成on的方法setsebool -P nfs_export_all_rw on-P参数表示写入持久化配置重启后依然生效。第五步查防火墙。NFS用到的端口不固定除了2049是NFS本身之外还需要放行rpcbind的111端口以及mountd、rquotad等辅助服务的端口。如果不想让这些端口动态变动最省心的做法是在服务端固定端口然后在/etc/exports同级配置或防火墙里做端口映射再把固定端口一次性放行。不过在小规模内网环境下更快的做法是直接放行参与NFS的网段firewall-cmd --permanent --add-servicenfs --add-servicerpc-bind --add-servicemountd firewall-cmd --reload这三条分别是NFS本身、rpcbind、mountd的服务放行基本上把常见场景都覆盖了。如果加完之后还有问题再用前面提到的iptables -L -nv --line-numbers看计数器确认流量是否被拦。4. 三者在真实网络环境中的协同与边界前面三节分别讲了SELinux、防火墙、共享存储的独立知识但实际工作中这三者从来都是交织在一起的。这一节我用一个完整的项目场景来演示它们怎么配合再整理一个常见症状-原因-解决方案对照表方便读者遇到问题时快速对号入座。4.1 一个文件服务器从零到对外服务的完整流程场景假设企业内网有一台CentOS 7.9服务器IP是192.168.20.10需要把它上面的/srv/files目录通过NFS共享给192.168.20.0/24网段的Linux客户端客户端需要可读写同时提供Nginx服务让办公网的同事通过HTTP下载文件。要求是保持SELinux开启、防火墙只放行必要端口。第一步创建目录并写入测试文件mkdir -p /srv/files echo test from server /srv/files/readme.txt第二步配置/etc/exports/srv/files 192.168.20.0/24(rw,sync)第三部导出共享并确认exportfs -r exportfs -v第四步配置SELinux。NFS导出本身需要打开nfs_export_all_rw布尔值setsebool -P nfs_export_all_rw on而Nginx要访问/srv/files这个目录还需要给httpd_t类型加上对目标目录的访问权限。如果/srv/files目录的安全上下文是default_tNginx默认是读不了的需要按前面说的方法用audit2allow生成放行模块或者直接把目录类型改成httpd_sys_content_tsemanage fcontext -a -t httpd_sys_content_t /srv/files(/.*)? restorecon -Rv /srv/files第一种做法是动态生成和加载策略模块第二种是直接修改文件的安全上下文标签。两种方式都能解决Nginx读取文件的问题第一种更灵活第二种更直观看你的维护习惯选。第五步防火墙放行。NFS要放行rpc-bind、mountd、nfs三个服务Nginx要放行http和httpsfirewall-cmd --permanent --add-service{nfs,rpc-bind,mountd,http,https} firewall-cmd --reload第六步客户端挂载测试mount -t nfs 192.168.20.10:/srv/files /mnt/files整个过程走下来你会发现如果哪一步没做故障特征是完全不一样的不做第二步客户端showmount -e正常但挂载时报Permission denied不做第四步客户端挂载成功了但Nginx访问403或者NFS客户端从挂载目录写文件失败不做第五步客户端直接连不上或挂载超时。这种症状差异反过来也能帮我们快速定位问题。4.2 常见报错与排查参照表我把自己这些年遇到的与这三个模块相关的典型故障整理成了一个表放在这里方便查阅故障现象可能原因排查方向NFS客户端挂载时卡住不动最终报Connection timed out防火墙未放行NFS相关服务检查服务端firewalld是否放行nfs/rpc-bind/mountdNFS能挂载但写入文件时报Permission deniedexports参数为ro或SELinux布尔值未开启检查exports里的rw参数执行getsebool -a | grep nfsHTTP服务访问共享目录返回403/srv/files目录安全上下文不是httpd_sys_content_t执行ls -Z /srv/files查看上下文类型客户端挂载后看不到文件属主显示nobody服务端和客户端的UID/GID不一致idmapd是否运行同步两端UIDSELinux策略模块已加载但规则未生效模块加载顺序冲突或策略未编译semodule -l确认模块存在必要时用semodule -d禁用再启用SSH端口不通但firewalld已放行防火墙区域可能不对流量走了另一个zonefirewall-cmd --get-active-zones确认接口归属的区域Windows防火墙开启了出站阻止软件连不上网出站规则把软件的某个组件拦了查看防火墙日志日志定位被拦截的程序的完整路径和端口共享目录能读不能写且服务端没开SELinuxNFS服务端export参数正确但客户端挂载时用了只读mount | grep /mnt/files查看挂载参数这张表覆盖了SELinux、NFS、防火墙三者叠加的绝大多数典型场景。实际排障时有一个额外的建议一次只改一个变量。如果你同时改了防火墙、selinux、exports三处配置但问题依旧你根本不知道是哪一步没生效。正确做法是逐个修改逐个验证每次只动一个配置项并测试走几步下来问题就定位得非常清晰了。根据我个人的实操经验SELinux和防火墙这两个模块真正花时间去理解之后它们不但不是障碍反而是你排查问题时的好朋友。SELinux的AVC日志会把访问路径写得明明白白防火墙的计数器会告诉你流量去了哪里它们给的不是阻碍而是证据。下一次服务访问异常的时候先别急着关SELinux、关防火墙试着打开这些证据看一眼。你会发现问题往往比你想象的更简单而你学到的内容也会比直接关闭它们之后的假正常更有价值。最后再分享一个小技巧在所有可能被拦截的服务调试场景里先开一个终端窗口实时盯着日志变化SELinux看audit日志防火墙看iptables计数器再在另一个窗口操作服务故障时刻的日志就是最精准的排障线索。
返回列表