Friday, September 20, 2024

Sử dụng Python theo dõi sự thay đổi một file và ghi log nó

-

1. Tổng quan.

Bài viết này chủ yếu giới thiệu các bạn quy trình sử dụng Python để lắng nghe sự thay đổi của một file nào đó và chuyển đổi sang định dạng log, cuối cùng là lưu nó vào một file bất kỳ nào đó. Ví dụ của mình là theo dõi file /etc/passwd.

Trong quản trị hệ thống Linux, việc giám sát các thay đổi trong file hệ thống quan trọng như /etc/passwd là rất cần thiết. File này chứa thông tin về các tài khoản người dùng trên hệ thống. Bất kỳ thay đổi nào trong file này đều có thể ảnh hưởng đến bảo mật và hoạt động của hệ thống.. Bài viết này sẽ hướng dẫn bạn cách sử dụng Python để giám sát các thay đổi trong file /etc/passwd và ghi lại các thay đổi này vào một file log.

2. Ứng dụng.

  • Bảo mật hệ thống: Giám sát các thay đổi trong file /etc/passwd giúp phát hiện kịp thời các hành vi bất thường hoặc trái phép.
  • Quản trị hệ thống: Giúp quản trị viên theo dõi các thay đổi về tài khoản người dùng, hỗ trợ trong việc quản lý và kiểm tra hệ thống.
  • Ghi log: Tạo ra các bản ghi chi tiết về các thay đổi trong file /etc/passwd để phục vụ cho việc kiểm tra và phân tích sau này.

3. Mục đích.

  • Giám sát thời gian thực: Phát hiện và ghi lại các thay đổi trong file /etc/passwd ngay khi chúng xảy ra.
  • Ghi log chi tiết: Ghi lại các thay đổi vào một file log để dễ dàng theo dõi và kiểm tra.
  • Tự động hóa: Tự động giám sát và ghi log mà không cần sự can thiệp thủ công.

4. Code.

4.1. Sử dụng interval.

Lý do mình sử dụng phương pháp này là do code hoặc nếu bạn sử dụng lệnh thì có lệnh tail (tail -f /etc/passwd) không phát hiện được dòng mới khi bạn sử dụng lệnh useradd demo là vì useradd không trực tiếp ghi vào file /etc/passwd. Thay vào đó, nó sử dụng các cơ chế khác để cập nhật file này, có thể là thông qua một file tạm thời hoặc một cách khác mà không kích hoạt sự thay đổi mà tail -f có thể phát hiện ngay lập tức.

Còn ví dụ bạn sử dụng echo 'demo' >> /etc/passwd, tức là bạn đang trực tiếp thêm một dòng mới vào cuối file, điều này sẽ kích hoạt tail -f phát hiện sự thay đổi ngay lập tức.

Để giải quyết vấn đề này, bạn có thể thử sử dụng interval để theo dõi các thay đổi trên file /etc/passwd.

Phương pháp kiểm tra theo khoảng thời gian (interval) là một giải pháp đơn giản và hiệu quả để giám sát các thay đổi trong file /etc/passwd. Mặc dù không phải là thời gian thực, nhưng nó cung cấp một cách tiếp cận dễ hiểu và dễ triển khai để đảm bảo rằng các thay đổi được phát hiện và ghi log một cách định kỳ.

import os
import logging
import time

# Custom logging filter to add hostname
class HostnameFilter(logging.Filter):
    def filter(self, record):
        record.hostname = os.uname()[1]
        return True

# Set up logging to /var/log/custom.log
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s',
    datefmt='%b %d %H:%M:%S',
    handlers=[logging.FileHandler('/var/log/custom.log')]
)
logger = logging.getLogger('passwd_monitor')
logger.addFilter(HostnameFilter())

def log_passwd_changes():
    with open('/etc/passwd', 'r') as f:
        lines = f.readlines()
        for line in lines:
            logger.info(f"root::{line.strip()}")

# Check for changes every 60 seconds
interval = 60
last_mtime = os.path.getmtime('/etc/passwd')

