FAQ

Đây là hướng dẫn cài đặt, cấu hình và bảo trì Arcivéo Monitor. Các phần được nhóm lại: tổng quan chung, triển khai bảng điều khiển, kết nối các công cụ bảo mật, mô-đun tích hợp và chẩn đoán. Có thể sao chép lệnh bằng nút bên phải.

Bắt đầu từ đâu

01. Cài đặt bảng điều khiển — chọn phương thức

Việc cài đặt bảng điều khiển nằm trên các trang hướng dẫn từng bước riêng. Hãy chọn phương thức:

Nếu chưa chắc — hãy chọn cách tự động. Cẩm nang này vẫn là nguồn thông tin thống nhất về SSL, công cụ, cron và chẩn đoán — các trang cài đặt tham chiếu đến những mục của nó mà không lặp lại nội dung.

Tổng quan

02. Arcivéo Monitor là gì

Arcivéo Monitor — bảng điều khiển bảo mật máy chủ. Nó thu thập dữ liệu từ các công cụ đã cài đặt (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco v.v.) và hiển thị chúng trong một giao diện thống nhất với dashboard, bản đồ tấn công và các trang chi tiết cho từng công cụ.

Monitor không phải là công cụ bảo vệ chủ động — bản thân nó không chặn các cuộc tấn công. Nhiệm vụ của nó là tổng hợp thông tin từ các công cụ đang hoạt động và trình bày một cách tiện lợi.

03. Cách trình giám sát hoạt động trên máy chủ

Trình giám sát chỉ hoạt động cục bộ — phải được cài trên chính máy chủ mà nó giám sát. Không dùng SSH hay API từ xa.

Mọi lệnh (fail2ban-client, ufw status, ipset list v.v.) được dashboard chạy dưới danh nghĩa người dùng máy chủ web (thường là www-data, trên panel hosting là tài khoản site) với bộ quyền hạn hẹp sudo — chỉ cho các tiện ích cụ thể, không có quyền root chung. Kết quả được phân tích và hiển thị trong trình duyệt.

Với nhiều máy chủ, hãy cài trình giám sát riêng cho từng máy, mỗi máy một tên miền riêng.

04. Cách tính Điểm bảo mật

Điểm bắt đầu từ mức tối đa và giảm dần theo từng vấn đề được phát hiện:

  • UFW không hoạt động — −30
  • Fail2ban chưa chạy (không có jail hoạt động) — −25
  • Không có khóa WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum chưa được nạp — −10
  • ClamAV phát hiện mối đe dọa — −20
  • AIDE phát hiện thay đổi tệp — −15
  • SSL đã hết hạn — −30, hết hạn trong <14 ngày — −15, <30 ngày — −5
  • CrowdSec đã cài nhưng chưa chạy — −5
  • Suricata đã cài nhưng chưa chạy — −5
  • CSDL/bộ đệm (MySQL, PostgreSQL, Redis…) truy cập được từ bên ngoài — −10
  • Cho phép đăng nhập root qua SSH (PermitRootLogin yes) — −20
  • Có bản cập nhật bảo mật đang chờ cài — −5

Kết quả: 80+ = Được bảo vệ, 60–79 = Cần chú ý, <60 = Đang bị đe dọa.

Các mức trừ cho ClamAV, AIDE, CrowdSec và Suricata chỉ áp dụng nếu công cụ đã được cài đặt. Lynis và AIDE khi chưa khởi tạo cơ sở dữ liệu sẽ hiển thị là “không có dữ liệu” và không làm giảm điểm. Số lượng tấn công trong hôm nay được hiển thị trên dashboard, nhưng không ảnh hưởng đến Điểm bảo mật.

Cài đặt và giấy phép

05. WebAuthn — xác thực hai yếu tố

WebAuthn — chuẩn xác thực không cần mật khẩu thông qua khóa phần cứng. Hỗ trợ YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

Sau khi đăng nhập bằng mật khẩu, hệ thống yêu cầu xác nhận qua khóa đã đăng ký. Ngay cả khi mật khẩu bị lộ — không có khóa vật lý hoặc sinh trắc học thì cũng không thể đăng nhập.

Để thiết lập, mở Khóa WebAuthn trong menu bên và nhấn “Đăng ký khóa”. Hãy đăng ký ngay hai khóa: nếu khóa duy nhất bị mất hoặc hỏng, bạn sẽ không thể đăng nhập vào dashboard bằng khóa đó.

WebAuthn chỉ hoạt động qua HTTPS. Trên kết nối HTTP, việc đăng ký và đăng nhập bằng khóa không khả dụng.

06. Thông báo: Telegram và Email

Bảng điều khiển có thể gửi báo cáo bảo mật đến Telegram và email (theo nút bấm và theo lịch). Cấu hình trong mục “Cài đặt”.

Telegram. Cần token của botchat id:

  1. Trong Telegram, nhắn cho @BotFather/newbot → nhận token dạng 123456:ABC....
  2. Gửi cho bot mới của bạn một tin nhắn bất kỳ (để bot có thể trả lời bạn).
  3. Tìm chat id của bạn: nhắn cho bot @userinfobot, hoặc mở https://api.telegram.org/bot<TOKEN>/getUpdates và tìm "chat":{"id":...}.
  4. Dán token và chat id vào “Cài đặt” → Telegram, nhấn “Lưu và gửi thử”.

Email. Hai cách để chọn trong “Cài đặt” → Email:

  • SMTP — host, cổng (465/SSL hoặc 587/TLS), tên đăng nhập và mật khẩu hộp thư của bạn;
  • Resend — API hiện đại: nhập API key (re_...) và tên miền người gửi đã xác minh.
Nút “Gửi thử” sẽ kiểm tra kênh ngay lập tức. Lịch báo cáo tự động — qua cron (mục “Tất cả tác vụ cron”): nó kích hoạt việc gửi, còn các kênh được lấy từ cài đặt.

Trạng thái báo cáo: “CHÚ Ý” hoặc “OK”. Tiêu đề chuyển thành “CHÚ Ý” chỉ khi có sự cố thực sự hoặc có hành động đang chờ: phát hiện mối đe dọa ClamAV, thay đổi tệp trong AIDE, sự kiện nghiêm trọng của Falco (Emergency/Alert/Critical trong 24 giờ qua), dịch vụ bị sập trong Monit, cần khởi động lại, SSL sắp hết hạn (≤14 ngày) hoặc đang chờ cập nhật bảo mật. Nhiễu nền — bot dò SSH, IP bị fail2ban cấm, cảnh báo Suricata, cảnh báo Lynis và các yêu cầu đã bị ModSecurity chặn — không làm tăng trạng thái, nên những con số đó trong báo cáo tự thân không có nghĩa là “CHÚ Ý”.

07. Giấy phép — nhập và kích hoạt

Các module giám sát chi tiết (Lynis, UFW, ModSecurity, bản đồ tấn công, AIDE, ClamAV v.v.) sẽ mở khi có giấy phép hợp lệ. Nếu không có, dashboard, cài đặt và tài khoản vẫn hoạt động, còn các module hiển thị thẻ “Cần giấy phép”.

Sau khi mua trong tài khoản cá nhân, bạn có mã kích hoạt dạng ARCIVEO-XXXX-XXXX-XXXX-XXXX. Cần “kích hoạt” mã này cho domain của panel — thao tác này biến mã thành tệp giấy phép đã ký (khối [license]), rồi bạn dán vào panel.

Cách kích hoạt (3 bước):

  1. Lấy mã kích hoạt. Tài khoản cá nhân my.arciveo.com → mục “Giấy phép” / “Kích hoạt giấy phép” — sao chép mã ARCIVEO-….
  2. Kích hoạt mã cho domain của bạn. Cũng trong tài khoản cá nhân, mở “Kích hoạt giấy phép”, nhập: mã kích hoạt, email của bạn và domain của panel (địa chỉ mở monitor, ví dụ monitor.example.com). Nhấn kích hoạt — hệ thống tạo tệp giấy phép gắn với domain này và hiển thị trong ô có nút “Sao chép”.
  3. Dán key vào panel. Sao chép toàn bộ nội dung giấy phép → trong panel mở “Cài đặt” → khối “Giấy phép”, dán và nhấn “Lưu”. Các module mở khóa ngay.

Panel kiểm tra key bằng mật mã: chữ ký, sự gắn kết với domain và thời hạn hiệu lực.

Domain khi kích hoạt phải trùng khớp chính xác với địa chỉ panel. Lấy nó từ hằng APP_URL trong config.php và chỉ nhập tên host — không có https:// và không có tiền tố www. Kích hoạt chỉ một lần: mã biến thành giấy phép cho domain đã nhập và không kích hoạt lại được — nếu nhập sai domain, key sẽ không hợp với panel của bạn, còn mã thì đã bị tiêu tốn. Vì vậy hãy nhập domain thật cẩn thận.
Hết hạn hoặc đổi domain — trên đầu panel sẽ hiện cảnh báo. Giấy phép gắn với domain vĩnh viễn và không chuyển sang domain khác: với thời hạn mới hoặc domain mới cần key mới (mua trong tài khoản cá nhân và kích hoạt một lần).

08. Tệp config.php — toàn bộ cài đặt của panel

Tất cả tham số chính của panel được khai báo trong một tệp duy nhất config.php ở thư mục gốc (cạnh thư mục public/) bằng các hằng define() thông thường. Tệp được tạo khi cài đặt; hiếm khi cần sửa thủ công — chủ yếu khi đổi domain, di chuyển hoặc kết nối tới cơ sở dữ liệu khác. Sau mỗi lần sửa, hãy khởi động lại PHP-FPM (nếu không, do OPcache mà thay đổi sẽ không được áp dụng).

Điền giá trị của bạn vào những chỗ được tô sáng; phần còn lại giữ nguyên:

// --- Cơ sở dữ liệu --- define('DB_HOST', 'localhost'); // giữ nguyên define('DB_NAME', 'db_name'); // giá trị đã đặt khi tạo CSDL define('DB_USER', 'user'); // giá trị đã đặt khi tạo CSDL define('DB_PASS', 'db_password'); // giá trị đã đặt khi tạo CSDL define('DB_CHARSET', 'utf8mb4'); // giữ nguyên // --- Ứng dụng --- define('APP_URL', 'https://monitor.example.com'); // địa chỉ panel, không có dấu gạch chéo ở cuối define('TIMEZONE', 'Asia/Ho_Chi_Minh'); // múi giờ của bạn // --- Thời gian phiên --- define('SESSION_LIFETIME', 28800); // thời gian nhàn rỗi trước khi đăng nhập lại, giây (28800 = 8 giờ)

Cơ sở dữ liệu. Thông tin kết nối tới MySQL/MariaDB:

  • DB_HOST — máy chủ CSDL, hầu như luôn là localhost;
  • DB_NAME — tên cơ sở dữ liệu của panel;
  • DB_USER — người dùng CSDL (chỉ truy cập được cơ sở dữ liệu của mình);
  • DB_PASS — mật khẩu của người dùng này;
  • DB_CHARSET — bảng mã kết nối, hãy giữ utf8mb4.

