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:
- 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:
os
,logging
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ệnIN_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 os
, logging
, 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ả.