免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2531 | 回复: 3
打印 上一主题 下一主题

交学习作业:FB的ssh防火墙脚本。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-05-30 11:49 |只看该作者 |倒序浏览
对在Internet中的机子开有ssh的,有人对之“探头探脑”的情况非常普遍。尽管进来没那么容易,但总感觉不太舒服,最好是“还以颜色”。

因无聊日前突然想用python,哈哈……目前只学到函数,所以脚本还不完善,但能用。敬请各位高手指正。

环境

fb# uname -a
FreeBSD fb.ljzx.gx.cn 7.2-RELEASE FreeBSD 7.2-RELEASE #1: Thu May 21 16:52:12 CST 2009     wjy@fb.ljzx.gx.cn:/usr/obj/usr/src/sys/FB  i386
You have new mail.
fb# pkg_info | grep python
python25-2.5.4      An interpreted object-oriented programming language
python26-2.6.1      An interpreted object-oriented programming language
python30-3.0        An interpreted object-oriented programming language
fb# pkg_info | grep MySQLdb
py25-MySQLdb-1.2.2  Access a MySQL database through Python
fb# python -V
Python 2.5.4



库结构
  1. CREATE TABLE `firewall`.`fbsshd` (
  2. `ID` int( 10 ) unsigned NOT NULL AUTO_INCREMENT ,
  3. `DT` datetime NOT NULL ,
  4. `addr` char( 100 ) NOT NULL ,
  5. `username` varchar( 30 ) NOT NULL ,
  6. `stat` varchar( 20 ) NOT NULL ,
  7. `memo` varchar( 100 ) NOT NULL ,
  8. `times` int( 11 ) NOT NULL ,
  9. `MT` datetime NOT NULL ,
  10. PRIMARY KEY ( `ID` )
  11. ) ENGINE = MYISAM DEFAULT CHARSET = latin1;
复制代码
控制变量

MON={'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}
localip='10.0.0.251'
count={
        'maps':0,
        'mapping':0,
        'tty':0,
        'Invalid':0,
        'illegal':0,
        'authentication':0,
        'Bad':0,
        'Did':0,
        'interactive':0
}
offset={
        'maps'2,None,None,1),
        'mapping'6,None,None,1),
        'tty'5,1,None,1),
        'Invalid'5,3,None,1),
        'illegal'8,6,None,3),
        'authentication'6,4,None,3),
        'Bad'7,5,None,1),
        'Did'7,None,None,1),
        'interactive'6,4,None,1)
}
splittime=[(2,2,10),(5,3,10),(10,4,10),(30,4,15),(60,5,15),(120,5,20),(720,6,20),(1440,8,20),(2880,9,20),(7200,10,20),(21600,16,30)]


MON是字符月向数字月转换用。

localip是你要监管的IP地址。

count是用于某次日志检查中各关键字出现次数计数。

offset是关键字及其用到的相关数据。
#{keysaddr, usetname, memo, strindex)}
常见的关键字如下:
        # maps          ->      Address 202.80.112.19 maps to ip19.ez.net.id, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
        # mapping       ->      reverse mapping checking getaddrinfo for abts-tn-dynamic-138.160.145.203.airtelbroadband.in [203.145.160.138] failed - POSSIBLE BRE
AK-IN ATTEMPT!
        # tty           ->      jsk to root on /dev/ttyp1
        # interactive   ->      Accepted keyboard-interactive/pam for jsk from 192.168.0.9 port 61766 ssh2
        # Invalid       ->      Invalid user library from 115.238.55.166
        # illegal       ->      error: PAM: authentication error for illegal user andi from 121.8.241.59
        # authentication->      error: PAM: authentication error for root from 121.8.241.59
        # Bad           ->      Bad protocol version identification 'hello' from 24.11.10.30
        # Did           ->      Did not receive identification string from 119.188.7.201
对应地,如'maps':(2,None,None,1)指的是:IP地址是第三字段的位置(202.80.112.19),用户名没存在(None),memo字段不记录(None),strindex指的是在日志中的“sshd[58334]: Address 202.80.112.19 maps to ip19.ez.net.id, but ...”用冒号打散后的有效串索引为1。因在illegal关键字中,日志中该条目有多个冒号,对应数过来strindex就是3。
如果出现新的关键字怎么办?没关系,当发现新的日志记录时,在脚本日志中会记录有,只要定义offset和count的相应部分即可。

splittime是策略定义。
#         [(minute,times,limit)...]
你想订怎样的防火墙策略就用这个变量。如[(2,2,10),(5,3,10)]则定义了两个策略,一是“两分钟内有两条记录以上的取不多于十个IP”,二是“五分钟内有三条记录以上的不多于10个IP”。取得这些IP后当然还要去掉重复。

