SNMP开发过程中的一些积累


采用snmpusm和snmpvacm操作snmp V3的设置, 有一点一定要注意,就是这些设置在snmpd退出之前,是存在内存中的,当snmpd退出之前,才会写入到文件中. 这就是为什么单板被突然断电,snmpd会失去之前的usm和vacm配置的原因. 这不是bug,是snmp官方故意这么设计的. 要解决这个问题, 只要执行snmpset [options] agent_ip 1.3.6.1.4.1.2021.100.13.0 i 1 即可,即设置versionSavePersistentData这个mib节点为1,让其立刻将信息存储在文件中. 因此,usm和vacm的操作之后,应该加入让versionSavePersistentData为1的操作才是严谨的. 


接收SNMP的TRAP可以采用MIB Browser的工具接收,如果用wireshark发现电脑可以收到snmpd上送的包,但是MIB Browser却无法显示,基本是如下两个问题:1.操作系统的防火墙;2.win7自带的snmp trap进程占用了162端口。对于第二个问题,搜索“服务”,找到“SNMP Trap”服务,将启动类型改为“禁用”,重启电脑,即可生效。目前主流的MIB Browser可以支持Trap V3,但是支持不了Inform V3,从好用的角度来说,推荐MG-Soft,从免费的角度来说,推荐ManageEngine。


MD5/SHA算法有3个特点:不可逆;雪崩效应;不冲突。不可逆就是拿到计算结果没有反回来算出输入是啥;雪崩效应是输入任何一点变化都会导致输出变化很大,防止根据输出的相似性来猜测输入;不冲突就是输入与输出的唯一性。根据这些特点,MD5/SHA会主要用于:认证和内容篡改识别。认证:由于密码不能明文在网络传输,因此用MD5/SHA计算出一个值,网络接受者用已知的密码和用相同的算法计算出另一个值,由于唯一性特点,一定是相等的,因此,可以判断对方是否拥有正确密码的设备。SNMP的V3就是使用了这种方法进行认证的。内容篡改识别:计算输入和计算结果需要一起发给接收者,接收者采用相同算法计算其拿到的输入,如果结果不一样,那内容肯定被改了 


SNMP最安全的V3,我们一直用错了N年,最大的问题是密码明文存储。SNMP的V3用户名和密码应该写在/var/net-snmp/snmpd.conf,而不是我们自己的snmpd.conf中,SNMP启动时会将前者的明文密码加密。这样,即使黑客攻入了我们的单板,拿走的也只是加密的密码,只能访问这台设备而已,其他不通用;反之,密码明文化,一旦密码文件被盗,我们的全部单板都将会落入别人的完全监控之中。由于密码加密,我们也无法使用Web的方式去查看和修改密码,必须采用SNMP提供的工具snmpusm和snmpvacm来控制V3的访问。我们以前的方法,因为不正规,反而是无法实现世界通用的snmpusm和snmpvacm的方式来控制权限的


SNMP的V3正确应用为:在单板第一次启动时,在/var/net-snmp/snmpd.conf通过createUser明文创建一个V3的初始用户,snmpd启动后,会将其删除并加密。客户通过snmpusm(user security model)克隆一个新用户的方式来更改用户名和密码(建议同时删除我们的初始用户)。这样客户就有自己的账号了,但还不够,他们还不能访问我们的单板,因为这些V3用户没有被定义访问权限。客户必须通过已知的可读写V2账户操作snmpvacm(view access control model),经过createView,createSec2Group,createAccess三步后,V3用户才具备访问权限。也就是说,权限管理全掌握在客户那,我们甚至无法知道客户的V3密码。这种才是世界最正式的SNMPV3密码权限管理。


snmpusm用户安全模型命令的用法例子:snmpusm -v3 user_name -l authPriv -a MD5 -A md5_passphase -x DES -X des_passphase target_ip create new_user old_user. snmpusm和create之间的一大串,和snmp通用命令操作V3的用法完全一致。注意,snmpusm操作,本质也是操作相应的MIB节点的内容,但该节点只允许V3用户操作,这就是为什么单板必须有一个V3初始用户的原因。除此之外,V3用户也应该具有该MIB节点的操作权限,如果无法操作,可以通过snmpvacm增加其权限。需要注意的是,snmpusm无法改变用户的认证方式和加密方式!如果被克隆的用户是MD5和DES,那么是无法修改为SHA和AES的。如果要支持全部MD5/SHA和DES/AES,必须有4个不同初始用户才行。 


snmpvacm访问控制模型命令的用法例子:snmpvacm -v2c -c private target_ip createSec2Group 3 zteuser ZteGroup. 首先,客户将自己的账户zteuser放入一个安全模型为V3的ZteGroup组中。snmpvacm ... createView zte_view .1.3.6.xx.xx 定义zte_view的可视mib节点范围,类似宏定义。snmpvacm ... createAccess ZteGroup 3 1 1 zte_view zte_view none给ZteGroup定义访问级别,第一个数字3表示V3, 第二个数字1代表安全认证级别noAuthNoPriv(2,3依次类推),第三个数字的1代表精确匹配OID,最后三个分别是read,write,notify的view范围,none表示没有权限。通过这种方式,可以非常严密地控制账户的信息安全。


