一、背景
简单说下事件的背景,使用CentOS6安装freeradius作为Radius Server,接受来自多台服务器(PPP)的认证请求,服务器之间做了故障转移,每两台服务器为一组,只为本组用户提供认证服务,当组中某台服务器出现故障时自动将请求定向到另外一台存活的服务器,对于不是本组的用户认证请求不予通过。服务器全部是虚拟机。整体结构图如下:
1 2 3 |
Log{ auth = yes #在/var/log/radius/radius.log中记录认证日志 } |
sites-available/default需要内容
1 2 3 4 5 6 7 8 9 10 11 |
Authorize{ # # If you want to have a log of authentication requests, # un-comment the following line, and the 'detail auth_log' # section, above. auth_log #重点是这个,生成认证的详细记录 } post-auth{ reply_log #reply日志,根据需要开启 } |
以上内容一定需要事先配置好,否则不会有详细日志,也就没办法方便地进行故障检测。
二、故障状况在更换一组服务器中的一台服务器之后,重新设置好各项参数,使用超级用户(没有限制,可在所有服务器中认证)去新添加的服务器中进行认证通过,以为已经OK了。过了两天去查看日志发现大量Access-Reject的记录,并且查看该组用户的Accounting记录发现都是在另外一台旧的服务器上,也就是说新添加的服务器没有成功接受用户的认证和记账。再次更换超级用户测试OK,说明NAS和Radius Server通讯正常,但是换用一个普通用户帐号却发现认证失败。
三、查找直接原因
查看Radius Server上的radius.log日志文件中发现如下信息:
1 2 |
Auth: Login incorrect: [a01/<via Auth-Type = Reject>] (from client SA02 por t 0 cli 114.XX.XX.XX) |
由于认证规则采用的是匹配接受,不匹配拒绝的策略,从这里能看出是没有匹配到认证策略,而认证策略中唯一的匹配条件就是NAS-IP-Address,需要进一步查看详细的认证日志来确定匹配失败的原因,查看日志文件/var/log/radius/radacct/[NAS的IP]/auth-detail-[日期] 可以看到类似于以下的内容
1 2 3 4 5 6 7 8 9 |
Packet-Type = Access-Request Service-Type = Framed-User Framed-Protocol = PPP User-Name = "a01" MS-CHAP-Challenge = 0x20dbb5fce2ce09553ba......(省略) MS-CHAP2-Response = 0x75008ae626fb88ac5caf2ad5d73e8d7ef......(省略) Calling-Station-Id = "114.XXX.XXX.XXX" NAS-IP-Address = 127.0.0.1 NAS-Port = 0 |
这里似乎可以发现问题了,NAS-IP-Address = 127.0.0.1,NAS的IP居然是127.0.0.1,这个肯定匹配不上认证策略中的NAS-IP-Address设置了。那么现在要针对这个问题来查找原因。
四、查找根本原因
上述详细日志中的内容实际上是从NAS发送给Raidus Server的,也就是根本原因在NAS上(PPP服务器),由于PPP的radius认证使用的是radius.so和radattr.so两个插件,插件的配置文件位于/usr/local/etc/radiusclient/中,主要配置文件:radiusclient.conf,radius服务器列表:servers,从radiusclient.conf中查找看是否有关于本机标识相关内容,结果只找到这些:
1 2 3 4 5 6 7 8 9 10 11 |
# NAS-Identifier # # If supplied, this option will cause the client to send the given string # as the contents of the NAS-Identifier attribute in RADIUS requests. No # NAS-IP-Address attribute will be sent in this case. # # The default behavior is to send a NAS-IP-Address option and not send # a NAS-Identifier. The value of the NAS-IP-Address option is chosen # by resolving the system hostname. # nas_identifier MyUniqueNASName |
这部分是说,默认会发送一个 NAS-IP-Address 选项给服务器,这里只是指定NAS-Identifier,但是里面有一句重要的提示:“The value of the NAS-IP-Address option is chosen by resolving the system hostname.”,大意是说NAS-IP-Address选项的值是根据主机名的解析来的。为了证实这个说法,找来了ppp的源码包并解开,
1 2 3 4 5 6 |
[root@SA02 ~]# cd ppp-2.4.5/pppd/plugins/radius/ [root@SA02 radius]# ls COPYRIGHT clientid.c includes.h options.h radattr.c sendserver.c Makefile.linux config.c ip_util.c pathnames.h radius.c util.c avpair.c dict.c lock.c pppd-radattr.8 radiusclient.h buildreq.c etc md5.c pppd-radius.8 radrealms.c |
由于实现没有了解过ppp的源码结构,只能猜测与哪个文件有关,先看看radius.c和radattr.c均没有发现,最后在ip-util.c中发现以下两个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/* * Function: rc_get_ipaddr * * Purpose: return an IP address in host long notation from a host * name or address in dot notation. * * Returns: 0 on failure */ UINT4 rc_get_ipaddr (char *host) { struct hostent *hp; if (rc_good_ipaddr (host) == 0) { return ntohl(inet_addr (host)); } struct hostent *hp; if (rc_good_ipaddr (host) == 0) { return ntohl(inet_addr (host)); } else if ((hp = gethostbyname (host)) == (struct hostent *) NULL) { error("rc_get_ipaddr: couldn't resolve hostname: %s", host); return ((UINT4) 0); } return ntohl((*(UINT4 *) hp->h_addr)); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * Function: rc_own_ipaddress * * Purpose: get the IP address of this host in host order * * Returns: IP address on success, 0 on failure * */ UINT4 rc_own_ipaddress(void) { static UINT4 this_host_ipaddr = 0; if (!this_host_ipaddr) { if ((this_host_ipaddr = rc_get_ipaddr (hostname)) == 0) { error("rc_own_ipaddress: couldn't get own IP address"); return 0; } } return this_host_ipaddr; } |
对C语言一窍不通的我,只能从命名和语法大概看出个一二:
rc_get_ipaddr函数通过主机名获取IP地址
rc_own_ipaddress获取主机名后调用rc_get_ipaddr获取IP地址
至于具体怎么获取的主机名的就不管了。
接下来按照流程来取得IP地址,先通过hostname命令查询主机名,然后通过ping命令将主机名解析成IP,两步并作一步:
1 |
ping $(hostname) |
结果果然是127.0.0.1!
五、解决问题
既然知道了问题原因在于主机名和IP的对应问题就好办了,修改/etc/hosts文件中的主机名和IP对应关系,
将
127.0.0.1 SA02.localhost SA02
中的127.0.0.1替换成实际的IP地址
再次执行
1 |
ping $(hostname) |
这次看到正确的IP了,重新尝试radius认证,成功!日志变成了:
1 2 3 4 5 6 7 8 9 |
Packet-Type = Access-Request Service-Type = Framed-User Framed-Protocol = PPP User-Name = "a01" MS-CHAP-Challenge = 0xea098e3c46cc5e134......(省略) MS-CHAP2-Response = 0x2f0095a416f344078aed661396............(省略) Calling-Station-Id = "114.XXX.XXX.XXX" NAS-IP-Address = [NAS的真实IP] NAS-Port = 0 |
六、问题原因分析
由于直接一直使用的是OpenVZ的虚拟机,而这次新增的服务器采用的是KVM虚拟化的机器,两者区别还是相当大的。通过对比两者的hosts文件,发现在OpenVZ中默认就将主机的真实IP与主机名对应起来了,而KVM更接近实体机,采用的是127.0.0.1的IP来与主机名对应。
原文链接:Linux主机名解析问题引起的PPP使用radius认证失败,转载请注明来源!