脚本
  1. #!/usr/local/bin/python

  2. # e-mail: ljwsy@163.com  QQ:8101895

  3. import MySQLdb
  4. import datetime
  5. import commands

  6. MON={'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}
  7. localip='10.0.0.251'

  8. db = MySQLdb.connect(host="mysql.somewhere.com", user="fire", passwd="somepasswd", db="firewall")
  9. cursor = db.cursor()
  10. log=open('/var/log/sshdipfw.log','a')
  11. NOW = datetime.datetime.now()
  12. sql="SELECT DT FROM fbsshd ORDER BY DT DESC LIMIT 0,1"
  13. cursor.execute(sql)
  14. result = cursor.fetchall()
  15. lasttime="%s" % result[0]
  16. count={
  17.         'maps':0,
  18.         'mapping':0,
  19.         'tty':0,
  20.         'Invalid':0,
  21.         'illegal':0,
  22.         'authentication':0,
  23.         'Bad':0,
  24.         'Did':0,
  25.         'interactive':0
  26. }
  27. offset={
  28.         'maps':(2,None,None,1),
  29.         'mapping':(6,None,None,1),
  30.         'tty':(5,1,None,1),
  31.         'Invalid':(5,3,None,1),
  32.         'illegal':(8,6,None,3),
  33.         'authentication':(6,4,None,3),
  34.         'Bad':(7,5,None,1),
  35.         'Did':(7,None,None,1),
  36.         'interactive':(6,4,None,1)
  37. }
  38. #{keys:(addr, usetname, memo, strindex)}
  39.         # maps          ->      Address 202.80.112.19 maps to ip19.ez.net.id, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!
  40.         # mapping       ->      reverse mapping checking getaddrinfo for abts-tn-dynamic-138.160.145.203.airtelbroadband.in [203.145.160.138] failed - POSSIBLE BRE
  41. AK-IN ATTEMPT!
  42.         # tty           ->      jsk to root on /dev/ttyp1
  43.         # interactive   ->      Accepted keyboard-interactive/pam for jsk from 192.168.0.9 port 61766 ssh2
  44.         # Invalid       ->      Invalid user library from 115.238.55.166
  45.         # illegal       ->      error: PAM: authentication error for illegal user andi from 121.8.241.59
  46.         # authentication->      error: PAM: authentication error for root from 121.8.241.59
  47.         # Bad           ->      Bad protocol version identification 'hello' from 24.11.10.30
  48.         # Did           ->      Did not receive identification string from 119.188.7.201

  49. def insline(datetime,line,key):
  50.         t=line.split(' ')
  51.         addr='' if offset[key][0]==None else t[offset[key][0]]
  52.         username='' if offset[key][1]==None else t[offset[key][1]]
  53.         memo='' if offset[key][2]==None else t[offset[key][2]]
  54.         sql='INSERT INTO fbsshd (  DT ,  addr, stat, username,memo,times,MT) VALUES ( \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\'0\',NOW());' % (datetime,addr,key,userna
  55. me,memo)
  56.         cursor.execute(sql)
  57.         count[key]+=1

  58. for line in open('/var/log/auth.log') :
  59.         if line.find('newsyslog')>-1: continue          #May 14 08:15:16 fb newsyslog[14997]: logfile turned over due to size>100K
  60.         if line.find('message repeated')>-1: continue   #May 19 01:50:15 <auth.err> fb last message repeated 2 times
  61.         if line.find('disconnect')>-1: continue         #May 19 16:53:39 <auth.info> fb sshd[66966]: Received disconnect from 192.168.0.9: 11: disconnected by user
  62.         if line.find('listening')>-1: continue          #May 19 16:53:39 <auth.info> fb sshd[66966]: Server listening on 0.0.0.0 port 22.
  63.         if line.find('User uucp')>-1: continue          #May 19 16:53:39 <auth.info> fb sshd[66966]: User uucp not allowed because shell /usr/local/libexec/uucp/uu
  64. cico does not exist
  65.         line=line[:len(line)-1]
  66.         t=line.split('<')                               #['May 14 08:15:16 ','auth.info> fb sshd[14983]: Invalid user shoutcast from 200.107.9.140\n']
  67.         if len(t)<2 :
  68.                 print >> log,"Unknow(%s): %s" % (NOW,line)
  69.                 continue
  70.         tmp=t[1].split(':')                             #['May 14 08:15:16 ','auth.info> fb sshd[14983]',' Invalid user shoutcast from 200.107.9.140\n']
  71.         tmp=t[0].rsplit()                               #['May', '14', '08:15:16',' ']
  72.         DT ='%s-%02d-%02d %s' % ( NOW.year, MON[tmp[0]],int(tmp[1]),tmp[2])     #'2012-5-14 08:15:16'
  73.         if DT <= lasttime : continue
  74.         if DT.find('2012-05-9')>-1 : print line
  75.         tmp=t[1].split(':')
  76.         sql=0
  77.         for (k,v) in offset.items() :
  78.                 if line.find(k)>-1 :
  79.                         insline(DT,tmp[v[3]],k)
  80.                         sql=1
  81.                         break
  82.         if sql>0 : continue
  83.         print >> log,"Unknow(%s) : %s" % (line,NOW)
  84. tmp=0
  85. for (k,v) in count.items() : tmp+=v
  86. t="%s" % NOW
  87. t=t.split('.')
  88. if tmp > 0 :
  89.         print >> log, "%s Add %03d new record." % ( t[0],tmp)
  90.         for (k,v) in count.items() :
  91.                 print >> log, "\t%20s\t->%8d" % (k,v)
  92. else :
  93.         if ((NOW.minute % 2) > 0) :
  94.                 print >>log,"None new record at %s" % NOW
  95.                 exit()
  96. splittime=[(2,2,10),(5,3,10),(10,4,10),(30,4,15),(60,5,15),(120,5,20),(720,6,20),(1440,8,20),(2880,9,20),(7200,10,20),(21600,16,30)]
  97. #         [(minute,times,limit)...]


  98. done=[]
  99. for (t,s,tty) in splittime :
  100.         tmp=datetime.datetime.now()-datetime.timedelta(seconds=t*60)
  101.         tmp=str(tmp)
  102.         tmp=tmp.split('.')
  103.         sql="SELECT addr,count(addr),sum(times) FROM `fbsshd` WHERE DT > \'%s\' AND stat!=\'su\' GROUP BY addr ORDER BY sum(times),count(addr) DESC LIMIT 0,%s" % (
  104. tmp[0],tty)
  105.         cursor.execute(sql)
  106.         sql=cursor.fetchall()
  107.         for tmp in sql :
  108.                 t=' '.join(line for line in done)
  109.                 if int(tmp[1])<s :
  110.                         continue
  111.                 if t.find(tmp[0]) > -1 :
  112.                         continue
  113.                 done.append(tmp[0])
  114. a=[
  115. '/sbin/ipfw -q -f flush',
  116. '/sbin/ipfw -q add 100 allow ip from any to any via lo0',
  117. '/sbin/ipfw -q add 400 deny ip from any to ::1',
  118. '/sbin/ipfw -q add 500 deny ip from ::1 to any',
  119. '/sbin/ipfw -q add 600 allow ipv6-icmp from :: to ff02::/16',
  120. '/sbin/ipfw -q add 700 allow ipv6-icmp from fe80::/10 to fe80::/10',
  121. '/sbin/ipfw -q add 800 allow ipv6-icmp from fe80::/10 to ff02::/16',
  122. '/sbin/ipfw -q add 900 allow ipv6-icmp from any to any ip6 icmp6types 1',
  123. '/sbin/ipfw -q add 1000 allow ipv6-icmp from any to any ip6 icmp6types 2,135,136',
  124. ]
  125. for sql in a :
  126.         (t,tmp)=commands.getstatusoutput(sql)
  127.         if t < 0 : print >> log,"%s ipfw RUN error(%s): %s" % ( datetime.datetime.now(),t,sql)
  128. sql=1
  129. for line in done :
  130.         t="/sbin/ipfw -q add %d deny log ip from %s to %s 22" % (10000+sql*10,line,localip)
  131.         (t,tmp)=commands.getstatusoutput(t)
  132.         if t < 0 :
  133.                 print >> log,"%s ipfw RUN error(%s): %s" % ( datetime.datetime.now(),t,a)
  134.         else :
  135.                 sql+=1
  136. if (datetime.datetime.now().minute % 30)==0 :
  137.         print >> log , "ipfw update at %s." % datetime.datetime.now()
  138. exit()
复制代码
注意

因要下班了,懒得查文字错误,将就着看吧。

另外:脚本中的tty关键字没有得去除,会进入防火墙的,别把自己关在门外哟!!!

如果哪个地方闹笑话也请大家多多包含并给予指正。谢谢围观。

论坛徽章:
0
2 [报告]
发表于 2012-05-30 12:22 |只看该作者
本帖最后由 ljwsy 于 2012-05-30 12:24 编辑

勘误及说明:

1.脚本中有些行在复制粘贴时跨行了,请自行辨别。80行是调试时用的,忘记删除了。
2.数据库中至少要有一条“旧”记录,脚本只处理“比数据库中最后一记录”还要新的日志。
3.脚本放到crontab中运行,每分钟跑一次。

论坛徽章:
0
3 [报告]
发表于 2012-05-30 12:25 |只看该作者
支持分享,fb和mysql都很少用,不熟这两个

论坛徽章:
0
4 [报告]
发表于 2012-05-30 12:43 |只看该作者
anonymous0502 发表于 2012-05-30 12:25
支持分享,fb和mysql都很少用,不熟这两个


这不是无聊嘛,得学习又实用,何乐而不为?

不一定就用在FB和Mysql上,只要有python、有保存日志记录的地方、有防火墙的系统再改一改就可以用了。这哪算是分享,只能算是孩童玩的玩具。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP