Bối cảnh & Lý do Tôi Chuyển sang osquery
Sáu tháng trước, tôi đang chạy OSSEC trên một vài máy chủ VPS, chìm ngập trong log nhiễu mà không có cách nào dễ dàng đặt câu hỏi cụ thể về hệ thống của mình. Điều tôi thực sự muốn là: “Hiển thị mọi tiến trình đã mở kết nối đến một IP nước ngoài trong 10 phút qua.” Với các stack log truyền thống, để trả lời câu hỏi đó cần phải phân tích file log, kết hợp nhiều lệnh grep và hy vọng rằng dữ liệu đúng đã được ghi lại đúng định dạng.
osquery giải quyết vấn đề đó. Thay vì theo dõi log, bạn truy vấn hệ thống như một cơ sở dữ liệu quan hệ. Mọi tiến trình đang chạy, socket đang mở, kernel module đã tải, cron job và tài khoản người dùng đều trở thành một hàng trong bảng SQL. JOIN giữa các bảng. Lọc bằng mệnh đề WHERE. Lên lịch truy vấn chạy mỗi 30 giây. Sau sáu tháng vận hành thực tế, đây là đánh giá thực lòng của tôi về việc xây dựng một EDR nhẹ nhàng, tự host xung quanh nó.
Đáng lưu ý về vị trí của nó: osquery không phải là sự thay thế cho Wazuh hay Suricata. Các công cụ đó xử lý tốt việc tổng hợp log và network IDS. osquery được xây dựng cho các câu hỏi pháp y theo yêu cầu về trạng thái endpoint — điều gì đang chạy ngay bây giờ, điều gì đã thay đổi trong giờ qua, tiến trình nào đã mở socket đó. Chúng hoạt động tốt khi kết hợp với nhau.
Cài đặt
Ubuntu / Debian
Meta mã nguồn mở osquery vào năm 2014 và hiến tặng cho Linux Foundation vào năm 2019. Các gói chính thức có tại pkg.osquery.io. Thiết lập chỉ mất khoảng hai phút:
# Quản lý key hiện đại (Ubuntu 22.04+ / Debian 11+)
# apt-key đã bị deprecated — dùng gpg --dearmor với signed-by thay thế
curl -fsSL https://pkg.osquery.io/deb/pubkey.gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/osquery.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/osquery.gpg] https://pkg.osquery.io/deb deb main" | \
sudo tee /etc/apt/sources.list.d/osquery.list
# Cài đặt
sudo apt-get update && sudo apt-get install osquery -y
# Kiểm tra
osqueryi --version
CentOS / RHEL
curl -L https://pkg.osquery.io/rpm/GPG | \
sudo tee /etc/pki/rpm-gpg/RPM-GPG-KEY-osquery
sudo yum-config-manager --add-repo https://pkg.osquery.io/rpm/osquery-s3-rpm.repo
sudo yum install osquery -y
Hai binary được cài lên hệ thống của bạn: osqueryi (shell SQL tương tác, lý tưởng cho việc điều tra ad-hoc) và osqueryd (daemon chạy các truy vấn theo lịch và liên tục ghi kết quả ra đĩa).
Cấu hình
Tạo File Cấu hình Chính
osqueryd đọc cấu hình từ /etc/osquery/osquery.conf. Đây là nơi bạn định nghĩa các truy vấn theo lịch, đường dẫn giám sát toàn vẹn file (FIM) và đích đến của log.
sudo mkdir -p /etc/osquery
sudo nano /etc/osquery/osquery.conf
Đây là cấu hình tôi chọn sau nhiều lần thử nghiệm trong thực tế:
{
"options": {
"config_plugin": "filesystem",
"logger_plugin": "filesystem",
"logger_path": "/var/log/osquery",
"disable_logging": "false",
"log_result_events": "true",
"schedule_splay_percent": "10",
"utc": "true"
},
"schedule": {
"running_processes": {
"query": "SELECT pid, name, path, cmdline, uid, parent FROM processes WHERE path != '';",
"interval": 60
},
"network_connections": {
"query": "SELECT p.pid, p.name, lc.local_address, lc.local_port, lc.remote_address, lc.remote_port FROM process_open_sockets lc LEFT JOIN processes p USING(pid) WHERE lc.remote_address NOT IN ('', '0.0.0.0', '::');",
"interval": 30
},
"user_accounts": {
"query": "SELECT username, uid, gid, directory, shell FROM users;",
"interval": 300
},
"cron_jobs": {
"query": "SELECT command, path, minute, hour FROM crontab;",
"interval": 300
},
"suid_binaries": {
"query": "SELECT path, username, groupname, permissions FROM suid_bin;",
"interval": 3600
},
"kernel_modules": {
"query": "SELECT name, size, status, address FROM kernel_modules;",
"interval": 3600
}
},
"file_paths": {
"system_configs": [
"/etc/%%",
"/etc/ssh/%%"
],
"user_ssh": [
"/root/.ssh/%%",
"/home/%/.ssh/%%"
],
"system_binaries": [
"/usr/bin/%%",
"/usr/sbin/%%",
"/bin/%%",
"/sbin/%%"
]
},
"exclude_paths": {
"system_configs": ["/etc/mtab"]
}
}
Trong phần schedule, mỗi truy vấn chạy theo khoảng thời gian đã định và ghi kết quả dưới dạng JSON. Trong phần file_paths, osquery đăng ký các thư mục đó với hệ thống con audit của kernel và phát ra sự kiện khi CREATE, MODIFY hoặc DELETE. Lưu ý cú pháp glob: %% khớp đệ quy với các thư mục con, trong khi một % đơn chỉ khớp đúng một phần đường dẫn.
Bật Linux Audit Framework cho FIM
Sự kiện FIM cần hệ thống con audit của kernel đang chạy. Tạo /etc/osquery/osquery.flags:
--disable_audit=false
--audit_allow_config=true
--audit_allow_process_events=true
--audit_allow_sockets=true
Khởi động Daemon
sudo systemctl enable osqueryd
sudo systemctl start osqueryd
sudo systemctl status osqueryd
Log được ghi vào /var/log/osquery/osqueryd.results.log dưới dạng JSON phân cách bởi dòng mới. Mỗi mục ghi lại tên truy vấn, Unix timestamp, trường action (added / removed cho truy vấn differential) và dữ liệu hàng.
Một bài học đầu tiên: hãy tạo thông tin xác thực mạnh, duy nhất cho mọi endpoint tổng hợp log hay trang quản trị bạn đặt trước những log này. Tôi dùng trình tạo mật khẩu tại toolcraft.app/vi/tools/security/password-generator cho mật khẩu máy chủ — nó chạy hoàn toàn trên trình duyệt mà không gửi dữ liệu qua mạng. Khi bạn đang tăng cường bảo mật hạ tầng, sự khác biệt đó rất quan trọng.
Xác minh & Giám sát
Điều tra Tương tác với osqueryi
Vào shell tương tác và bắt đầu đặt câu hỏi pháp y ngay lập tức. Không cần học ngôn ngữ truy vấn mới — chỉ cần SQL thuần túy:
-- Mọi tiến trình đang lắng nghe trên cổng không chuẩn
SELECT p.pid, p.name, p.path, lp.port, lp.protocol
FROM listening_ports lp
JOIN processes p USING(pid)
WHERE lp.port NOT IN (22, 80, 443, 3306)
ORDER BY lp.port;
-- Tiến trình có kết nối ra ngoài đến IP công khai
SELECT p.name, p.pid, p.cmdline,
s.remote_address, s.remote_port
FROM process_open_sockets s
JOIN processes p USING(pid)
WHERE s.remote_address NOT LIKE '10.%'
AND s.remote_address NOT LIKE '192.168.%'
AND s.remote_address NOT LIKE '172.16.%'
AND s.remote_address != '127.0.0.1'
AND s.remote_address != '';
-- File trong /etc được chỉnh sửa trong giờ qua
SELECT path, mtime, size
FROM file
WHERE path LIKE '/etc/%'
AND mtime > (strftime('%s','now') - 3600)
ORDER BY mtime DESC;
-- Tiến trình chạy với quyền root không nằm trong danh sách an toàn
SELECT pid, name, uid, path, cmdline
FROM processes
WHERE uid = 0
AND name NOT IN ('systemd','sshd','cron','rsyslogd','agetty');
Truy vấn Differential để Phát hiện Thay đổi
Chế độ differential là tính năng nổi bật của osquery trong công việc EDR. Khi osqueryd chạy một truy vấn theo lịch, nó so sánh tập kết quả hiện tại với lần chạy trước. Chỉ các hàng đã thay đổi mới được ghi vào log — hàng mới được gắn thẻ added, hàng bị xóa được gắn thẻ removed.
Điều đó biến truy vấn SUID binary ở trên thành một bộ phát hiện thay đổi tự động. Một SUID binary mới xuất hiện trên đĩa sẽ hiển thị như một hàng added trong vòng một giờ. Đây chính xác là cách tôi phát hiện một cryptominer trên một trong các máy chủ của mình — malware đã thả một binary wrapper setuid, và osquery đã gắn cờ nó trong vòng hai chu kỳ lên lịch, khoảng hai giờ sau khi bị xâm nhập.
Đọc Sự kiện Toàn vẹn File
-- Sự kiện FIM trong 10 phút qua (cần bật audit)
SELECT target_path, action, time, md5, sha256
FROM file_events
WHERE time > (strftime('%s', 'now') - 600)
ORDER BY time DESC;
Phát hiện sự kiện UPDATED trên /etc/passwd, /etc/sudoers, hay bất kỳ binary nào trong /usr/bin ngoài cửa sổ bảo trì có nghĩa là có gì đó đã thay đổi mà không nên. Hãy điều tra trước khi cho rằng đó là thông thường.
Xây dựng Pipeline Cảnh báo Đơn giản
Với một nhóm máy nhỏ — chẳng hạn 5 đến 20 máy chủ — việc theo dõi log kết quả và viết script cảnh báo là hoàn toàn khả thi mà không cần SIEM đầy đủ:
#!/bin/bash
# /usr/local/bin/osquery-alert.sh
# Đọc kết quả osquery và cảnh báo khi phát hiện mẫu đáng ngờ
LOGFILE="/var/log/osquery/osqueryd.results.log"
ALERT_LOG="/var/log/osquery-alerts.log"
tail -F "$LOGFILE" | while read -r line; do
# Cảnh báo khi có SUID binary mới
if echo "$line" | grep -q '"name":"suid_binaries"' && \
echo "$line" | grep -q '"action":"added"'; then
echo "$(date -u): SUID BINARY MỚI: $line" >> "$ALERT_LOG"
# Thêm thông báo của bạn vào đây (curl đến Telegram, sendmail, v.v.)
fi
# Cảnh báo khi có kết nối ra ngoài đến cổng cao
if echo "$line" | grep -q '"name":"network_connections"'; then
REMOTE_PORT=$(echo "$line" | python3 -c \
"import sys,json; d=json.loads(sys.stdin.read()); \
print(d.get('columns',{}).get('remote_port','0'))" 2>/dev/null)
if [[ "$REMOTE_PORT" =~ ^[0-9]+$ ]] && [[ "$REMOTE_PORT" -gt 1024 ]]; then
echo "$(date -u): KẾT NỐI ĐẦU RA ĐÁNG NGỜ $REMOTE_PORT: $line" >> "$ALERT_LOG"
fi
fi
done
# Chạy như một dịch vụ nền
sudo chmod +x /usr/local/bin/osquery-alert.sh
nohup /usr/local/bin/osquery-alert.sh &
Kiểm tra Truy vấn Theo lịch Đang Chạy
-- Kiểm tra trạng thái daemon osquery
SELECT pid, version, config_hash, config_valid
FROM osquery_info;
-- Xác nhận các truy vấn theo lịch đang thực thi
SELECT name, executions, last_executed, average_memory, last_status
FROM osquery_schedule;
-- Liệt kê tất cả bảng có sẵn trên host này
SELECT name, description
FROM osquery_registry
WHERE type = 'table'
ORDER BY name;
Sau sáu tháng, điều bất ngờ lớn nhất không phải là việc phát hiện các xâm nhập đang xảy ra — mặc dù osquery đã gắn cờ cryptominer đó trong vòng hai giờ. Lợi ích thực sự là khả năng hiển thị cơ sở: biết chính xác điều gì đang chạy, đang kết nối đến đâu, và điều gì đã thay đổi trong /etc vào thứ Ba tuần trước lúc 3 giờ sáng. Nhận thức tình huống đó vượt trội hơn cảnh báo phản ứng trong mọi trường hợp.
SQL cũng giúp việc onboarding thành viên nhóm trở nên dễ dàng. Một lập trình viên gần như không biết gì về bảo mật Linux có thể viết SELECT * FROM processes WHERE name = 'python3' và hiểu ngay những gì họ đang nhìn vào. Rào cản gia nhập thấp là lý do osquery vẫn ở trong thiết lập máy chủ tiêu chuẩn của tôi ngay cả khi có các công cụ thương mại nặng hơn.

