问题现象

在公司内网使用 OpenWrt 路由器桥接公司内网,上网时无法打开公司内部业务系统,报 DNS 解析错误。

网络拓扑

诊断

经查,nslookup 内网业务系统域名回应无 A 或 AAAA 记录,访问外网无异常。直接连入公司内网访问公司内部业务系统无异常。ping 内网业务系统 ip 地址通,定位在 DNS 服务器问题上。由于我司有内网 DNS 服务器,nslookup 指定内网 DNS 服务器解析,回复无 A 或 AAAA 记录,检查内网 DNS 服务器服务正常。指定 114DNS 服务器解析,回复无 A 或 AAAA 记录。故分析问题可能是因为 OpenWrt 路由器引起的。

解决

登录 OpenWrt,Network->DHCP and DNS,去使能 Rebind protection Discard upstream RFC1918 responses

原因分析

IANA 保留了以下三个 IP 地址块用于私有网络。10.0.0.0 - 10.255.255.255,172.16.0.0 - 172.31.255.255,192.168.0.0 - 192.168.255.255。

OpenWrt 的 DNS 和 DHCP 是由 dnsmasq 包提供服务的。该包默认会对私有地址进行检查,一旦出现私有地址在路由器外网进行转发,就会被丢弃。路由器处理 DNS 回复时也有检查是否为私有地址,若域名被解析为私有地址,则认为可能是 DNS rebind 攻击。OpenWrt 默认会开启 DNS rebind 保护,防止域名被指向为私有地址,一旦域名被指向私有地址,将丢弃 DNS 解析数据包。默认认为访问域名都通过外网,不需要通过域名访问内网,所以这个问题更多的会出现在内网挂内网的情况下。

相关代码

../trunk/build_dir/target-i386_i486_uClibc-0.9.33.2/dnsmasq-nodhcpv6/dnsmasq-2.71/src/forward.c

1
2
3
4
5
6
if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
}

../trunk/build_dir/target-i386_i486_uClibc-0.9.33.2/dnsmasq-nodhcpv6/dnsmasq-2.71/src/rfc1035.c

1
2
3
4
5
/* check for returned address in private space */
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))
return 1;

../trunk/build_dir/target-i386_i486_uClibc-0.9.33.2/dnsmasq-nodhcpv6/dnsmasq-2.71/src/rfc1035.c

1
2
3
4
5
6
7
8
9
10
11
/* is addr in the non-globally-routed IP space? */
int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}