根本原因是目标服务未链接libwrap或启动方式绕过tcp-wrappers;需用ldd验证支持,匹配按行序执行,顺序即策略,ip/cidr优于主机名,调试须检查换行、ipv6括号及selinux限制。

为什么 /etc/hosts.allow 和 /etc/hosts.deny 有时不生效
根本原因通常是目标服务没链接 libwrap,或者启动方式绕过了 Tcp-Wrappers。比如 systemd 直接用 ExecStart= 启动的 sshd 或 vsftpd,若未编译进 libwrap 支持,tcpd 就完全插不上手。
验证方法:ldd $(which sshd) | grep wrap —— 没输出说明不支持;有 libwrap.so 才可能生效。
- 常见已支持的服务:
sshd(OpenSSH 默认启用)、vsftpd、telnetd、in.ftpd - 常见不支持的服务:
nginx、apache2、dockerd、所有用systemd-socket激活的服务 - Debian/Ubuntu 的
openssh-server包默认启用 libwrap;CentOS 8+ 的openssh-server默认**禁用**(需手动编译或改配置)
hosts.allow 和 hosts.deny 的匹配逻辑到底怎么走
不是“先 allow 后 deny”或“deny 优先”,而是按行顺序逐条匹配,**找到第一条匹配规则就立即执行并停止搜索**。所以顺序就是策略本身。
-
ALL: ALL在hosts.deny里 → 拒绝所有,除非hosts.allow里有更早的明确放行项 -
sshd: 192.168.1.0/24在hosts.allow第一行 → 只放行该网段,其余全拒(因hosts.deny默认存在ALL: ALL) - 主机名匹配依赖 DNS 或
/etc/hosts,解析失败时会 fallback 到 IP,但延迟高且不可靠,**强烈建议用 IP 或 CIDR** - 支持通配符:
.表示子域(.example.com匹配www.example.com),*匹配任意字符串(*.cn不等价于.cn)
如何让自定义服务也受 Tcp-Wrappers 控制
如果服务本身不链接 libwrap,又不想改代码,可以用 tcpd 做 wrapper 进程——前提是它监听的是 inetd/xinetd 管理的端口,或能被 tcpd 接管启动。
- 编辑
/etc/inetd.conf(或/etc/xinetd.d/myapp),把原服务启动行改成:myport stream tcp nowait root /usr/sbin/tcpd /path/to/myapp - 确保
inetd或xinetd正在运行,并重载配置(sudo systemctl reload xinetd) - 注意:不能用于常驻进程类服务(如 nginx),也不适用于 bind 到
localhost或使用 socket activation 的服务 - 调试技巧:临时在
hosts.deny加ALL: ALL : twist /bin/echo "blocked %a",然后 telnet 测试,看日志是否输出来源 IP
实际部署时最常踩的三个坑
Tcp-Wrappers 看似简单,但线上出问题往往卡在极细的环节。
-
hosts.allow文件末尾缺换行符 → 最后一行被内核忽略(POSIX 标准要求文本文件以\n结尾) - IPv6 地址写成
2001:db8::1而没加方括号 → 规则不匹配(正确写法:sshd: [2001:db8::1]) - SELinux 启用时,
tcpd可能被阻止读取hosts.*文件(检查ausearch -m avc -ts recent | grep tcpd,必要时设布尔值:setsebool -P tcpd_read_hosts_files on)
真正起作用的永远不是“写了规则”,而是“服务进程是否真由 libwrap 插桩、规则语法是否被准确解析、系统层有没有其他机制覆盖了它”。别只盯着文件内容,先确认支撑链是否完整。











