- 论坛徽章:
- 0
|
对在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
库结构- CREATE TABLE `firewall`.`fbsshd` (
- `ID` int( 10 ) unsigned NOT NULL AUTO_INCREMENT ,
- `DT` datetime NOT NULL ,
- `addr` char( 100 ) NOT NULL ,
- `username` varchar( 30 ) NOT NULL ,
- `stat` varchar( 20 ) NOT NULL ,
- `memo` varchar( 100 ) NOT NULL ,
- `times` int( 11 ) NOT NULL ,
- `MT` datetime NOT NULL ,
- PRIMARY KEY ( `ID` )
- ) 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是关键字及其用到的相关数据。
#{keys addr, 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后当然还要去掉重复。
脚本- #!/usr/local/bin/python
- # e-mail: ljwsy@163.com QQ:8101895
- import MySQLdb
- import datetime
- import commands
- 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'
- db = MySQLdb.connect(host="mysql.somewhere.com", user="fire", passwd="somepasswd", db="firewall")
- cursor = db.cursor()
- log=open('/var/log/sshdipfw.log','a')
- NOW = datetime.datetime.now()
- sql="SELECT DT FROM fbsshd ORDER BY DT DESC LIMIT 0,1"
- cursor.execute(sql)
- result = cursor.fetchall()
- lasttime="%s" % result[0]
- 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)
- }
- #{keys:(addr, 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
- def insline(datetime,line,key):
- t=line.split(' ')
- addr='' if offset[key][0]==None else t[offset[key][0]]
- username='' if offset[key][1]==None else t[offset[key][1]]
- memo='' if offset[key][2]==None else t[offset[key][2]]
- sql='INSERT INTO fbsshd ( DT , addr, stat, username,memo,times,MT) VALUES ( \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\'0\',NOW());' % (datetime,addr,key,userna
- me,memo)
- cursor.execute(sql)
- count[key]+=1
- for line in open('/var/log/auth.log') :
- if line.find('newsyslog')>-1: continue #May 14 08:15:16 fb newsyslog[14997]: logfile turned over due to size>100K
- if line.find('message repeated')>-1: continue #May 19 01:50:15 <auth.err> fb last message repeated 2 times
- 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
- 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.
- 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
- cico does not exist
- line=line[:len(line)-1]
- t=line.split('<') #['May 14 08:15:16 ','auth.info> fb sshd[14983]: Invalid user shoutcast from 200.107.9.140\n']
- if len(t)<2 :
- print >> log,"Unknow(%s): %s" % (NOW,line)
- continue
- tmp=t[1].split(':') #['May 14 08:15:16 ','auth.info> fb sshd[14983]',' Invalid user shoutcast from 200.107.9.140\n']
- tmp=t[0].rsplit() #['May', '14', '08:15:16',' ']
- DT ='%s-%02d-%02d %s' % ( NOW.year, MON[tmp[0]],int(tmp[1]),tmp[2]) #'2012-5-14 08:15:16'
- if DT <= lasttime : continue
- if DT.find('2012-05-9')>-1 : print line
- tmp=t[1].split(':')
- sql=0
- for (k,v) in offset.items() :
- if line.find(k)>-1 :
- insline(DT,tmp[v[3]],k)
- sql=1
- break
- if sql>0 : continue
- print >> log,"Unknow(%s) : %s" % (line,NOW)
- tmp=0
- for (k,v) in count.items() : tmp+=v
- t="%s" % NOW
- t=t.split('.')
- if tmp > 0 :
- print >> log, "%s Add %03d new record." % ( t[0],tmp)
- for (k,v) in count.items() :
- print >> log, "\t%20s\t->%8d" % (k,v)
- else :
- if ((NOW.minute % 2) > 0) :
- print >>log,"None new record at %s" % NOW
- exit()
- 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)]
- # [(minute,times,limit)...]
- done=[]
- for (t,s,tty) in splittime :
- tmp=datetime.datetime.now()-datetime.timedelta(seconds=t*60)
- tmp=str(tmp)
- tmp=tmp.split('.')
- 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" % (
- tmp[0],tty)
- cursor.execute(sql)
- sql=cursor.fetchall()
- for tmp in sql :
- t=' '.join(line for line in done)
- if int(tmp[1])<s :
- continue
- if t.find(tmp[0]) > -1 :
- continue
- done.append(tmp[0])
- a=[
- '/sbin/ipfw -q -f flush',
- '/sbin/ipfw -q add 100 allow ip from any to any via lo0',
- '/sbin/ipfw -q add 400 deny ip from any to ::1',
- '/sbin/ipfw -q add 500 deny ip from ::1 to any',
- '/sbin/ipfw -q add 600 allow ipv6-icmp from :: to ff02::/16',
- '/sbin/ipfw -q add 700 allow ipv6-icmp from fe80::/10 to fe80::/10',
- '/sbin/ipfw -q add 800 allow ipv6-icmp from fe80::/10 to ff02::/16',
- '/sbin/ipfw -q add 900 allow ipv6-icmp from any to any ip6 icmp6types 1',
- '/sbin/ipfw -q add 1000 allow ipv6-icmp from any to any ip6 icmp6types 2,135,136',
- ]
- for sql in a :
- (t,tmp)=commands.getstatusoutput(sql)
- if t < 0 : print >> log,"%s ipfw RUN error(%s): %s" % ( datetime.datetime.now(),t,sql)
- sql=1
- for line in done :
- t="/sbin/ipfw -q add %d deny log ip from %s to %s 22" % (10000+sql*10,line,localip)
- (t,tmp)=commands.getstatusoutput(t)
- if t < 0 :
- print >> log,"%s ipfw RUN error(%s): %s" % ( datetime.datetime.now(),t,a)
- else :
- sql+=1
- if (datetime.datetime.now().minute % 30)==0 :
- print >> log , "ipfw update at %s." % datetime.datetime.now()
- exit()
复制代码 注意
因要下班了,懒得查文字错误,将就着看吧。
另外:脚本中的tty关键字没有得去除,会进入防火墙的,别把自己关在门外哟!!!
如果哪个地方闹笑话也请大家多多包含并给予指正。谢谢围观。 |
|