在CentOS服务器运维中,网络访问控制列表(ACL)管理是经常被低估但极其关键的一环。很多运维人员习惯用iptables或firewalld做包过滤,但当需要针对单个用户、单个目录或已建立的连接做更细粒度控制时,内核自带的ACL机制配合TCP Wrapper和文件系统ACL往往能解决防火墙规则难以覆盖的场景。这篇文章不绕弯子,直接讲CentOS环境下网络访问控制列表的实际管理方法、配置要点和排错思路。
理解CentOS中两类完全不同的ACL
首先要明确一个容易混淆的点:CentOS里的“ACL”至少涉及两个层面。一是网络层的访问控制,主要通过TCP Wrapper(/etc/hosts.allow和/etc/hosts.deny)以及iptables/firewalld的规则链实现;二是文件系统层的POSIX ACL,通过setfacl和getfacl管理,控制哪些用户或组能读写执行特定文件或目录。很多文章把这两者混为一谈,导致实际部署时思路不清。网络访问控制列表管理,重点在网络层,但文件系统ACL在运维场景中常与网络服务权限联动,比如限制某个服务账号只能访问特定日志目录,所以本文会同时覆盖这两个维度。
TCP Wrapper:轻量级网络ACL的第一道闸
TCP Wrapper是CentOS中默认启用的主机级访问控制机制,它通过libwrap库对xinetd管理的服务以及编译时支持libwrap的网络服务生效。判断一个服务是否受TCP Wrapper控制,可以用ldd命令查看是否链接了libwrap:
ldd /usr/sbin/sshd | grep libwrap
如果输出包含libwrap.so,说明该服务受/etc/hosts.allow和/etc/hosts.deny控制。配置规则时,CentOS按照先检查hosts.allow再检查hosts.deny的顺序执行,匹配到allow就放行,匹配到deny就拒绝,两个文件都不匹配则默认允许。所以最常见的加固策略是在hosts.deny中写一条ALL: ALL拒绝所有,然后在hosts.allow中精确放行需要访问的IP或网段。
# /etc/hosts.deny ALL: ALL # /etc/hosts.allow sshd: 192.168.10.0/24 10.0.0.5 vsftpd: 172.16.0.0/16
这种配置方式简单直接,但要注意:TCP Wrapper只对支持libwrap的服务有效,像Nginx、MySQL这类默认不链接libwrap的程序,写了规则也不生效。另外,hosts.allow支持通配符和EXCEPT语法,例如允许整个网段但排除某个IP:
sshd: 192.168.10.0/24 EXCEPT 192.168.10.88
运维中经常遇到的问题是修改了hosts.allow或hosts.deny后规则不生效。这通常是因为服务进程在启动时只读取一次配置,修改后需要重启对应服务,或者发送HUP信号让进程重新加载。对于sshd,可以执行systemctl reload sshd。另外,如果服务是通过systemd管理的,还要确认服务单元文件中没有设置PrivateTmp或ProtectSystem等限制导致libwrap无法读取配置。
iptables与firewalld:内核级网络ACL的精细控制
TCP Wrapper只能做主机级允许或拒绝,无法基于端口、协议、连接状态做精细控制。CentOS 7及之后版本默认使用firewalld作为前端管理工具,底层仍然调用iptables或nftables。对于网络访问控制列表管理,firewalld的rich rules和direct rules提供了比单纯zone更灵活的表达能力。
一个典型场景是:只允许特定IP访问MySQL的3306端口,同时限制连接速率。用firewalld的rich rule可以这样写:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.0/24" port port="3306" protocol="tcp" accept' firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" port port="3306" protocol="tcp" reject' firewall-cmd --reload
但rich rule在表达复杂逻辑时不够直观,比如需要同时匹配多个端口、多个协议或者基于连接状态做限制。这时候direct rules更接近传统iptables语法,适合有iptables经验的运维人员。例如限制单个IP对80端口的并发连接数:
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 80 -m connlimit --connlimit-above 20 -j REJECT --reject-with tcp-reset firewall-cmd --reload
这里要注意direct rules的优先级和持久化问题。firewalld的direct规则在reload后仍然保留,但如果直接使用iptables命令添加规则,重启后就会丢失。所以生产环境要么统一用firewalld管理,要么用iptables-save保存规则到/etc/sysconfig/iptables(CentOS 6风格)或者通过systemd服务加载自定义脚本。很多运维事故就是因为混用firewalld和iptables命令,导致规则互相覆盖或重启后丢失。
nftables:新一代内核ACL框架的落地实践
CentOS 8及后续版本中,nftables逐渐取代iptables成为默认的内核包过滤框架。nftables的语法更简洁,规则集管理更高效,而且原生支持集合、映射和字典,适合大规模网络ACL场景。例如定义一个IP集合,然后统一放行:
nft add table inet filter
nft add set inet filter admin_ips { type ipv4_addr\; }
nft add element inet filter admin_ips { 192.168.10.5, 10.0.0.8 }
nft add chain inet filter input { type filter hook input priority 0\; policy drop\; }
nft add rule inet filter input ip saddr @admin_ips accept
nftables的优势在于规则多了之后性能下降不明显,而且可以用nft -f命令从文件批量加载规则,便于版本化管理。但要注意,CentOS 7上nftables和iptables共存时存在兼容层问题,如果系统同时运行firewalld和手动nft命令,可能产生规则冲突。建议在CentOS 7上要么完全使用firewalld,要么完全切换到nftables并禁用firewalld。CentOS 8及以上版本则可以直接用nftables作为主要工具。
文件系统ACL:网络服务权限的底层支撑
网络访问控制列表管理不仅仅是IP和端口的限制,还涉及服务运行时的文件访问权限。CentOS默认挂载的文件系统大多支持POSIX ACL,但ext4和xfs需要确认挂载选项。可以用mount | grep acl查看,如果没有acl选项,需要在/etc/fstab中加上acl并重新挂载。
实际运维中,一个常见需求是:Web服务器运行用户是nginx,但网站目录属于开发者用户dev,需要让nginx只读访问,同时允许dev组完全控制。用setfacl可以精确实现:
setfacl -m u:nginx:rx /var/www/site setfacl -m g:dev:rwx /var/www/site setfacl -m d:u:nginx:rx /var/www/site setfacl -m d:g:dev:rwx /var/www/site
这里d:前缀表示默认ACL,对目录下新建的文件和子目录自动继承。很多运维人员忽略了默认ACL,导致新上传的文件权限不对,服务无法读取。另外,getfacl可以查看ACL条目,ls -l显示的权限位末尾有加号表示该文件设置了扩展ACL。注意,chmod修改文件权限时会同时影响ACL中的mask条目,如果mask被收紧,原本ACL中赋予的权限也会被限制,这是排错时经常忽略的点。
网络ACL与文件系统ACL的联动场景
单独看网络ACL或文件系统ACL都容易理解,但在真实业务中两者需要配合。例如一个SFTP服务器,要求不同用户只能访问自己的目录,且只能从特定IP登录。网络层用TCP Wrapper或firewalld限制来源IP,文件系统层用ACL限制用户目录访问。如果只配置了网络ACL,用户登录后可能通过路径遍历访问其他目录;如果只配置文件系统ACL,攻击者可以从任意IP尝试登录,增加暴力破解风险。
另一个典型场景是日志审计。CentOS的rsyslog或auditd生成的日志文件默认权限是600,root可读,但安全审计团队需要只读访问。用setfacl给审计用户添加读取权限,同时用firewalld限制审计管理界面只能从内网网段访问,这样既满足合规要求,又避免日志泄露到公网。
排错思路与常见陷阱
网络ACL配置后不生效,排查顺序应该是:先确认服务本身是否监听了预期端口(ss -tlnp),再检查TCP Wrapper是否匹配(如果服务支持libwrap),然后看firewalld或nftables的规则顺序和默认策略。很多人直接看firewalld的zone配置,却忽略了direct rules可能覆盖了zone规则。另外,firewalld的默认zone如果是public,而接口绑定在其他zone,规则也不会生效。可以用firewall-cmd --get-active-zones查看接口所属zone。
文件系统ACL排错时,先确认挂载选项支持acl,再检查ACL mask值。如果一个用户明明在ACL中有rw权限,但实际无法写入,很可能是因为文件属主或chmod操作修改了mask。用getfacl查看mask条目,必要时用setfacl -m m::rwx重新设置mask。另外,默认ACL只对新建文件生效,对已存在的文件不会自动应用,需要手动用setfacl -R递归设置。
还有一个容易踩的坑是SELinux。CentOS默认启用SELinux,即使网络ACL和文件系统ACL都配置正确,SELinux策略仍然可能阻止服务访问特定端口或文件。例如Nginx需要访问非标准端口时,必须用semanage port -a -t http_port_t -p tcp 8080添加端口标签。排错时如果发现规则都对但服务仍然被拒绝,检查/var/log/audit/audit.log中的AVC拒绝记录,用audit2allow生成自定义策略模块。
生产环境ACL管理的最佳实践
网络访问控制列表管理不是一次性配置,而是需要持续维护的过程。建议将所有ACL规则纳入版本控制,无论是firewalld的配置文件、nftables的规则集还是setfacl脚本,都应该放在Git仓库中,变更时走审批流程。对于大规模服务器集群,可以用Ansible或SaltStack批量下发ACL规则,避免手工操作不一致。
定期审计也是必要的。用firewall-cmd --list-all和nft list ruleset导出当前规则,与版本库中的基线对比,发现异常变更及时告警。文件系统ACL可以用getfacl -R导出关键目录的权限列表,定期检查是否有越权条目。对于TCP Wrapper,检查hosts.deny中是否始终保留ALL: ALL兜底策略,防止误删导致完全开放。
最后强调一点:ACL是纵深防御中的一层,不能替代服务自身的认证和授权机制。即使网络ACL配置得再严密,如果SSH允许密码登录且密码弱,攻击者一旦进入内网仍然可以横向移动。所以网络ACL管理必须与强密码策略、密钥认证、服务最小化权限结合,才能形成完整的访问控制体系。
