ljwsy 发表于 2014-04-13 23:48

FreeBSD下的stdin读取求助 [已解决,感谢@HH106和@timespace]

本帖最后由 ljwsy 于 2014-04-15 17:28 编辑

我自己构思了一个“ssh扫描封堵的实现过程”,用的是py实现,经过近两年的运行,效果还不错。脚本是用crontab来运行的,控制粒度为分钟,太低了点,对付慢扫是足够的,而对付脑残不管别人感受的狂扫则有点吃力:开5线程一秒的扫描,一分钟就有300+个记录,等程序反应过来添加防火墙后,这个记录量上升已成事实。

为改变这种现状,打算改用守护进程的方式监控。想法是这样的:脚本进程实时监控auth.log,一旦有新的扫描行则入库;每5分钟从库中提取触犯规则的IP重新生成防火墙规则;在重新生成防火墙规则之间的时间段中,只要某IP产生的扫描记录数超过6条,就把这IP加入临时违规并生成防火墙规则。

为实现上面的想法,我把auth.log重定向为守护进程的stdin,但我用下面的代码“监控”stdin时却只取到进程启动时当前stdin存在的行,新行则取不到(注:因为是测试脚本,不直接用auth.log作为输入,而是改用每分钟都有新行生成的sshdipfw.log)。#!/usr/local/bin/python

# -*-coding:utf-8-*-
import sys, os, time

fname='/tmp/sshd_new.pid'

def daemonize(stdin='/dev/null',stdout= '/dev/null', stderr= 'dev/null'):
    if os.path.isfile(fname) :
      if (time.time()-os.path.getatime(fname)<300) :
            exit()

    #Perform first fork.
    try:
      pid = os.fork()
      if pid > 0:
            sys.exit(0)#first parent out
    except OSError, e:
      sys.stderr.write("fork #1 failed: (%d) %s\n" %(e.errno, e.strerror))
      sys.exit(1)

    os.chdir("/")
    os.umask(0)
    os.setsid()
    #fork
    try:
      pid = os.fork()
      if pid > 0:
            sys.exit(0) #second parent out
    except OSError, e:
      sys.stderr.write("fork #2 failed: (%d) %s]n" %(e.errno,e.strerror))
      sys.exit(1)
    #
    for f in sys.stdout, sys.stderr: f.flush()
    si = file(stdin, 'r')
    so = file(stdout,'a+')
    se = file(stderr,'a+',0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())
    pid=open(fname,'w')
    print >> pid,'Daemon started with pid %d\n' % os.getpid()
    pid.close()

def _example_main():
    sys.stdout.write('Daemon started with pid %d\n' % os.getpid())
    sys.stdout.write('Daemon stdout output\n')
    sys.stderr.write('Daemon stderr output\n')
    c = 0
    while True:
      for line in sys.stdin:
            sys.stdout.write('%s: %s' %( time.ctime(),line))
            sys.stdout.flush()
            c = c+1
            #time.sleep(1)

if __name__ == "__main__":
    daemonize('/var/log/sshdipfw.log','/var/log/sshdipfw_new.log','/var/log/sshdipfw_new.log')
    _example_main()
这是sshdipfw_new.log输出(stdout):……
Sun Apr 13 23:30:36 2014: update at 2014-04-13 23:29:00.
Sun Apr 13 23:30:36 2014:               Total rule(s)   ->      86
Sun Apr 13 23:30:36 2014: update at 2014-04-13 23:30:00.
Sun Apr 13 23:30:36 2014:               Total rule(s)   ->      86
而新的sshdipfw.log(stdin)多出的几行则不见出现在sshdipfw_new.log(stdout)中:……
update at 2014-04-13 23:29:00.
                Total rule(s)   ->      86
update at 2014-04-13 23:30:00.
                Total rule(s)   ->      86
update at 2014-04-13 23:31:00.
                Total rule(s)   ->      86
update at 2014-04-13 23:32:00.
                Total rule(s)   ->      86
update at 2014-04-13 23:33:00.
                Total rule(s)   ->      86
update at 2014-04-13 23:34:00.
                Total rule(s)   ->      86
呵呵,由于本人拿着比码农的一半不到的工资,自然水平也不会高到哪里去,通常都是碰到什么问题才会想着去解决什么问题,所以水平提高得非常的慢。在此想请教达人:在_example_main()段我怎样监控,如果有新行则取到新的行(如原来的最后行时间为23:30:00以及它下面的一行,这个点以后的都视为新行),再进行处理(原来我是从从文件头读取再对比时间——这样做效率太低)。

要不坛友有什么更好的办法指点指点也行,反正是要实现这功能:实时监控某文件,有新行则处理。

在此先谢谢您的指导,也谢谢来围观或回帖的坛友。

HH106 发表于 2014-04-14 09:56

这样试试?利用tail命令

command='tail -f '+/var/log/sshdipfw.log+'|grep "xxx"'
popen=subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)

ljwsy 发表于 2014-04-14 10:16

回复 2# HH106


    谢谢您的回复,今早来上班后一直在试着各种办法,感觉用管道应该可以解决问题,正不知道具体怎样做时就见您的回复,我再努力奋斗一番……

还有个笨办法是:查文件的存取时间,与原来的有变化则只取后面的100行,这样做就减少了不少的无效行处理时间。

timespace 发表于 2014-04-14 10:22

如果stdin不是指向交互设备,默认是全缓存,所以遇到换行可能不刷新缓存,试下"si = file(stdin, 'r', buffering=1)"。

如果上面的方法解决不了,那就是另外一个问题了,用常规文件没法单独模拟重定向,就像shell的重定向,要借助os.pipe。

还有一个办法,模拟“tail -f”的行为,直接打开auth.log,定时file.seek文件尾部,有变化就读取变化的部分。如果觉得定时轮询比较低效,可以用select.kqueue,各类BSD系统都支持。

timespace 发表于 2014-04-14 10:27

哈,刚回复,才看到楼上有提到tail + subprocess,确实这个更简单,搞定问题就行。

ljwsy 发表于 2014-04-14 18:37

回复 4# timespace


    感谢提供方向。真心话:由于平时用的少,没有去深入研究,您所提到的方法大致有印象,不敢再问具体实施,只有自己摸索,也权当提高自己水平的之一。

:D

ljwsy 发表于 2014-04-15 17:25

回复 2# HH106


    这方法真好用,现在被监控文件只要有变动,在stdout文件中不到一秒就体现出来。

    再次表示感谢。
...
   a=subprocess.Popen(args='/usr/bin/tail -f /var/log/sshdipfw.log',stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
    while True:
      c=a.stdout.readline()
      if len(c)>5 :    #忽略空行
            sys.stdout.write('%s: \n\t%s\n' %( time.ctime(),c[:-1]))
            sys.stdout.flush()

ljwsy 发表于 2014-04-15 17:26

回复 5# timespace


    已经完美解决,再次表示感谢。
页: [1]
查看完整版本: FreeBSD下的stdin读取求助 [已解决,感谢@HH106和@timespace]