Ứng dụng.

  • APP_URL — địa chỉ đầy đủ của panel (vd. https://monitor.example.com). Phải trùng với domain mà giấy phép đã được kích hoạt — nếu không, khóa sẽ bị từ chối (xem mục “Giấy phép”);
  • TIMEZONE — múi giờ PHP: chỉ ảnh hưởng tới cách panel hiển thị ngày và giờ. Không ảnh hưởng tới thời điểm chạy các tác vụ cron — nơi đó dùng múi giờ của hệ thống (xem “Tất cả tác vụ cron”).

Thời gian phiên. SESSION_LIFETIME — thời gian chờ nhàn rỗi của phiên tính bằng giây (trượt: làm mới khi có hoạt động). Mặc định 28800 = 8 giờ; sau khoảng thời gian không hoạt động này, panel sẽ yêu cầu đăng nhập lại. Ví dụ, 3600 = 1 giờ, 86400 = một ngày.

Ghi nhật ký lỗi. Lỗi không bao giờ hiển thị cho khách truy cập mà được ghi vào logs/php_errors.log — có thể xem chúng ở trang “Nhật ký ứng dụng”. Các dòng này (display_errors=0, log_errors=1, đường dẫn error_log) thường không cần đổi — cài đặt được khai báo ngay trong tệp và không phụ thuộc vào php.ini.

config.php — tệp bí mật. Nó chứa mật khẩu CSDL. Tệp nằm ở thư mục gốc của panel (cạnh public/), và với panel này thì web-root (DocumentRoot) chính là thư mục gốc của panel, không phải public/. Bản thân tệp không bị “rò rỉ”: trong .htaccess ở gốc đã có lệnh cấm rõ ràng cho nó (Require all denied) — máy chủ trả về 403. Ngay cả khi không có quy tắc này thì mã nguồn cũng không rò rỉ: đây là PHP — máy chủ thực thi nó chứ không trả về dưới dạng văn bản. Để chắc chắn: đừng đưa nó lên các kho công khai và đừng gửi cho bộ phận hỗ trợ với mật khẩu thật. Quyền của tệp là 640.
Khi di chuyển hoặc khôi phục quyền truy cập, tệp này là nguồn thông tin chính: tên CSDL, người dùng và mật khẩu đều lấy từ đây (xem các mục “Cập nhật và di chuyển panel” và “Khôi phục quyền truy cập”).

Công cụ bảo mật

09. Tường lửa UFW

UFW (Uncomplicated Firewall) — giao diện đơn giản cho nftables/iptables. Nó chặn mọi cổng đến, trừ những cổng được cho phép rõ ràng. Trang “Tường lửa UFW” hiển thị trạng thái và các quy tắc.

sudo apt install ufw # Cho phép SSH (bắt buộc TRƯỚC khi bật!) và web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Chặn truy cập DB từ bên ngoài (chỉ truy cập nội bộ) sudo ufw deny 3306 # Bật và kiểm tra sudo ufw enable sudo ufw status verbose
Trước khi chạy ufw enable, hãy chắc chắn cho phép SSH (ufw allow OpenSSH), nếu không bạn sẽ mất quyền truy cập máy chủ.
“Mức phơi bày ra bên ngoài” trên dashboard có tính đến UFW: cổng bị chặn bởi quy tắc deny không được xem là truy cập được từ bên ngoài.
Skipping adding existing rule — đây không phải lỗi. Đó là cách UFW báo rằng quy tắc y hệt đã tồn tại và không thêm lại nó. Khi chạy lại tự động cấu hình (vốn có tính idempotent), đây là thông báo bình thường — không cần xử lý gì.

10. Cài đặt Fail2ban

Tự động chặn IP khi vượt quá số lần đăng nhập thất bại. Phân tích log của SSH, nginx, Apache và các dịch vụ khác.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Kiểm tra trạng thái: sudo fail2ban-client status
Cấu hình hoạt động (jail.local với hàng chục jail và tự động chặn từ ipsum) — ở phần tiếp theo.

11. Cấu hình hoạt động Fail2ban + ipsum

Cài đặt cơ bản đã ở trên. Ở đây là cấu hình thực tế cho hàng chục jail đang hoạt động và hàng nghìn lượt chặn: thiết lập chung, các jail chính và tự động chặn IP độc hại từ danh sách ipsum.

Tệp /etc/fail2ban/jail.local — thiết lập chung và các jail quan trọng nhất:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Chặn lũy tiến: mỗi lần tái phạm — chặn lâu hơn bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # chặn vĩnh viễn khi dò mật khẩu SSH findtime = 3600 # Kẻ tái phạm: ai bị chặn nhiều lần sẽ bị chặn vĩnh viễn [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Dịch vụ web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … và các jail còn lại theo dịch vụ (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
Trong ignoreip nhớ nhập IP của bạn và các mạng tin cậy, nếu không bạn có thể tự chặn chính mình. Sau khi sửa: sudo fail2ban-client reload.

Tự động nạp danh sách chặn ipsum — vào cron của root (sudo crontab -e): level 1 (hơn 100 nghìn IP) được nạp vào tập ipsum và bị cắt ở tường lửa (chi tiết ở mục “Danh sách chặn IPset”):

# 04:00 — cập nhật ipset ipsum (level 1, độ bao phủ tối đa): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Tập phải có tên ipsum — đây chính là tập mà dashboard đọc (thẻ “IPset ipsum”). Cấp độ: levels/1.txt — bao phủ tối đa, levels/3.txt — chính xác hơn (từ 3 nguồn trở lên).

Vì sao “Màn hình giám sát an ninh” được chia thành hai vùng. Việc bảo vệ hoạt động ở hai cấp độ và dashboard không trộn lẫn chúng:

  • Tấn công thực tế (phản ứng) — tất cả những gì fail2ban bắt được: các nỗ lực xâm nhập thực (jail sshd, apache-*, nginx-* v.v.) và kẻ tái phạm nguy hiểm (jail recidive — những kẻ đã bị chặn nhiều lần). Đây là các IP thực sự đã tấn công vào bạn — chúng hiện trên bản đồ tấn công và “Dòng thời gian”.
  • Chặn phòng ngừa (chủ động) — danh sách chặn công khai gồm các IP độc hại đã biết ipset ipsum, bị cắt ở tường lửa bằng quy tắc DROP. Phần lớn các địa chỉ này còn chưa hề chạm tới máy chủ của bạn — chúng bị chặn từ trước; bộ đếm “IPset ipsum” cho biết đã cắt được bao nhiêu địa chỉ theo cách phòng ngừa.

Khác biệt rất đơn giản: phản ứng — “những kẻ này đã tấn công và bị chặn”, phòng ngừa — “những kẻ này bị chặn trước cả khi ra tay”. Trước đây list-3 ipsum bị nhồi giả tạo vào recidive (từ đó có cách chia cũ “recidive theo danh sách”); nay recidive chỉ gồm kẻ tái phạm thực sự, còn phòng ngừa hoàn toàn nằm ở tường lửa.

12. Danh sách chặn IPset (ipsum)

ipsum — danh sách IP độc hại công khai, cập nhật hàng ngày. Monitor hiển thị số địa chỉ đã tải trên dashboard và bản đồ tấn công, đồng thời tính vào Điểm bảo mật (−10 nếu tập chưa được tải).

Phương án tối giản không dùng fail2ban — tập ipsum riêng với việc chặn qua iptables:

# Tạo tập (một lần duy nhất): sudo ipset create ipsum hash:ip # Script cập nhật /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron (sudo crontab -e, hàng ngày lúc 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Phương án mở rộng với fail2ban-recidive — xem mục “Cấu hình hoạt động Fail2ban + ipsum”.
ipset nằm trong bộ nhớ và mất khi khởi động lại. Chỉ riêng cron hàng ngày sẽ để tập rỗng từ lúc reboot đến lần chạy tiếp theo (dashboard hiển thị 0). Hãy nạp tập cả khi khởi động — tách phần nạp thành script và gắn vào @reboot. Đồng thời lệnh create … -exist đặt giới hạn maxelem 300000 (mặc định 65536 — level 1 không vừa, sẽ báo “Hash is full”):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — hàng ngày lúc 04:00 VÀ mỗi lần khởi động: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Cơ chế khi cài đặt tự động. Script nạp toàn bộ danh sách level 1 (hơn 100 nghìn IP) vào tập ipsum và, nếu tường lửa do trình cài đặt quản lý (VPS mới — profile “Đầy đủ”/“Rút gọn”), gắn tập vào UFW bằng quy tắc DROP — lưu lượng từ các IP này thực sự bị chặn. Quy tắc nằm sau ESTABLISHED,RELATED, nên các kết nối hiện tại (kể cả SSH của bạn) không bị ngắt — chỉ chặn các kết nối mới từ danh sách. Tập được khôi phục khi dịch vụ ipsum-load.service khởi động trước tường lửa (nếu không UFW sẽ không lên được), và được cron cập nhật lúc 04:00. Trên máy chủ đã cấu hình sẵn (dashboard, tường lửa riêng), trình cài đặt không đụng vào tường lửa — ở đó ipsum vẫn là danh sách cho dashboard và bản đồ tấn công, còn quy tắc DROP nếu muốn thì thêm thủ công (phương án tối giản với iptables … --match-set ipsum … -j DROP — ở trên). Khi cài đặt tự động thì không cần làm gì thủ công.

13. Cài đặt CrowdSec

Giải pháp thay thế hiện đại cho Fail2ban với threat intelligence cộng đồng: chặn từ cộng đồng cùng với luật riêng của bạn. Cần một bouncer riêng để áp dụng lệnh chặn vào tường lửa.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer cho iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Kiểm tra trạng thái: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Trạng thái “Chưa chạy” trong bảng điều khiển = dịch vụ đã cài nhưng dịch vụ chưa hoạt động (monitor kiểm tra qua systemctl is-active crowdsec). Khởi động: sudo systemctl enable --now crowdsec; nếu bị lỗi hãy xem sudo journalctl -u crowdsec -n 30. Áp dụng tương tự cho mọi dịch vụ có trạng thái “Chưa chạy” (Suricata, Falco, Monit, MySQL).
“0 kịch bản” hoặc “0 bouncer” trên dashboard. CrowdSec mặc định gần như trống rỗng — không có collection thì nó không phát hiện gì, và không có bouncer đã đăng ký thì lệnh chặn không được áp dụng vào tường lửa. Hãy cài các collection cơ bản và đảm bảo bouncer có trong danh sách:
# Collection cơ bản (Linux + SSH + máy chủ web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer phải có trong danh sách và ở trạng thái kết nối hoạt động: sudo cscli bouncers list
Trong log của bouncer có stream halted / lệnh chặn không được áp dụng. Đây là api-key mồ côi: bouncer đã bị xóa khỏi cscli bouncers list, nhưng key cũ của nó vẫn còn trong /etc/crowdsec/bouncers/*.yaml. Hãy đăng ký lại bouncer và ghi key mới vào:
sudo cscli bouncers add fw-bouncer # sẽ xuất ra api_key mới # ghi key này vào api_key: trong /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
Cài đặt tự động (hồ sơ “Bảo vệ toàn diện”) sẽ tự cài các collection và đăng ký firewall-bouncer — chỉ cần làm thủ công khi cài đặt thủ công hoặc sau khi can thiệp thủ công vào CrowdSec.

14. Cài đặt AIDE

AIDE (Advanced Intrusion Detection Environment) tạo ảnh chụp hệ thống tập tin và mỗi lần kiểm tra sẽ báo cáo các thay đổi trong /etc, /bin, /usr. Sau khi cài đặt bắt buộc phải khởi tạo cơ sở dữ liệu (aideinit).

sudo apt install aide # Khởi tạo cơ sở dữ liệu (5–15 phút): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: thư mục /var/lib/aide được tạo ở chế độ 700 (chủ sở hữu _aide), # và dashboard (www-data) không thấy cơ sở dữ liệu → hiển thị “Chưa khởi tạo”. # Mở quyền truy cập vào thư mục (các tập tin cơ sở dữ liệu vẫn giữ 600): sudo chmod 755 /var/lib/aide # Lần kiểm tra đầu tiên CÓ GHI vào log mà monitor đọc. # Trên Ubuntu/Debian aide cần --config rõ ràng (nếu không sẽ báo “missing configuration”; # bản binary aide.wrapper không còn được cung cấp trong các phiên bản mới): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Trong lúc aideinit terminal sẽ đứng ở dòng Running aide --init... trong 5–15 phút — đây là bình thường (băm toàn bộ hệ thống tập tin, tải nặng lên đĩa). Đừng ngắt bằng Ctrl+C. Nếu tiến trình “treo” mà không hiển thị gì — có thể nó đang chờ trả lời một yêu cầu ẩn Overwrite existing aide.db.new [Yn]? (nhấn Y). Kiểm tra hoạt động từ một phiên khác: pgrep -af aide.
Lỗi aideinit: “21_aide_spamassassin … printf: invalid number” (return code 20) — lỗi đã biết của đoạn cấu hình AIDE trong Ubuntu 22.04. Cơ sở dữ liệu không được tạo. Hãy chuyển đoạn snippet bị lỗi ra ngoài rồi thử lại:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Trạng thái trên dashboard. “Chưa khởi tạo” = panel không thấy tập tin cơ sở dữ liệu: hoặc aideinit chưa chạy, hoặc (Ubuntu 24.04) thư mục /var/lib/aide được tạo ở chế độ 700 và www-data không truy cập được — khắc phục bằng sudo chmod 755 /var/lib/aide (xem khối trên). “Chưa có lần kiểm tra nào” = đã có cơ sở dữ liệu nhưng chưa chạy kiểm tra — đây không phải lỗi. Monitor đọc kết quả từ /var/log/aide/aide.log.
Kiểm tra định kỳ → log cho panel. /etc/cron.daily/aide mặc định trên Ubuntu/Debian mới có thể không ghi /var/log/aide/aide.log đúng định dạng (và aide.wrapper cũng không còn trong đó). Đáng tin cậy hơn là thêm cron riêng với --config rõ ràng — nó ghi log dưới quyền root ở chế độ 644, và monitor đọc được mà không cần nhóm bổ sung:
# sudo crontab -e — kiểm tra hằng ngày lúc 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Chạy ngay bây giờ, không cần đợi lịch: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Cài đặt tự động đã làm toàn bộ những việc này: chmod 755 /var/lib/aide và cron kiểm tra lúc 02:00 — không cần làm thủ công gì cả.
Hãy thực hiện khởi tạo lần đầu trên server sạch — trước khi cài các ứng dụng web. Sau các thay đổi hợp lệ, hãy tạo lại cơ sở dữ liệu: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Cài đặt ClamAV

Trình quét diệt virus cho Linux. Đặc biệt hữu ích để kiểm tra /var/www tìm PHP-shell và mã độc.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Cập nhật cơ sở dữ liệu chữ ký: sudo freshclam # Quét thư mục thủ công: sudo clamscan -r /var/www --infected
Daemon clamd báo “Không hoạt động” sau enable --now? Ba nguyên nhân thường gặp:

1. Trong cấu hình còn dòng Example — clamd không chịu khởi động khi dòng này còn:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. Chưa tải cơ sở dữ liệu chữ ký — clamd không khởi động khi thiếu nó:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. Chỉ đang tải — clamd nạp ~8 triệu chữ ký vào bộ nhớ mất 30–60 giây. Hãy đợi và kiểm tra: systemctl is-active clamav-daemon (trạng thái activating → vẫn đang tải).

Chẩn đoán: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Trên dashboard hiện “Số tệp đã quét: 0” / “Lần quét cuối: —”? Daemon clamd chỉ giữ chữ ký trong bộ nhớ, bản thân nó không quét theo lịch. Bảng điều khiển hiển thị kết quả quét theo lịch, nên cần một cron để quét và ghi log. Trình tự động cài đặt sẽ đặt wrapper /usr/local/bin/clamav-scan.sh và cron lúc 01:30 — sau lần chạy đầu tiên, “Số tệp đã quét” và “Lần quét cuối” sẽ được điền. Chạy ngay không cần đợi lịch: sudo /usr/local/bin/clamav-scan.sh.

16. Cài đặt Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — trình quét phần mềm độc hại chuyên về các mối đe dọa web: PHP-shell, backdoor web, trình tải mã độc. Dùng engine ClamAV và bổ sung thêm chữ ký riêng.

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # Cập nhật chữ ký: sudo maldet -u # Quét /var/www: sudo maldet -a /var/www
LMD và ClamAV phối hợp rất tốt với nhau. Báo cáo gần nhất: maldet --report.
Khi cài đặt có thể xuất hiện dòng update-rc.d: error: unable to read /etc/init.d/maldet — dòng này vô hại. maldet không dùng init.d, việc cập nhật chữ ký và quét được chạy qua /etc/cron.daily/maldet. Nếu bên dưới thấy installation completed thì mọi thứ đã cài xong.
Trên trang, LMD hiện “Chưa cài đặt” dù đã cài? maldet không được cài qua apt mà nằm ở /usr/local/maldetect, và khi bật open_basedir thì sự tồn tại của nó được kiểm tra qua shell — xem mục “Trang trống dù dữ liệu đã có trên máy chủ”.

17. Cài đặt Suricata

Hệ thống phát hiện xâm nhập mạng: phân tích lưu lượng ở cấp độ gói tin và biết hàng nghìn chữ ký tấn công. Bổ sung cho ModSecurity (ModSecurity hoạt động ở cấp HTTP, còn Suricata ở cấp TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Tải các quy tắc mới nhất: sudo suricata-update sudo systemctl enable --now suricata
Suricata “Đang chạy” nhưng bảng điều khiển không hiển thị cảnh báo / số sự kiện là 0? Suricata ghi /var/log/suricata/eve.json dưới quyền root với chế độ 750 cho thư mục, nên máy chủ web (www-data) không đọc được. Hãy mở quyền truy cập thư mục — các tệp bên trong vẫn được bảo vệ:
sudo chmod o+rx /var/log/suricata
Cài đặt tự động sẽ tự làm việc này — không cần thao tác thủ công.

18. Cài đặt Falco

Chặn bắt các lời gọi hệ thống qua eBPF/kernel module và phát hiện bất thường theo thời gian thực: shell từ nginx, tiến trình web đọc /etc/passwd, ghi vào /bin, v.v.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
Monitor đọc sự kiện Falco qua journalctl -u falco (không cần sudo — thông qua nhóm systemd-journal). Hãy đảm bảo www-data thuộc nhóm này — xem “Cấu hình sudo” (mục 2) ở trang cài đặt thủ công.
“0 sự kiện trong 24 giờ” là bình thường, không phải lỗi. Falco hoạt động theo sự kiện: nó im lặng khi mọi thứ ổn, và chỉ ghi sự kiện khi có bất thường (shell từ tiến trình web, đọc /etc/passwd, ghi vào thư mục hệ thống). Không có sự kiện nghiêm trọng nào trong một ngày trên máy chủ yên tĩnh là trạng thái khỏe mạnh.
Với dashboard, xuất ra file đáng tin cậy hơn. Đọc qua journalctl đòi hỏi quyền truy cập nhật ký; để dashboard thấy sự kiện ổn định, quá trình tự cài đặt bật file_output cho Falco → /var/log/falco/falco.log và đặt UMask=0022 cho dịch vụ (log được web server đọc). Trên bản cài đặt mới, không cần cấu hình thủ công điều này.

19. Cài đặt ModSecurity (WAF)

ModSecurity — tường lửa web (WAF) cho Apache hoặc Nginx. Chặn các cuộc tấn công ở tầng ứng dụng: SQL injection, XSS, vượt đường dẫn, trình quét.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Bộ quy tắc OWASP Core Rule Set: sudo apt install modsecurity-crs # BẮT BUỘC: không có file này thì bộ máy quy tắc bị tắt sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # Kiểm tra: phải trả về 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Chỉ cài gói thôi thì chưa bảo vệ được gì. Apache nạp cấu hình bằng dòng IncludeOptional /etc/modsecurity/*.conf, còn gói chỉ đặt modsecurity.conf-recommended — file này không khớp mẫu *.conf. Nếu không sao chép nó thành modsecurity.conf, SecRuleEngine vẫn ở trạng thái Off: module đã nạp, quy tắc CRS đã nạp, nhưng lưu lượng không được kiểm tra và không tạo audit log. Chế độ trung gian DetectionOnly chỉ ghi sự kiện vào log mà không chặn yêu cầu — dashboard hiển thị nó màu vàng.

Quyền truy cập audit log của dashboard. Log /var/log/apache2/modsec_audit.log thuộc quyền root (quyền 640), người dùng web không đọc được. Dashboard lấy dữ liệu qua một lớp bọc — hãy tạo nó:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # trong /etc/sudoers.d/monitor (người dùng = người mà PHP-FPM chạy dưới quyền đó): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Lớp bọc lấy chỉ thị SecRuleEngine cuối cùng không có thụt lề: các dòng có thụt lề nằm bên trong khối <LocationMatch>/<Directory> (ví dụ tắt WAF cho phpMyAdmin) và không quyết định chế độ toàn cục.
Người dùng trong sudoers phải trùng với người dùng của FPM pool: trên Apache/Debian thông thường đó là www-data, còn trong HestiaCP pool của site chạy dưới quyền chủ site (ví dụ admin) — hãy kiểm tra grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Nếu site đứng sau proxy Nginx (HestiaCP), Apache thấy client chính là proxy — dashboard lấy IP thật của kẻ tấn công từ header X-Forwarded-For. Chỉ những giao dịch có quy tắc kích hoạt mới được đưa vào thống kê: chỉ thị SecAuditLogRelevantStatus ghi vào audit log mọi phản hồi 4xx/5xx, nên các lỗi 403/500 thông thường cũng lọt vào đó — dashboard không tính chúng là sự kiện WAF.
Khối ---RULES--- cần cho mục “Tất cả quy tắc đang hoạt động” — dashboard hiển thị không chỉ những quy tắc đã kích hoạt mà toàn bộ quy tắc CRS đã nạp + quy tắc tùy chỉnh. Ba đường dẫn trong vòng lặp for f in … là các vị trí điển hình cho quy tắc CRS và phần bổ sung cục bộ; nếu bố trí của bạn khác (gói đặt file vào thư mục riêng, hoặc quy tắc tùy chỉnh không nằm ở /etc/modsecurity/custom-rules.conf), hãy tìm đường dẫn thật bằng lệnh sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null rồi thay chúng vào danh sách. Nếu lớp bọc cũ (không có phần này) — mục đó chỉ hiển thị cảnh báo “không khả dụng”, phần còn lại của trang vẫn hoạt động như trước.

20. Cài đặt Auditd

Auditd (Linux Audit Daemon) ghi lại các lệnh gọi hệ thống ở cấp nhân: đăng nhập và đăng xuất, lệnh sudo, các lần xác thực thất bại, thay đổi tệp. Monitor hiển thị lượt đăng nhập, các lần thất bại và lệnh sudo trong ngày hôm nay.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Kiểm tra trạng thái và sự kiện: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Monitor đọc sự kiện qua ausearch (/usr/sbin/ausearch) và khi cần thì từ /var/log/audit/audit.log bằng lệnh tail. Cả hai phải có trong sudoers.

21. Cài đặt Monit

Theo dõi các dịch vụ (nginx, php-fpm, mysql, v.v.) và tự khởi động lại khi chúng ngừng chạy. Có thể gửi cảnh báo qua email.

sudo apt install monit sudo systemctl enable --now monit # Tệp cấu hình: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Monitor lấy danh sách dịch vụ qua monit status. Trong /etc/monit/monitrc phải bật giao diện HTTP (khối set httpd với allow localhost), nếu không monit status sẽ báo lỗi.
Trên dashboard hiện “0 dịch vụ đang được theo dõi”? Có hai nguyên nhân. (1) Giao diện HTTP đang tắt — trong monitrc dòng set httpd bị chú thích (mặc định ở dạng # set httpd port 2812 …). Hãy bỏ chú thích khối đó và cho phép localhost. (2) Bản thân httpd được bật không theo dõi gì cả — Monit chỉ đếm những gì được mô tả trong các stanza check; thiếu chúng thì danh sách vẫn trống dù giao diện đang chạy. Cấu hình tối thiểu hoạt động được:
# /etc/monit/conf.d/00-httpd — giao diện HTTP cho localhost: set httpd port 2812 use address localhost allow localhost # ví dụ các stanza check (những gì cần theo dõi): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # kiểm tra cú pháp (Control file syntax OK) sudo systemctl reload monit sudo monit status
Trình cài đặt tự động đặt sẵn conf.d với httpd trên cổng 2812 và một bộ kiểm tra — trên bản cài mới không cần cấu hình thủ công.
Dịch vụ ở trạng thái “Có lỗi”? Monitor chỉ hiển thị trạng thái và cố ý không khởi động lại dịch vụ từ bảng điều khiển web (điều đó sẽ là thực thi lệnh root từ xa ngay trong panel bảo mật). Chẩn đoán và khởi động lại — qua SSH bằng Monit:
sudo monit status <service> # nguyên nhân lỗi sudo monit restart <service> # khởi động lại qua Monit # nếu Monit không dựng được dịch vụ — xem unit riêng của nó: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Cài đặt PSAD (phát hiện quét cổng)

PSAD phân tích nhật ký iptables và phát hiện việc quét cổng cùng các cuộc tấn công mạng, gán cho mỗi nguồn một mức độ nguy hiểm (1–5). Bổ trợ cho fail2ban và Suricata.

sudo apt install psad # PSAD đọc log iptables — cần bật ghi log (UFW tự làm việc này). # Với iptables thuần, hãy thêm quy tắc LOG vào chuỗi INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Monitor đọc dữ liệu qua psad --Status (cần có trong sudoers). Nếu không bật ghi log iptables, trang sẽ trống — điều này là bình thường khi chưa có lần quét nào.

23. AppArmor / SELinux (kiểm soát truy cập)

Mandatory Access Control giới hạn những tệp và tài nguyên mà một chương trình có thể truy cập, ngay cả khi nó đã bị xâm nhập. Trên Ubuntu/Debian, AppArmor được dùng mặc định (thường đã được cài sẵn và đang hoạt động).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # kiểm tra các profile
Monitor đọc trạng thái qua aa-status (cần có trong sudoers). Nó hiển thị số profile ở chế độ enforce/complain và các tiến trình không có profile.

“Số profile đã nạp” nhiều hơn enforce + complain là điều bình thường. Trong AppArmor 4.x (Ubuntu 24.04 trở lên) có thêm chế độ unconfined: profile được nạp vào nhân nhưng không giới hạn gì cả. Ubuntu gắn nhãn này cho hàng chục profile của các chương trình dùng user namespaces (trình duyệt, ứng dụng torrent và tương tự). Khi có những profile như vậy, thẻ “Số profile đã nạp” chuyển sang màu hổ phách và hiển thị số lượng của chúng — ví dụ unconfined: 90 trong tổng số 120 profile đã nạp và 26 ở chế độ enforce. Chỉ những profile ở enforce mới thực sự bảo vệ; trên Ubuntu 22.04 (AppArmor 3.x) không có chế độ này và các con số luôn khớp nhau.

sudo aa-status | grep -E "profiles are" # phân tách theo chế độ sudo aa-enforce /etc/apparmor.d/profile-name # chuyển profile sang enforce
Chỉ nên chuyển sang enforce những profile mà Ubuntu cố ý để ở unconfined khi bạn thực sự hiểu rõ: chúng bị tắt không phải do lỗi, mà vì nếu không thì chính các chương trình đó sẽ hỏng. Còn các profile ở complain là chuyện khác: quy tắc ở đó đã được viết sẵn và chỉ là chưa được áp dụng mà thôi.

24. Cài đặt debsums (kiểm tra tính toàn vẹn gói)

debsums kiểm tra xem tệp của các gói đã cài có khớp với giá trị băm từ kho lưu trữ không — phát hiện các tệp nhị phân hệ thống bị thay thế (bổ sung cho AIDE). Quét đầy đủ mất 1–2 phút nên chạy qua cron, còn dashboard đọc kết quả từ data/debsums/debsums.log và tự phân loại (chỉ tệp nhị phân và thư viện là quan trọng).

Tác vụ nằm trong cron của root (sudo crontab -e). Trình bao bọc debsums-scan.sh có sẵn được đặt vào /usr/local/bin/ (chmod +x; xem bảng tóm tắt các tác vụ cron) và tự ghi báo cáo vào data/debsums/ của dashboard.

sudo apt install debsums # Dòng cron (hằng ngày lúc 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Trình bao bọc debsums-scan.sh tự tìm data/ của dashboard — không cần khai báo đường dẫn.

Thay đổi trong /etc/ (tệp cấu hình) và /usr/share/ (tài nguyên) trên máy chủ thường là bình thường — dashboard đánh dấu chúng bằng màu riêng. Đáng lo là thay đổi ở tệp nhị phân và thư viện (/bin, /sbin, /usr/lib v.v.) — thẻ “Tệp nhị phân / thư viện” hiển thị đúng những thay đổi này.

25. Cấu hình báo cáo Lynis

Lynis chạy thủ công hoặc theo cron. Báo cáo phải được lưu vào thư mục data/lynis/ của dự án — monitor đọc tệp lynis-report.dat.

# Chạy một lần (thay bằng đường dẫn tới thư mục gốc của bảng điều khiển): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Kiểm tra hằng ngày — dòng cron (trình bao lynis-scan.sh sẵn có trong /usr/local/bin/, xem bản tóm tắt): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Sau lần chạy đầu tiên, trang “Audit Lynis” sẽ hiển thị ngay hardening index, các cảnh báo và khuyến nghị.
Nút “Chạy kiểm tra” trên trang Lynis. Nút này chạy lynis-scan.sh ở chế độ nền ngay từ bảng điều khiển (không cần đợi cron): hiển thị “Đang quét…” và tự cập nhật báo cáo khi hoàn tất. Để làm được điều này, người dùng web cần một dòng sudoers để chạy script — trình cài đặt tự động thêm nó vào /etc/sudoers.d/monitor. Nếu bảng điều khiển được cài thủ công/trước đây, hãy thêm dòng đó với cùng người dùng đã ghi trong tệp:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

26. Cấu hình báo cáo Logwatch

Logwatch phải lưu báo cáo hằng ngày vào thư mục data/logwatch/ của dự án ở định dạng .txt. Monitor hiển thị báo cáo mới nhất và kho lưu trữ.

# Hằng ngày (6:00) — dòng cron (trình bao logwatch_daily.sh có sẵn trong /usr/local/bin/, xem phần tóm tắt): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Mô-đun dashboard

27. Giám sát mạng (tích hợp sẵn)

Giám sát mạng không cần cài đặt — đây là một trang tích hợp sẵn trong bảng điều khiển. Nó hiển thị trạng thái mạng của máy chủ từ các nguồn cục bộ:

  • giao diện và lưu lượng — từ /proc/net/dev;
  • trạng thái liên kết (UP/DOWN) và IP — qua ip;
  • kết nối và cổng đang lắng nghe — qua ss;
  • sự kiện mạng của nhân trong 24 giờ — qua journalctl -k.

Ba nguồn đầu hoạt động không cần sudo, nên giao diện, lưu lượng, kết nối và cổng hiển thị ngay lập tức. Khối “Sự kiện nhân” dùng journalctl -k — được đọc qua nhóm systemd-journal (“Cấu hình sudo”, mục 2), không cần sudo. Kiểm tra rằng người dùng web truy cập được mọi thứ:

# Kiểm tra dưới danh nghĩa www-data (PHP chạy bằng người dùng này): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
Khối “Sự kiện mạng của nhân” hiển thị các sự kiện của ngăn xếp mạng nhân (thay đổi liên kết up/down, lỗi sóng mang, “network unreachable”). Các bản ghi tường lửa UFW BLOCK không xuất hiện ở đây — chúng nằm ở trang “Tường lửa UFW” và “Bản đồ tấn công”. Khối trống với dấu tích xanh = suốt ngày qua không có sự cố mạng nào.

28. Đĩa và SMART

Trang tích hợp hiển thị ba nội dung:

  • Hệ thống tệp — mức đầy của phân vùng (df); thanh chuyển đỏ khi ≥90%;
  • Ổ lưu trữ — danh sách đĩa (lsblk), chỉ đĩa thật (loop/snap được ẩn);
  • Tình trạng (SMART) — trạng thái và thuộc tính của đĩa (smartctl).

Dung lượng và danh sách thiết bị hoạt động ngay, không cần thiết lập. SMART cần gói smartmontools. Tiến trình web không truy cập trực tiếp được vào thiết bị đĩa, nên SMART được thu thập bằng cron vào tệp data/disk/smart.txt để dashboard đọc.

Tác vụ nằm trong cron của root (sudo crontab -e). Trình bao smart-scan.sh có sẵn được đặt vào /usr/local/bin/ (chmod +x; xem tổng hợp tác vụ cron) và tự ghi vào thư mục data/disk/ của dashboard.

sudo apt install smartmontools # Dòng cron (mỗi 30 phút): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Trình bao smart-scan.sh tự tìm thư mục data/ của dashboard — không cần khai báo đường dẫn. Bên trong, lsblk -e7,11 loại trừ loop/cdrom.

Trên đĩa ảo (QEMU/KVM và tương tự) thường chỉ có trạng thái chung “tình trạng: OK”, còn nhiệt độ, giờ hoạt động và số sector đã phân bổ lại có thể trống — điều này là bình thường. Trên máy chủ vật lý sẽ hiển thị đầy đủ mọi thuộc tính.

29. Hiệu năng (CPU/RAM/Mạng/Đĩa)

Trang này hiển thị lịch sử tải của máy chủ trong 24 giờ gần nhất — Load Average, mức chiếm dụng CPU và thời gian chờ I/O, RAM/Swap, lưu lượng mạng (nhận/gửi), I/O đĩa (đọc/ghi), mức lấp đầy đĩa và inode, số file descriptor đang mở và kết nối MySQL, cùng số kết nối TCP và tiến trình hiện tại.

Dữ liệu do cron/collect_metrics.php thu thập — cứ mỗi 5 phút ghi một ảnh chụp “thô” của các bộ đếm (/proc/loadavg, /proc/meminfo, /proc/stat, /proc/net/dev, /proc/diskstats, df/df -i, /proc/sys/fs/file-nr, SHOW GLOBAL STATUS LIKE 'Threads_connected') vào bảng CSDL system_metrics; phần trăm và tốc độ được trang tự tính từ chênh lệch giữa hai ảnh chụp liền kề (mức lấp đầy đĩa/inode/descriptor/kết nối MySQL là giá trị tức thời, không tính lại). Không cần sudo — các nguồn được đọc mà không cần quyền root. Các điểm cũ hơn 24 giờ sẽ tự động bị xóa mỗi lần ghi.

# Dòng cron (mỗi 5 phút): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Trình bao bọc collect-metrics-all.sh (xem tóm tắt các tác vụ cron) tự tìm mọi phiên bản dashboard đã cài trên máy chủ và chạy cron/collect_metrics.php của từng cái dưới danh nghĩa chủ sở hữu trang web.

Cho đến khi trình thu thập chạy được ít nhất hai lần (khoảng 10 phút đầu sau khi cài đặt), trang sẽ hiển thị “đang thu thập dữ liệu” — biểu đồ cần tối thiểu một cặp điểm liền kề để tính tốc độ và phần trăm.

Cảnh báo về tải (mục “Cài đặt” → “Cảnh báo về tải”) — khi vượt ngưỡng CPU/RAM/đĩa/inode, dashboard sẽ gửi thông báo qua Telegram/Email (cùng các kênh như báo cáo hằng ngày — không cần bật riêng cho cảnh báo), và một thông báo nữa khi chỉ số trở lại bình thường. Không spam lặp lại trong lúc còn giữ ở ngưỡng: thông báo tiếp theo chỉ đến sau chu kỳ “trở lại bình thường → lại vượt ngưỡng”.

Các ngưỡng được kiểm tra bởi chính collect_metrics.php mỗi lần chạy (mỗi 5 phút) — không cần cron riêng. Trạng thái “đã thông báo / chưa” được lưu trong data/alerts_state.json, còn các ngưỡng nằm trong cài đặt của dashboard.

30. Bản đồ tấn công (GeoIP)

Trang “Bản đồ tấn công” xác định quốc gia theo IP bằng lệnh geoiplookup. Nếu thiếu gói GeoIP thì quốc gia sẽ không được xác định và các điểm sẽ không hiện trên bản đồ:

sudo apt install geoip-bin geoip-database # Kiểm tra: geoiplookup 8.8.8.8
Không cần sudo — cơ sở dữ liệu /usr/share/GeoIP/GeoIP.dat ai cũng đọc được, kết quả được lưu cache trong tmp/geoip_cache.json. Bản đồ (Leaflet + ô bản đồ OpenStreetMap) được tải trong trình duyệt — cần có internet trên máy tính đang mở dashboard.

31. Phơi lộ ra ngoài, cập nhật và tự động cập nhật

Hai thẻ tích hợp sẵn trên dashboard cho thấy không phải công cụ đang “bật/tắt”, mà mức độ an toàn thực sự của máy chủ. Không cần cài đặt, đọc cục bộ mà không cần sudo.

Phơi lộ ra ngoài — số dịch vụ lắng nghe trên mọi giao diện (0.0.0.0/[::]) và truy cập được từ bên ngoài. Tô đỏ nếu CSDL hoặc cache lộ ra ngoài (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — đây là lỗ hổng trực tiếp (−10 điểm An toàn). Nguồn: ss -tuln.

Nếu thẻ đỏ — hãy đóng CSDL với thế giới bên ngoài: gắn vào 127.0.0.1 (bind-address trong cấu hình MySQL/PostgreSQL, bind 127.0.0.1 trong Redis) hoặc đóng cổng trong UFW.
“Cổng mở” ≠ “truy cập được từ bên ngoài”. Dịch vụ lắng nghe trên 127.0.0.1 (loopback) chỉ chính máy chủ nhìn thấy — từ bên ngoài không thể truy cập, ngay cả khi cổng đã “mở”. Vì vậy Postfix trên cổng 25 gắn với loopback là an toàn: tự động cấu hình đặt inet_interfaces = loopback-only (cộng thêm smtpd_banner trung tính — khắc phục cảnh báo Lynis MAIL-8818 về việc lộ phiên bản). Thẻ “Phơi lộ ra ngoài” chỉ tính là lộ ra ngoài những gì lắng nghe trên 0.0.0.0/[::]; các dịch vụ loopback không được tính.
Lynis MAIL-8818 thủ công (nếu bạn tự cài mail): trong /etc/postfix/main.cf hãy đặt smtpd_banner = $myhostname ESMTP (không có phiên bản và HĐH) và inet_interfaces = loopback-only, rồi sudo systemctl restart postfix.

Cập nhật bảo mật — bao nhiêu bản vá bảo mật đang chờ cài đặt và có cần khởi động lại sau khi cập nhật nhân hay không (−5 điểm An toàn khi có bản vá). Nguồn: /usr/lib/update-notifier/apt-check, tệp /var/run/reboot-required. Danh sách chi tiết — ở trang “Cập nhật bảo mật”.

# Cài đặt bản cập nhật: sudo apt update && sudo apt upgrade # Kiểm tra dịch vụ nào lắng nghe ra ngoài: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Thẻ cập nhật hoạt động trên Ubuntu/Debian (update-notifier-common). Nếu không có apt-check — monitor tính số bản vá qua apt-get -s upgrade.

Tự động cập nhật bảo mật (unattended-upgrades) — ở trang “Cập nhật bảo mật” có một thẻ riêng cho thấy việc tự động cài bản vá bảo mật có được bật hay không và lần cuối nó chạy khi nào. Không cần sudo — trạng thái được đọc qua apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # bật # Kiểm tra đã bật chưa: apt-config dump | grep Unattended-Upgrade

Bảo trì

32. Sao lưu dữ liệu

Sao lưu là biện pháp bảo hiểm quan trọng nhất: mất dữ liệu còn đáng sợ hơn bất kỳ vụ xâm nhập nào. Bạn cần hai thứ — bản sao lưu máy chủ/website và riêng một bản sao lưu cơ sở dữ liệu của dashboard (nơi lưu người dùng, khóa WebAuthn, thiết lập, giấy phép).

Cách A — HestiaCP: tab Backup ở người dùng → nút tạo bản sao lưu (hoặc theo lịch trong thiết lập máy chủ). Bản sao lưu bao gồm website và cơ sở dữ liệu của chúng.

Cách B — thủ công (cron): dump cơ sở dữ liệu + nén thư mục data/ của dashboard:

# cron của root (sudo crontab -e) — sao lưu hằng ngày lúc 2:30 (thay tên/đường dẫn của bạn): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # Xóa các bản lưu cũ hơn 14 ngày: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Sao lưu trên cùng máy chủ giúp phòng lỗi, nhưng không cứu được khi mất máy chủ. Hãy sao chép các bản lưu ra lưu trữ bên ngoài (máy chủ khác, S3, rclone lên đám mây). Kiểm tra rằng việc khôi phục thực sự hoạt động.

33. Cập nhật và di chuyển panel

Cập nhật lên phiên bản mới. Trước tiên hãy sao lưu. Sau đó tải lại các tệp mã, giữ nguyên dữ liệu của bạn:

  • ghi đè (mã): public/, includes/, assets/, cron/, database/, cùng với các tệp gốc .htaccess (front-controller — không được để định tuyến từ phiên bản cũ), manifest.json, sw.js;
  • không đụng tới: config.php (dữ liệu CSDL), data/ (báo cáo), logs/, tmp/ (phiên và cache).
# Sau khi tải lên — xóa cache PHP (nếu bật opcache): sudo systemctl reload php*-fpm
FileZilla báo SSH_FX_PERMISSION_DENIEDPermission denied. Tệp của bảng điều khiển thuộc về www-data (chúng được đặt như vậy khi cài đặt), trong khi ứng dụng SFTP kết nối bằng người dùng của bạn, vốn không có quyền ghi. Giao toàn bộ bảng điều khiển cho www-data “cho nó chạy” chính là điều dẫn đến lỗi này; dưới đây là ba cách, cách nào cũng giải quyết được vấn đề.
# Phương án A (khuyến nghị) — tách chủ sở hữu: mã là của bạn, thư mục làm việc thuộc máy chủ web. # Máy chủ web hoàn toàn không nhận quyền ghi vào MÃ của bảng điều khiển: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # Phương án B — ACL phủ lên các chủ sở hữu hiện tại (không thay đổi gì): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # Phương án C — qua nhóm www-data. Đơn giản hơn, nhưng quyền ghi vào tệp của # bảng điều khiển cũng thuộc về máy chủ web (lỗ hổng PHP có thể thay mã): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
Vì sao phương án A an toàn. Bảng điều khiển chỉ ghi vào ba thư mục — data/ (báo cáo), tmp/ (phiên và cache), logs/; chúng vẫn thuộc về www-data. Phần còn lại là mã, và máy chủ web chỉ cần đọc chúng, điều mà nhóm www-data với quyền 644 đã cho phép. Lợi ích phụ: khi có lỗ hổng trong PHP, các tệp của bảng điều khiển không thể bị ghi đè. Trên các bảng điều khiển hosting (HestiaCP và tương tự) không cần phương án A: ở đó tệp website vốn đã thuộc tài khoản mà bạn dùng để đăng nhập qua SFTP, còn máy chủ web đọc chúng theo nhóm.
Cạm bẫy của phương án B: mọi lệnh chmod tiếp theo lên các tệp sẽ đặt lại mặt nạ ACL, và quyền truy cập lặng lẽ biến mất. Nếu sau khi “dọn dẹp lại quyền” việc tải lên lại vấp phải Permission denied — hãy chạy lại cả hai lệnh setfacl.
Bit 2 trong phương án C chính là setgid: tệp tải lên qua SFTP vẫn nằm trong nhóm www-data, nếu không bảng điều khiển sẽ không ghi đè được. Sau phương án C, hãy kết nối lại trong FileZilla — nhóm mới chỉ có hiệu lực khi đăng nhập mới. Kiểm tra: id deploy (phải xuất hiện nhóm www-data) và ls -ld /path/to/monitor (drwxrwsr-x — chữ s nghĩa là setgid đã được đặt).

Di chuyển sang máy chủ khác:

  1. Trên máy chủ mới, dựng website + HTTPS (xem trang cài đặt thủ công).
  2. Sao chép tất cả tệp của panel cùng với config.php, data/.
  3. Di chuyển CSDL: mysqldump trên máy cũ → nhập vào máy mới; sửa dữ liệu CSDL trong config.php.
  4. Lặp lại trên máy chủ mới: sudoers, thành viên nhóm adm, tác vụ cron.
  5. Giấy phép gắn với tên miền — nếu tên miền không đổi, khóa sẽ tiếp tục hoạt động.

34. Khôi phục quyền truy cập (mất khóa, mật khẩu, chặn IP)

Nếu bạn không thể đăng nhập — mọi thứ đều sửa được trực tiếp trong CSDL từ máy chủ. Mở CSDL (tên lấy từ config.php):

sudo mysql MY_DB

Mất khóa WebAuthn (không qua được yếu tố thứ hai) — tắt 2FA, đăng nhập bằng mật khẩu, đăng ký khóa mới:

UPDATE users SET webauthn_enabled = 0;

Quên mật khẩu — đặt hash mới (tạo hash trên máy chủ rồi thay vào):

# Tạo hash cho mật khẩu mới: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # Trong CSDL (dán hash vừa tạo vào): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Tự chặn mình bằng bộ lọc IP — tắt giới hạn:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Bạn luôn có quyền truy cập CSDL: sudo mysql trên máy chủ, hoặc phpMyAdmin / mục CSDL trong bảng điều khiển hosting. Sau khi khôi phục, hãy bật lại WebAuthn và bộ lọc IP.

35. Tất cả tác vụ cron ở một nơi

Tổng hợp các tác vụ nằm trong root-cron của máy chủ (thêm qua sudo crontab -e). Chỉ giữ lại các dòng của những công cụ bạn dùng; chỉnh đường dẫn cho khớp máy chủ của bạn.

# Cron máy chủ của monitor (root) — nhập qua: sudo crontab -e # 01:30 — quét ClamAV theo các đường dẫn nguy hiểm (web, home, temp) → thẻ “Tệp đã kiểm tra” và “Lần quét gần nhất” 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — kiểm tra tính toàn vẹn tệp bằng AIDE (cần --config rõ ràng) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # khi khởi động — khôi phục quyền /var/lib/aide (tệp tmpfiles của gói # aide-common.conf đặt lại về 0700, khiến dashboard không còn thấy cơ sở dữ liệu) @reboot chmod 755 /var/lib/aide # 03:00 — kiểm toán an ninh Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — cập nhật danh sách chặn ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # tập ipsum khi khởi động được nạp bởi dịch vụ ipsum-load.service (TRƯỚC firewall, nếu không # UFW sẽ không thấy tập trong before.rules) — không phải cron. Ở đây chỉ có việc làm mới hằng ngày bên trên. # 06:00 — báo cáo Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # mỗi 30 phút — kiểm tra ổ đĩa SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — tính toàn vẹn gói debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — báo cáo theo lịch qua Email và Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # mỗi giờ — làm mới danh sách gói (cho thẻ “Cập nhật bảo mật”) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # mỗi 5 phút — chụp trạng thái tài nguyên (CPU/RAM/mạng/đĩa) cho trang “Hiệu năng” */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Chi tiết từng mục xem trong các phần tương ứng. Các tác vụ sao lưu (phần trước) được thêm vào cùng cron này. Sau khi chỉnh, hãy kiểm tra: sudo crontab -l và xem dịch vụ cron còn hoạt động không.
Giờ cron = múi giờ máy chủ, không phải TIMEZONE trong config.php. Hằng số TIMEZONE chỉ ảnh hưởng đến PHP (cách dashboard hiển thị ngày), nhưng cron-daemon chạy tác vụ theo giờ hệ thống của OS. Nếu múi giờ máy chủ không khớp với bạn, báo cáo “08:00” sẽ đến sai giờ. Ví dụ: máy chủ đặt ở múi giờ khác (Asia/Dhaka, UTC+6), còn bạn ở TP. Hồ Chí Minh (UTC+7) → báo cáo “08:00” sẽ đến lúc 09:00 giờ của bạn. Hãy kiểm tra và nếu cần, chỉnh múi giờ hệ thống về giờ của bạn:
# Kiểm tra múi giờ hiện tại của máy chủ: timedatectl # Đặt múi giờ của bạn (ví dụ) và khởi động lại cron: sudo timedatectl set-timezone Asia/Ho_Chi_Minh sudo systemctl restart cron
Sau đó dòng 0 8 * * * sẽ chạy lúc 08:00 giờ địa phương. Nếu không, bạn sẽ phải tự dịch chuyển cron, nhưng khi chuyển giờ mùa đông/mùa hè độ lệch lại sai — nên chỉnh múi giờ hệ thống là đúng hơn.
Các script bao bọc sẵn có. Bản làm việc của chúng và mẫu crontab (crontab.txt) nằm trong thư mục system/ cạnh dự án, bên ngoài public_html. Đây không phải phần của trang web — không cần tải chúng lên web-root; hãy đặt trên máy chủ theo các đường dẫn hệ thống (như trong crontab ở trên):
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — chạy lynis audit system, trong lúc quét đặt cờ /tmp/lynis-running và sao chép lynis-report.dat vào data/lynis/ của dashboard;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — tạo báo cáo Logwatch hằng ngày (sshd, fail2ban, sudo, postfix) vào data/logwatch/;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — lấy trạng thái ổ đĩa (smartctl) vào data/disk/;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — kiểm tra tính toàn vẹn gói (debsums) vào data/debsums/;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — quét diệt virus ClamAV theo các đường dẫn nguy hiểm (web, home, temp); ghi tổng hợp vào /var/log/clamav/scan.log, nơi trang ClamAV đọc dữ liệu (dòng 01:30 trong crontab ở trên);
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — cập nhật tập ipset ipsum (level 1) tại chỗ, không phá vỡ các quy tắc tường lửa đang hoạt động (dòng 04:00 trong crontab ở trên);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — chạy báo cáo cron/daily_report.php của dashboard (dòng 08:00 trong crontab ở trên);
  • daily_report.php — đã có sẵn trong dashboard (cron/daily_report.php), chạy qua daily-report-all.sh, không cần cài riêng;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — chạy cron/collect_metrics.php của dashboard (trang “Hiệu năng”, dòng */5 trong crontab ở trên); collect_metrics.php đã có sẵn trong dashboard, không cần cài riêng;
  • crontab.txt (system/cron/) — mẫu tác vụ; nhập các dòng cần thiết qua sudo crontab -e.
Đường dẫn tới script trong crontab phải khớp với nơi bạn đã đặt nó.
Cách đặt script vào /usr/local/bin/. Không thể ghi trực tiếp từ FileZilla vào đó — thư mục thuộc về root, và SFTP-client sẽ nhận SSH_FX_PERMISSION_DENIED. Trình tự như sau: trước tiên tải tệp lên /tmp (ai cũng ghi được vào đó), rồi chuyển nó vào đúng chỗ bằng một lệnh:
# trong FileZilla: ở ô “Máy chủ ở xa” nhập /tmp rồi tải script lên đó, # rồi qua SSH (install đặt luôn chủ sở hữu và quyền, không cần chown/chmod): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # kiểm tra: tệp đã ở đúng chỗ, quyền rwxr-xr-x, cú pháp còn nguyên bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Đừng nhầm thư mục: cần /tmp ở gốc máy chủ — không phải /var/tmp và cũng không phải tmp/ bên trong chính dashboard (thư mục sau thuộc về www-data và không cho người dùng của bạn truy cập). Trong cây thư mục của FileZilla, /tmp là nhánh cấp cao nhất, cạnh var, không nằm bên trong nó.
Đã cài máy chủ bằng cấu hình tự động? Các script bao bọc này và các tác vụ cron của chúng đã được cài sẵn bởi script (trong /usr/local/bin/, log — /var/log/arciveo-cron.log) — không cần làm gì thủ công.
Nơi các script tìm dashboard. Các script bao bọc là trung lập về tên miền: chúng tìm các bản cài dashboard bằng cách duyệt /home/*/web/*/public_html/var/www/*, rồi đặt báo cáo vào data/ của chúng. Nếu dashboard nằm ở đường dẫn khác — hãy thêm nó vào dòng for app in … bên trong các script, nếu không báo cáo Lynis/SMART/debsums/Logwatch sẽ không vào được dashboard.
cron.log và quyền truy cập. Tệp logs/cron.log được tạo đầu tiên bởi root-cron — nó sẽ thuộc về root, và tab “Nhật ký cron” trong dashboard sẽ không đọc cũng như xóa được nó. Hãy tạo tệp trước bằng danh nghĩa web-user (chủ sở hữu thư mục trang web; trên HestiaCP đây là tài khoản, vd. admin) — khi đó root-cron chỉ ghi thêm mà không đổi chủ sở hữu:
# tạo trước bằng web-user (trước khi thêm các dòng cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # nếu cron.log đã do root-cron tạo — giao lại cho web-user: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Xem chủ sở hữu thư mục: stat -c %U /path/to/monitor.
Quản lý từ dashboard. Trong phần “Hệ thống” có trang “Crontab” — có thể xem và thêm tác vụ mà không cần SSH. Dashboard chỉ chỉnh những tác vụ được thêm qua chính nó (một khối riêng trong root-crontab, đánh dấu bằng các chú thích nội bộ); tất cả những gì đã có trong crontab (danh sách ở trên) được hiển thị ở đó dưới dạng danh sách chỉ đọc “Tác vụ khác của máy chủ” với nút “Sao chép vào trình soạn thảo” — nút này chỉ chuyển lịch/lệnh vào biểu mẫu thêm, không đụng đến dòng gốc. Để “chuyển” một tác vụ đã có sang cho dashboard quản lý — hãy sao chép nó vào trình soạn thảo, lưu lại, rồi xóa dòng cũ thủ công (sudo crontab -e), nếu không nó sẽ chạy hai lần.
Thiết lập một lần trên máy chủ. Trang cần một script bao bọc có đặc quyền — không phải sudo crontab trần (như vậy sẽ là leo thang trực tiếp lên root cho bất kỳ ai chiếm được phiên dashboard), mà là một script hẹp với hai lệnh (list/set), chỉ đụng đến khối riêng của nó nằm giữa các chú thích nội bộ. Cài một lần:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
Web-user có thể khác www-data — hãy kiểm tra pool PHP-FPM của trang web chạy dưới người dùng nào (ps -o user= -C php-fpm), rồi thay vào dòng sudoers.
Tệp mới được tải lên với chủ sở hữu sai — trang trả về “Access denied.”. Nếu tệp public/crontab_monitor.php được tải lên qua FTP/SFTP dưới người dùng hệ thống khác (ví dụ root) so với các tệp còn lại của trang web, web-server sẽ không đọc được nó. Hãy đối chiếu chủ sở hữu và quyền với tệp lân cận rồi chỉnh cho khớp:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

Chẩn đoán

36. Công cụ đã cài nhưng vẫn báo “Chưa cài”

Monitor xác định sự hiện diện của công cụ thông qua dpkg-query — cơ sở dữ liệu gói APT. Nếu công cụ không được cài qua apt (thủ công, từ snap hoặc từ mã nguồn), dpkg sẽ không thấy nó.

# Kiểm tra qua dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Tìm đường dẫn tới tệp thực thi: which ufw fail2ban-client auditctl # Kiểm tra sudo từ www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Khắc phục sự cố (500, không có dữ liệu)

Lỗi 500 — hãy kiểm tra log của PHP, nginx và của chính monitor:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Log của monitor: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Quyền trên thư mục: ls -la data/ tmp/ logs/
Dashboard chạy trên hosting panel (HestiaCP, ISPmanager, cPanel)? Ở đó PHP không chạy dưới www-data mà dưới tài khoản người dùng (ví dụ admin — chủ sở hữu thư mục website). Mọi quy tắc sudo và thành viên nhóm (adm, systemd-journal) đều phải khai báo cho người dùng này, nếu không các mô-đun sẽ hiện “Không hoạt động / 0” dù dịch vụ đang chạy. Xác định người dùng PHP thực tế: ps -o user= -C php-fpm | sort -u hoặc chủ sở hữu thư mục website stat -c '%U' /path/to/monitor. Sau đó trong tất cả lệnh bên dưới hãy thay nó vào chỗ www-data. Bản cài đặt tự động tự nhận diện người dùng web và khai báo sudoers cho nó.

Dữ liệu không hiển thị — hầu như luôn do chưa cấp quyền sudo. Hãy kiểm tra từng lệnh cụ thể với tư cách người dùng web (thay www-data bằng người dùng của bạn). Cờ -n = không cần mật khẩu, giống như PHP — nếu nó hỏi mật khẩu nghĩa là chưa có quy tắc trong sudoers:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
Mô-đun hiện “Không hoạt động” / “0” dù công cụ vẫn chạy (ví dụ sudo aa-status trong terminal có hiện profile, nhưng trang “AppArmor” lại “Không hoạt động”). Nguyên nhân: người dùng web không có quyền sudo cho lệnh của chính mô-đun đó. Hãy thử lệnh đó từ danh sách trên: nếu bị hỏi mật khẩu — thêm dòng còn thiếu vào /etc/sudoers.d/monitor (“Cấu hình sudo”). Các lệnh “mới” hay gặp: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Nếu một trang cụ thể (Falco, ModSecurity, Auditd, cổng đang mở của UFW) trống rỗng — hãy đối chiếu với danh sách trong phần về sudo: có thể chưa cho phép apache2ctl, ausearch, aa-status hoặc ss, hoặc người dùng web không thuộc nhóm adm/systemd-journal (từ đó đọc log fail2ban/auth/modsec và journalctl — Falco cùng các sự kiện kernel).

38. Trang trống mặc dù dữ liệu đã có trên máy chủ

Triệu chứng: trên máy chủ có dữ liệu (thấy được qua shell), nhưng trang lại hiển thị “không có dữ liệu” hoặc trạng thái sai — ví dụ AIDE báo “Chưa khởi tạo” dù cơ sở dữ liệu đã được tạo.

Nguyên nhân là open_basedir: nhiều bảng điều khiển và hosting giới hạn pool PHP-FPM trong thư mục của domain, nên các hàm PHP file_exists(), file_get_contents(), filemtime() trên các đường dẫn hệ thống (/var/lib/aide, /var/log, /proc…) bị chặn. Monitor khắc phục bằng cách đọc các đường dẫn đó qua lệnh hệ thống chuẩn (cat, test, stat).

# Tệp có thấy được qua shell không (cách monitor đọc): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Giá trị open_basedir hiện tại của pool domain: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Nếu shell “thấy” tệp (VISIBLE) mà trang thì không — đó là open_basedir. Giải pháp đúng là đọc bằng lệnh hệ thống (đã áp dụng cho AIDE và Giám sát mạng). Không cần mở rộng open_basedir ra /var, /proc và như vậy kém an toàn hơn.

39. Trang SSL không hoạt động

Monitor kiểm tra chứng chỉ bằng cách kết nối trực tiếp đến các domain qua cổng 443. Nếu domain không truy cập được từ chính máy chủ hoặc cổng bị firewall chặn — việc kiểm tra sẽ thất bại.

# Kiểm tra chứng chỉ thủ công: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Kiểm tra khả năng truy cập: curl -I https://monitor.example.com
Monitor tự động lấy domain từ các cấu hình của Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) và Apache (/etc/apache2/sites-enabled/) cùng với host hiện tại từ HTTP_HOST.
Tự động phát hiện subdomain. Các subdomain được phát hiện tự động từ nhật ký công khai Certificate Transparency và kiểm tra qua mạng — kể cả khi chúng đặt trên máy chủ khác. Không cần thêm gì thủ công.

40. Chỉ thấy được một trong số nhiều CSDL

Monitor kết nối tới MySQL bằng người dùng khai báo trong config.php, người này chỉ có quyền truy cập cơ sở dữ liệu của mình. MySQL chỉ hiển thị trong information_schema các cơ sở dữ liệu mà người dùng có quyền — nên những cơ sở còn lại không hiện ra.

Để monitor thấy được tất cả CSDL, hãy cấp cho người dùng này quyền chỉ đọc (một lần bằng root; thay bằng tên người dùng trong config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* chỉ cấp quyền đọc — không thể sửa, xóa hay tạo bất kỳ thứ gì, hoàn toàn an toàn cho việc giám sát.
Nếu không có GRANT này, dashboard chỉ thấy cơ sở dữ liệu của mình — đây không phải lỗi mà là giới hạn quyền. Dashboard không dùng sudo mysql: danh sách cơ sở dữ liệu được lấy qua kết nối PDO riêng của nó.

41. PostgreSQL không hiển thị trên trang “Cơ sở dữ liệu”

PostgreSQL yêu cầu quyền truy cập ở cấp người dùng postgres, mà người dùng web của dashboard không có. Việc mở lệnh sudo psql rộng từ PHP là không an toàn — thay vào đó dashboard gọi một lớp bọc hẹp không tham số, chỉ in ra phiên bản, số kết nối và danh sách cơ sở dữ liệu kèm dung lượng. Hãy tạo nó:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # trong /etc/sudoers.d/monitor (người dùng = người mà PHP-FPM chạy dưới quyền): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Nếu không dùng PostgreSQL — hãy bỏ dòng monitor-pgstat khỏi sudoers (bước 13 của phần cài đặt thủ công) và không cần tạo script: thẻ PostgreSQL sẽ chỉ đơn giản là không hoạt động.

42. Cảnh báo được kích hoạt — cần làm gì

Bảng điều khiển cho thấy điều gì đang xảy ra; dưới đây là cần làm gì trong các tình huống điển hình. Nguyên tắc chung: đừng hoảng loạn, đối chiếu với hoạt động hợp lệ (thao tác của bạn, cập nhật, sao lưu), và xử lý theo mức độ nghiêm trọng.

  • Bản đồ tấn công / nhiều lượt chặn của fail2ban — đây là chuyện bình thường với mọi máy chủ trên internet (bot liên tục dò SSH/web). Điều quan trọng là các lượt chặn có hoạt động. Hãy đảm bảo đăng nhập SSH chỉ bằng khóa (đã tắt mật khẩu), và IP của bạn nằm trong ignoreip.
  • ModSecurity đã chặn các yêu cầu — WAF đang đẩy lùi các cuộc tấn công vào trang web, đó là nhiệm vụ của nó. Nếu lưu lượng hợp lệ của bạn bị chặn (báo động giả) — hãy tìm rule id trong chi tiết và thêm ngoại lệ vào cấu hình CRS.
  • AIDE: các tệp đã bị thay đổi — đối chiếu danh sách với những gì bạn đã làm (cập nhật gói, sửa cấu hình — là bình thường). Việc thay đổi các tệp nhị phân hệ thống mà bạn không đụng tới là lý do để cảnh giác. Sau các thay đổi hợp lệ, hãy cập nhật cơ sở dữ liệu AIDE.
  • debsums: tệp nhị phân/thư viện bị thay đổi (ngoài /etc, ngoài /usr/share) — có khả năng bị đánh tráo. Hãy đối chiếu gói: debsums PACKAGE_NAME, nếu nghi ngờ hãy cài đặt lại nó (apt install --reinstall).
  • ClamAV / maldet: phát hiện mối đe dọa — hãy kiểm tra tệp trong khu cách ly, đừng mở nó. Nếu đó là web-shell trong thư mục trang web — hãy cô lập máy chủ và tìm điểm xâm nhập (plugin có lỗ hổng, rò rỉ quyền truy cập).
  • Falco: sự kiện nghiêm trọng (khởi chạy shell trong container, truy cập các tệp nhạy cảm) — hãy phân tích sự kiện: tiến trình của ai, thứ gì đã khởi chạy. Thường đó là hoạt động quản trị hợp lệ.
  • Phơi nhiễm bên ngoài: CSDL/cache hiện màu đỏ — hãy đóng ngay: gắn dịch vụ vào 127.0.0.1 hoặc đóng cổng trong UFW. Đây là một lỗ hổng thực sự.
  • SSL sắp hết hạn / đã hết hạn — hãy gia hạn chứng chỉ (Let's Encrypt tự gia hạn; nếu không — hãy kiểm tra certbot renew hoặc thiết lập trong bảng điều khiển).
  • Có bản cập nhật bảo mật đang chờ — hãy cài đặt: sudo apt update && sudo apt upgrade; sau khi cập nhật nhân hãy khởi động lại máy chủ.
Dấu hiệu bị xâm nhập thực sự (tiến trình/người dùng lạ, tệp nhị phân bị thay đổi, spam gửi đi, tác vụ cron lạ): hãy ngắt máy chủ khỏi truy cập bên ngoài, tạo bản sao lưu để phân tích và, nếu dữ liệu là trọng yếu, hãy dựng máy chủ sạch từ một bản sao lưu đáng tin cậy — việc dọn sạch rootkit một cách chắc chắn là rất khó.
Arcivéo - Security Monitor © 2026