SNMP的V3主动上送其实不难,在snmpd.conf中使用trapsess关键字即可,同时trapsess兼容v1和v2c的主动上送,因此,trapsink(v1),trap2sink(v2c),informsink(v2c)三个指令都可以被trapsess取代掉。trapsess通过-Ci表示是inform格式,-v x代表x版本,如果是v3,用户名则用-u(user name),否则用户名用-c(community)。当trapsess使用v3时,通用参数和snmpget等一样使用,很简单。需要注意的是,我们自己的snmpd.conf的trapsess中不应该将v3的密码明文化,而应该使用在/var/net-snmp/snmpd.conf中存在的用户(已加密),这种情况下,我们的snmpd.conf中的trapsess只需要写已存在的用户名和需要的安全级别即可,不需要重复写密码。这样就更安全了。 


SNMP可以实现多维表格,通过多个index实现,index不仅仅可以是数字,甚至可以是字符串,object ID,IP等。假设某个变量的OID为X,那么X.3.5的3和5就是第一个index和第二个index的值,代表二维角度的第3行和三维角度的第5行。那其他格式的index怎么表达呢。字符串和object ID类型都是不定长,因此需要在最开始有其总长度的描述,然后才是内容(字符串没有C里的0作为结束符),举例,字符串index内容为_none_,那么表达就是6.95.110.111.110.101.95,6代表总长度,后面就是ascii码对应的数值。因此,SNMP中通过OID一定能唯一识别一个变量的身份。


snmp使用在嵌入式时,存在一些bug,交叉编译的动态库在加载时会出现一些奇怪的问题,因此目前需要将库信息编译在snmpd程序中。另外,将自己的业务代码编译成动态库时,如果该动态库依赖了snmp的动态库,加载时也会出现相同的奇怪问题(Inconsistency detected by ld.so: dl-deps.c: 622),因此,自己的动态库也不能链接snmp的动态库。解决方法:1.自己的动态库即使使用了snmp的动态库,但是编译时不能加上snmp库的链接信息。2.由于snmp的库信息实际是被主程序集成的,因此,主程序只需要在编译时,加上-rdynamic即可,即把符号信息输出到动态符号表中,那么后面采用dlmod加载自己的业务模块时,就不会存在undefined symbol的问题了。


新版源码snmp(如5.7.3,LTS)的交叉编译仍然有bug,因为net-snmp的核心团队没有嵌入式的人才。默认交叉编译时,会出现undefined reference to `clock_gettime'的错误。解决方法:configure中,加入--with-libs="-lrt"即可。另外,动态库的方式是有问题的,需要将库信息纳入bin中,才能正常运行,因此,需要加入--disable-shared 


snmpd支持动态加载mib库,好处是新的mib的需求,一般不需要改动核心源码。将新的mib需求编译成动态库,由snmpd选择性加载,对版本管理带来了很大的方便。业务代码和庞大的源码分开,也大大降低了维护难度。一般需要3部:1.让snmpd支持动态加载,./configure --with-mib-modules="ucd_snmp" --enable-shared 2.将业务代码编译成动态库 3.在snmpd.conf中告诉snmpd去哪加载库,加入 dlmod xxxx /a/b/c/xxxx.so 即可 第一个参数是动态库名,第二个参数是全路径。


如果需要使用pysnmp,强烈建议使用sudo aptitude install python-pysnmp4来安装。因为这样的安装,会将内容放在一个文件夹里,自己的MIB文件在编译后,可以方便的放入/usr/lib/python2.7/dist-packages/pysnmp/smi/mibs里。否则,如果是下载包自己python setup.py install,会产生安装内容全部放入一个egg文件里的情况,即使通过setMibPath来新增搜索路径来加载自己的mib.py文件也不行。这坑浪费了我半天时间!pysnmp需要通过build-pysnmp-mib指令来将普通的MIB文件(.txt,.my,.mib)变成xxx.py文件,且要放入pysnmp的smi/mibs里。 


ASN.1的INTEGER严格来说是一定要有范围,但是SMI不是完全照搬ASN.1,因此,SMI默认认为INTEGER为Integer32类型(Integer32为INTEGER (xxx,yyy)),Integer32由于本身就是加了范围,因此Integer32 {xxx,yyy}的限制就有问题,SMI直接报错的原因。


snmp的mib文件可以采用smilint命令来检查语法,如smilint -s xxx.mib . -s意思是加入数字告知错误的级别,越小越严重越要优先处理,如果只要检查严重的错误,只需要加入-l1,即level 为1才报错。如果使用smilint出现“smilint failed to locate MIB module”错误,只需要下载snmp_mibs_downloader即可(apt-get install snmp_mibs_downloader)安装基本的mib文件。


net-snmp中有大量的类似DEBUGMSGTL(("trap", "received the inform response for reqid=%d\n",reqid));的调试函数,怎么输出呢?网上只说了运行时加 -Dtoken的参数。实际上,Net-snmp-config.h文件中,必须注释掉NETSNMP_NO_DEBUGGING的宏定义,并把NETSNMP_ALWAYS_DEBUG改为0,才能是-Dtoken生效。举例,上面这句的调试token就是第一个“trap”,则./snmp -f -Dtrap就可以只输出token为trap的所有调试信息了。 这种技术,如果用在CSU上,将会为调试提供很多方便。


CSU的snmpd启动时默认会fork一个进程执行,因此,shell中是看不到任何输出的,可以加 -f,表示do not fork from the shell,这样,才会看到snmpd的输出信息。snmpd -h可以查看所有的选项,-f是这些当中比较实用的。 




猜你喜欢

转载自blog.csdn.net/yyw794/article/details/78091545