while True:
    current_mtime = os.path.getmtime('/etc/passwd')
    if current_mtime != last_mtime:
        log_passwd_changes()
        last_mtime = current_mtime
    time.sleep(interval)

Giải thích code:

  • Import các thư viện cần thiết:
    • os: Thư viện này cung cấp các hàm để tương tác với hệ điều hành, bao gồm việc lấy thông tin về file.
    • logging: Thư viện này được sử dụng để ghi log.
    • time: Thư viện này cung cấp các hàm liên quan đến thời gian, bao gồm việc tạm dừng chương trình trong một khoảng thời gian nhất định.
  • Tạo một bộ lọc logging để thêm hostname vào log:
class HostnameFilter(logging.Filter):
    def filter(self, record):
        record.hostname = os.uname()[1]
        return True

Bộ lọc này thêm thông tin về hostname của máy chủ vào mỗi bản ghi log.

Thiết lập logging để ghi vào file /var/log/custom.log:

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s',
    datefmt='%b %d %H:%M:%S',
    handlers=[logging.FileHandler('/var/log/custom.log')]
)
logger = logging.getLogger('passwd_monitor')
logger.addFilter(HostnameFilter())

Đoạn code này thiết lập cấu hình cho hệ thống logging, bao gồm định dạng của bản ghi log và file log đích.

Tạo hàm log_passwd_changes để ghi log các thay đổi trong file /etc/passwd:

def log_passwd_changes():
    with open('/etc/passwd', 'r') as f:
        lines = f.readlines()
        for line in lines:
            logger.info(f"root::{line.strip()}")

Hàm này mở file /etc/passwd, đọc tất cả các dòng và ghi chúng vào file log.

Kiểm tra các thay đổi mỗi 60 giây:

Như đã nói ở trên, chúng ta sử dụng phương pháp kiểm tra theo khoảng thời gian (interval) để đảm bảo rằng các thay đổi trong file /etc/passwd được phát hiện và ghi log một cách định kỳ.

interval = 60
last_mtime = os.path.getmtime('/etc/passwd')

while True:
    current_mtime = os.path.getmtime('/etc/passwd')
    if current_mtime != last_mtime:
        log_passwd_changes()
        last_mtime = current_mtime
    time.sleep(interval)

Đoạn code này kiểm tra thời gian sửa đổi cuối cùng của file /etc/passwd mỗi 60 giây. Nếu thời gian sửa đổi thay đổi, nó sẽ gọi hàm log_passwd_changes để ghi log các thay đổi.

Phương pháp này có một số ưu điểm và nhược điểm.

Ưu điểm:

  • Đơn giản: Dễ dàng triển khai và hiểu.
  • Không phụ thuộc vào thư viện bên ngoài: Không cần cài đặt và sử dụng các thư viện như pyinotify.

Nhược điểm:

  • Không phải thời gian thực: Có thể có độ trễ giữa thời điểm thay đổi xảy ra và thời điểm nó được phát hiện.
  • Tốn tài nguyên: Kiểm tra file liên tục có thể tốn tài nguyên hệ thống, đặc biệt nếu khoảng thời gian kiểm tra quá ngắn.

Tại sao sử dụng interval = 60:

  • Độ trễ chấp nhận được: Kiểm tra mỗi 60 giây là một khoảng thời gian hợp lý để phát hiện các thay đổi mà không gây quá nhiều tải cho hệ thống.
  • Đơn giản: Phương pháp này dễ hiểu và triển khai mà không cần các thư viện phức tạp.

4.1. Sử dụng inotifywait.

Nếu bạn cần giám sát thời gian thực mà không có độ trễ, bạn có thể sử dụng các thư viện như pyinotify để theo dõi các sự kiện hệ thống tập tin. Tuy nhiên, như đã thảo luận trước đó, việc sử dụng pyinotify có thể gặp một số vấn đề trong việc phát hiện các thay đổi liên tục.

