อันนี้เคยเขียนไว้ในบล็อกตัวเองมาสักพักแล้วครับ เริ่มจากความว่าผมเองต้องดูแลเครื่องที่แชร์ไฟล์ผ่าน Samba ค่อนข้างเยอะ เลยมีคำถามว่าจะป้องกันไวรัสกันยังไง
ก่อนหน้านี้ลินุกซ์มีระบบป้องกันไวรัสไว้ค่อนข้างดีอยู่แล้ว โดยมากแล้วมักเป็นแนวคิดแบบ On-Access Antivirus คือก่อนที่จะส่งข้อมูลในไฟล์ให้กับผู้ใช้ ก็เอามาตรวจไวรัสกันซะก่อน ตัวอย่างก็เช่น Samba-VScan หรือ ClamFS
กรณีของผมนี้ ผมยอมรับความเสี่ยงที่จะไม่ป้องกันไวรัสสำหรับลินุกซ์ เพราะผมเชื่อว่าความเสี่ยงต่ำพอ ความต้องการที่ผมคิดออกมาได้เป็นดังนี้
แนวคิดในการออกแบบเลยเป็นแนวคิดง่ายๆ ว่า ให้สแกนทุกไฟล์หลังจากมีการเปลี่ยนแปลงลงดิสก์ ถ้าติดไวรัสให้ย้ายไฟล์นั้นออกจากส่วนแชร์ไฟล์
หลังจากหาข้อมูลใน twitter อยู่สิบนาที คำตอบสุดท้ายที่ได้มาจาก @sugree คือการใช้ inotify เพื่อตรวจสอบว่ามีการเขียนไฟล์ใดไปแล้วบ้าง สำหรับ Python ก็มี pyinotify ไว้ให้ใช้เสร็จสรรพ ส่วนการจับไวรัสนั้นก็สามารถใช้ ClamAV ผ่านทาง pyclamd ได้
เขียน Proof-of-Concept ออกมาได้ในเวลาไม่ถึงชั่วโมง โดยอาศัย tutorial จาก pyinotify และ pyclamd
import os import shutil as sh import pyclamd as cav from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent #ประกาศ socket อันนี้จะแตกต่างไปตาม OS ในกรณีนี้เป็นของ Ubuntu 7.10 cav.init_unix_socket('/var/run/clamav/clamd.ctl') wm = WatchManager() #ตรวจสอบเฉพาะการสร้างและการแก้ไขไฟล์ ถ้าเปิดมาอ่านเฉยๆ ก็ปล่อยไป mask = EventsCodes.IN_CREATE | EventsCodes.IN_MODIFY class PAfterAccess(ProcessEvent): def process_IN_CREATE(self, event): fname = os.path.join(event.path, event.name) self.do_scan(fname) def process_IN_MODIFY(self, event): fname = os.path.join(event.path, event.name) self.do_scan(fname) def do_scan(self, fname): res = None try: res = cav.scan_file(fname) print "Scanned %s result is %s" % (fname,str(res)) except cav.ScanError: #ScanError มักพบในกรณีที่ไฟล์นั้นหายไปแล้ว เช่นกรณีไฟล์ Swap print "Can't scan file %s" % fname #ฟังก์ชั่น scan_file จะคืนค่าเป็น None ถ้าไม่เจอไวรัส #ในกรณีนี้ถ้าเจอไวรัสก็ให้ย้ายไฟล์ไปเก็บที่อื่น if res != None: hname = fname[1:].replace("/","-") sh.move(fname,"/home2/quarantine/"+hname) notifier = Notifier(wm, PAfterAccess()) #ระบุตำแหน่งที่ต้องการแสกน ควรตั้ง auto_add=True ไว้ด้วยเผื่อการสร้าง folder ใหม่ wdd = wm.add_watch('/home', mask, rec=True,auto_add=True) #ที่เหลือยกมาจาก Tutorial while True: # loop forever try: # process the queue of events as explained above notifier.process_events() if notifier.check_events(): # read notified events and enqeue them notifier.read_events() # you can do some tasks here... except KeyboardInterrupt: # destroy the inotify's instance on this interrupt (stop monitoring) notifier.stop() break
ผมยังไม่ได้วัดว่าการทำแบบนี้จะช่วยเพิ่มประสิทธิภาพการเข้าถึงไฟล์ได้หรือไม่ หรือจะเพิ่มประสิทธิภาพได้มากแค่ไหนเทียบกับ On-Access แบบเดิมๆ แต่เท่าที่ทดสอบดูก็พบว่าการใช้งานแบบนี้ก็สะดวกในการติดตั้งกว่าการสแกนแบบ On-Access ค่อนข้างมาก โดยเพียงแค่ให้สคริปต์นี้รันตอนบูทเครื่องเท่านั้น ไม่ต้องแก้ไข fstab หรือติดตั้งโมดูลใน samba ให้ยุ่งยาก
ไว้ต้องวัดประสิทธิภาพกับทำ performance tuning กันอีกที
กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น
สั้นดีแฮะ เคยใช้ inotify กับ on-line synchronization
เยี่ยม
ว่าแล้วก็หาทางเอา WatchManager ไปใช้บ้างดีกว่า
ของ Ruby ผมเคยใช้ filesystemwatcher ก็ตรงไปตรงมาดีครับ
ได้ทางเลือกเพิ่มขึ้น ขอบคุณครับ