Để listen file /etc/passwd và ghi log như code của bạn, bạn có thể sử dụng pyinotify để theo dõi các thay đổi trên file này và ghi log mỗi khi file bị thay đổi. Dưới đây là cách bạn có thể làm điều đó:

  • Import các thư viện cần thiết: oslogging và pyinotify.
  • Tạo một bộ lọc logging để thêm hostname vào log.
  • Thiết lập logging để ghi vào file /var/log/custom.log.
  • Tạo một class EventHandler kế thừa từ pyinotify.ProcessEvent để xử lý sự kiện IN_MODIFY.
  • Thiết lập inotify watcher để theo dõi file /etc/passwd.
  • Bắt đầu vòng lặp notifier để bắt đầu theo dõi.

Dưới đây là code hoàn chỉnh:

import os
import logging
import pyinotify

# Custom logging filter to add hostname
class HostnameFilter(logging.Filter):
    def filter(self, record):
        record.hostname = os.uname()[1]
        return True

# Set up logging to /var/log/custom.log
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s',
    datefmt='%b %d %H:%M:%S',
    handlers=[logging.FileHandler('/var/log/custom.log')]
)
logger = logging.getLogger('passwd_monitor')
logger.addFilter(HostnameFilter())

class EventHandler(pyinotify.ProcessEvent):
    def process_IN_MODIFY(self, event):
        with open('/etc/passwd', 'r') as f:
            lines = f.readlines()
            for line in lines:
                logger.info(f"root::{line.strip()}")

# Set up inotify watcher
wm = pyinotify.WatchManager()
handler = EventHandler()
notifier = pyinotify.Notifier(wm, handler)
wm.add_watch('/etc/passwd', pyinotify.IN_MODIFY)

# Start monitoring
notifier.loop()

Giải thích:

Import các thư viện cần thiết:

import os
import logging
import pyinotify

Tạo một bộ lọc logging để thêm hostname vào log:

class HostnameFilter(logging.Filter):
    def filter(self, record):
        record.hostname = os.uname()[1]
        return True

Thiết lập logging để ghi vào file /var/log/custom.log:

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s',
    datefmt='%b %d %H:%M:%S',
    handlers=[logging.FileHandler('/var/log/custom.log')]
)
logger = logging.getLogger('passwd_monitor')
logger.addFilter(HostnameFilter())

Tạo một class EventHandler kế thừa từ pyinotify.ProcessEvent để xử lý sự kiện IN_MODIFY:

class EventHandler(pyinotify.ProcessEvent):
    def process_IN_MODIFY(self, event):
        with open('/etc/passwd', 'r') as f:
            lines = f.readlines()
            for line in lines:
                logger.info(f"root::{line.strip()}")

Thiết lập inotify watcher để theo dõi file /etc/passwd:

wm = pyinotify.WatchManager()
handler = EventHandler()
notifier = pyinotify.Notifier(wm, handler)
wm.add_watch('/etc/passwd', pyinotify.IN_MODIFY)

Bắt đầu vòng lặp notifier để bắt đầu theo dõi:

notifier.loop()

Chạy đoạn code này sẽ giúp bạn theo dõi các thay đổi trên file /etc/passwd và ghi log vào file /var/log/custom.log mỗi khi file này bị thay đổi.

5. Kết quả.

Đây là kết quả của 2 code trên được đọc trong file /var/log/custom.log.

Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::root:x:0:0:root:/root:/bin/bash
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::bin:x:2:2:bin:/bin:/usr/sbin/nologin

<đã lược bỏ bớt>

Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::demo:x:1001:1003::/home/demo:/bin/sh
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::demo1:x:1002:1004::/home/demo1:/bin/sh
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::demo2:x:1003:1005::/home/demo2:/bin/sh
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::demo3:x:1004:1006::/home/demo3:/bin/sh
Sep 19 16:59:24 ubuntu2204 passwd_monitor[20103]: root::demo

6. Tổng kết.

Bài viết này đã hướng dẫn bạn cách sử dụng Python để giám sát các thay đổi trong file /etc/passwd trên hệ thống Linux. Bằng cách sử dụng các thư viện oslogging, pyinotify và time, bạn có thể tạo ra một công cụ giám sát mạnh mẽ và tự động ghi lại các thay đổi vào file log. Điều này giúp tăng cường bảo mật và quản lý hệ thống một cách hiệu quả.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories