Cài đặt thủ công

Cài đặt hoàn toàn thủ công: từ một VPS vừa mua đến bảng điều khiển hoạt động, theo từng bước. Chuẩn bị máy chủ, tạo website, cơ sở dữ liệu, config.php và SSL đều được trình bày ở đây. Lệnh cho từng công cụ bảo mật và cho cron nằm trong tài liệu FAQ, có liên kết dọc theo bài.

Nguyên tắc quan trọng: khi thay đổi SSH hoặc tường lửa, đừng đóng kết nối hiện tại cho đến khi đã kiểm tra kết nối mới trong một cửa sổ riêng. Nếu vẫn mất quyền truy cập — hầu hết nhà cung cấp hosting đều có console khẩn cấp (VNC/Recovery) trong bảng điều khiển.

01. Vừa mua VPS chạy Ubuntu/Debian — bắt đầu từ đâu

Sau khi mua, nhà cung cấp hosting sẽ gửi: địa chỉ IP, tên người dùng (thường là root) và mật khẩu (hoặc khóa SSH). Chừng đó đủ để đăng nhập. Trình tự thao tác (mỗi bước là một mục bên dưới):

  1. Kết nối tới máy chủ qua SSH;
  2. Cập nhật hệ thống, đặt tên host và múi giờ;
  3. Tạo một người dùng thường có quyền sudo (không làm việc dưới quyền root);
  4. Thiết lập đăng nhập bằng khóa SSH và tắt đăng nhập bằng mật khẩu;
  5. Bật tường lửa và tự động bảo vệ;
  6. (tùy chọn) cài bảng điều khiển HestiaCP — máy chủ web, CSDL, email “sẵn dùng”.

02. Kết nối SSH lần đầu

SSH là terminal bảo mật đến máy chủ. Thay IP của bạn vào chỗ 203.0.113.10.

203.0.113.10 chỉ là ví dụ, một địa chỉ không tồn tại (dành riêng cho tài liệu). Đừng nhập y nguyên — hãy thay bằng IP thật của máy chủ bạn có trong email từ nhà cung cấp hosting. Nếu không sẽ không kết nối được.

Windows 10/11: mở PowerShell hoặc “Terminal” và dùng ssh có sẵn (hoặc trình khách PuTTY / MobaXterm).
macOS / Linux: mở “Terminal”.

# Đăng nhập bằng root (mật khẩu do nhà cung cấp hosting gửi): ssh root@203.0.113.10 # Nếu nhà cung cấp hosting cấp tệp khóa thay vì mật khẩu: ssh -i đường/dẫn/tới/khóa root@203.0.113.10
Ở lần kết nối đầu tiên, SSH sẽ hỏi về “authenticity of host” — hãy nhập yes. Mật khẩu không hiển thị khi nhập (điều này là bình thường). Nếu nhà cung cấp hosting cấp mật khẩu tạm thời — hãy đổi bằng lệnh passwd.

03. Cập nhật hệ thống và cấu hình cơ bản

Việc đầu tiên — cập nhật tất cả gói phần mềm, đặt tên máy chủ và múi giờ.

# Cập nhật hệ thống: apt update && apt upgrade -y # Tiện ích cơ bản: apt install -y curl wget ufw fail2ban unattended-upgrades # Múi giờ (ví dụ) và tên máy chủ: timedatectl set-timezone Asia/Ho_Chi_Minh hostnamectl set-hostname myserver # Cập nhật bảo mật tự động: dpkg-reconfigure -plow unattended-upgrades
Danh sách múi giờ — timedatectl list-timezones. Nếu ở cuối quá trình cập nhật xuất hiện cửa sổ màu xanh “Daemons using outdated libraries” — hãy chọn tất cả dịch vụ (phím Cách) rồi nhấn OK, điều này an toàn.

04. Tạo người dùng có quyền sudo

Làm việc thường xuyên dưới quyền root là không an toàn. Hãy tạo một người dùng thông thường và cấp cho họ quyền sudo (chạy lệnh với quyền quản trị khi cần). Thay deploy bằng tên bất kỳ.

# Tạo người dùng (sẽ đặt mật khẩu và hỏi thông tin — có thể nhấn Enter): adduser deploy # Thêm vào nhóm sudo: usermod -aG sudo deploy # Kiểm tra (dưới quyền root): su - deploy sudo whoami # phải in ra: root exit
Từ giờ hãy đăng nhập vào máy chủ bằng người dùng này: ssh deploy@203.0.113.10, và chạy các lệnh quản trị với tiền tố sudo.

05. Khóa SSH và tắt đăng nhập bằng mật khẩu

Đăng nhập bằng khóa an toàn hơn mật khẩu: mật khẩu có thể bị dò ra, còn khóa thì gần như không. Đầu tiên tạo khóa trên máy tính của bạn, sao chép nó lên máy chủ, kiểm tra đăng nhập — rồi mới tắt mật khẩu.

Bước 1. Tạo khóa trên máy tính của bạn (Windows PowerShell / macOS / Linux):

ssh-keygen -t ed25519 -C "my-laptop" # Nhấn Enter cho mọi câu hỏi (khóa sẽ nằm ở ~/.ssh/id_ed25519)

Bước 2. Sao chép khóa công khai lên máy chủ:

# macOS / Linux: ssh-copy-id deploy@203.0.113.10 # Windows (PowerShell): type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Bước 3. Kiểm tra đăng nhập bằng khóa ở cửa sổ mới — phải vào được mà không cần mật khẩu:

ssh deploy@203.0.113.10
Đừng thực hiện Phương án B khi đăng nhập bằng khóa chưa được kiểm tra và hoạt động (Bước 1–3), và đừng đóng phiên đang làm việc. Nó tắt đăng nhập bằng mật khẩu cho mọi người dùng, bao gồm cả root. Không có khóa hoạt động, bạn sẽ mất hoàn toàn quyền truy cập máy chủ — chỉ có thể khôi phục qua console của nhà cung cấp hosting. Không có khóa thì chọn Phương án A.

Bước 4. Siết chặt truy cập SSH. Các thiết lập đặt vào một tệp riêng, không đụng vào cấu hình chính. Hãy chọn phương án theo tình huống:

Phương án A — chỉ chặn root, giữ lại mật khẩu. Không cần khóa, không mất quyền truy cập:

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null sudo systemctl restart ssh

Phương án B — hardening toàn diện. Tắt đăng nhập bằng mật khẩu và chỉ để root đăng nhập bằng khóa. Chỉ thực hiện khi đã chắc chắn đăng nhập bằng khóa hoạt động:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF' PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password KbdInteractiveAuthentication no EOF sudo systemctl restart ssh
Ở cả hai phương án, root bằng mật khẩu đều bị chặn. PermitRootLogin no cấm root hoàn toàn, prohibit-password chỉ để lại đăng nhập bằng khóa (để quản trị, hãy đăng nhập bằng deploy và dùng sudo). Nếu muốn đổi cổng SSH — hãy thêm dòng Port 2222, nhưng trước tiên hãy mở cổng mới trong tường lửa (phần kế tiếp) và kiểm tra đăng nhập, nếu không bạn sẽ tự khóa quyền truy cập của mình.

06. Tường lửa cơ bản và tự động bảo vệ

Chặn mọi thứ không cần thiết bằng tường lửa và bật fail2ban (chặn dò mật khẩu qua SSH). Trước tiên hãy cho phép SSH, nếu không sau khi bật UFW bạn sẽ mất quyền truy cập.

# Cho phép SSH (hoặc cổng của bạn, nếu đã đổi) và web: sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Bật tường lửa: sudo ufw enable sudo ufw status verbose # fail2ban — bảo vệ SSH khỏi dò mật khẩu (hồ sơ cơ bản kích hoạt ngay): sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd
Đây là mức tối thiểu. Cấu hình fail2ban thực tế, danh sách chặn ipsum, UFW nâng cao và các công cụ khác — xem nhóm “Công cụ bảo mật” trong tài liệu. Bản thân bảng điều khiển Arcivéo Monitor sẽ hiển thị rõ trạng thái của tất cả những thứ này.

07. Cài đặt bảng điều khiển HestiaCP (tùy chọn)

HestiaCP — bảng điều khiển hosting miễn phí: cài và cấu hình máy chủ web (nginx + apache), PHP, cơ sở dữ liệu (MariaDB), email, DNS và chứng chỉ SSL, cung cấp giao diện web cho các trang web. Tiện lợi nếu bạn không muốn cấu hình mọi thứ thủ công và dự định đặt các trang web (bao gồm cả bảng Arcivéo Monitor).

Hãy cài HestiaCP lên máy chủ sạch (Ubuntu/Debian bản mới được hỗ trợ, tối thiểu ~1–2 GB RAM), trước khi cài các máy chủ web và CSDL khác — nếu không sẽ xảy ra xung đột. Quá trình cài đặt mất 10–20 phút và sẽ khởi động lại máy chủ.
# Tải trình cài đặt và chạy: wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh sudo bash hst-install.sh

Trình cài đặt sẽ hỏi email và tên máy chủ, sau đó cài toàn bộ stack. Sau khi khởi động lại, bảng điều khiển truy cập được tại địa chỉ https://YOUR_IP:8083 (trình cài đặt sẽ hiển thị tên đăng nhập và mật khẩu ở cuối).

HestiaCP tự quản lý UFW và fail2ban — không cần cấu hình riêng, nó sẽ tự nhận. Vẫn nên thiết lập khóa SSH và tắt đăng nhập bằng mật khẩu (phần trước).

08. Yêu cầu hệ thống và ionCube

Dashboard là ứng dụng PHP chạy trên ngăn xếp LAMP/LEMP điển hình:

  • Hệ điều hành: Linux (khuyến nghị Ubuntu/Debian);
  • Máy chủ web: nginx hoặc Apache với PHP-FPM;
  • PHP 8.0+ kèm các phần mở rộng: pdo_mysql, openssl, curl, json, mbstring;
  • ionCube Loader — phần mở rộng PHP cần thiết để dashboard hoạt động;
  • CSDL: MySQL 5.7+ hoặc MariaDB 10.3+;
  • HTTPS — bắt buộc (đăng nhập và WebAuthn chỉ hoạt động qua https);
  • sudo cho người dùng máy chủ web (tập quyền hẹp — bước 13).
# Kiểm tra phiên bản PHP và các phần mở rộng: php -v php -m | grep -iE 'pdo_mysql|openssl|curl|mbstring|ioncube'

Cài đặt ionCube Loader (nếu chưa có). Trên hosting có panel (HestiaCP, cPanel), ionCube được bật bằng dấu tích trong phần cài đặt PHP. Thủ công trên Ubuntu/Debian:

# Xác định phiên bản PHP và thư mục phần mở rộng: php -v EXTDIR=$(php -r 'echo ini_get("extension_dir");'); PHPVER=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') # Tải và giải nén các loader (64-bit): cd /tmp wget -q https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz tar xzf ioncube_loaders_lin_x86-64.tar.gz # Sao chép loader phù hợp với phiên bản PHP vào thư mục phần mở rộng: sudo cp ioncube/ioncube_loader_lin_${PHPVER}.so "$EXTDIR"/ # Kích hoạt (CLI + PHP-FPM) và khởi động lại: echo "zend_extension=ioncube_loader_lin_${PHPVER}.so" | sudo tee /etc/php/${PHPVER}/mods-available/ioncube.ini sudo phpenmod ioncube sudo systemctl restart php${PHPVER}-fpm # Kiểm tra — kết quả sẽ hiện dòng "with the ionCube PHP Loader": php -v
Phiên bản loader phải khớp với phiên bản PHP (ví dụ ioncube_loader_lin_8.1.so cho PHP 8.1). Nếu dùng nhiều phiên bản PHP — hãy kích hoạt loader cho từng phiên bản.

09. Tên miền và DNS

Để mở dashboard qua địa chỉ dạng monitor.example.com và nhận SSL miễn phí — bạn cần một tên miền trỏ về máy chủ của mình. Trong bảng quản lý DNS, hãy tạo một bản ghi A:

Loại: A Tên: monitor (tên miền phụ → monitor.example.com) hoặc @ (tên miền gốc → example.com) Giá trị: 203.0.113.10 ← IP máy chủ của bạn TTL: 3600

Sau vài phút, hãy kiểm tra rằng tên miền đã trỏ về máy chủ:

dig +short monitor.example.com # phải trả về IP của bạn # hoặc, nếu không có dig: getent hosts monitor.example.com
Chứng chỉ SSL Let's Encrypt chỉ được cấp cho tên miền — DNS phải trỏ về máy chủ trước khi phát hành chứng chỉ.

10. Tạo website và tải tệp panel lên

Apache: DocumentRoot trỏ vào thư mục gốc của panel, KHÔNG trỏ vào public/. Các tệp style (CSS/JS), sw.js, manifest.json nằm trong assets/ cạnh public/ và được yêu cầu từ gốc website. Tệp .htaccess ở gốc là front-controller. Nếu với Apache mà đặt DocumentRoot vào public/ thì panel sẽ mở ra mất style. Với nginx thuần thì ngược lại: lấy public/ làm gốc, còn assets/ được phục vụ bằng một quy tắc riêng (xem khối nginx bên dưới).
Các tệp panel (gói phân phối) được tải về sau khi mua trong tài khoản cá nhân my.arciveo.com“Tải xuống”. Hãy giải nén gói trước khi tải lên.

1) Tạo thư mục panel và tải nội dung gói phân phối vào đó (sao cho bên trong có public/, assets/, config.php v.v.):

sudo mkdir -p /var/www/monitor # sau đó tải tệp của gói phân phối lên /var/www/monitor (FileZilla / WinSCP / scp)

2) Cấu hình máy chủ web. Apache: DocumentRoot trỏ vào gốc của panel (KHÔNG trỏ vào /public); bắt buộc phải có AllowOverride All. Đường dẫn tới socket PHP-FPM được xác định tự động. Dán toàn bộ khối vào terminal:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # tự động xác định socket PHP-FPM sudo tee /etc/apache2/sites-available/monitor.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName monitor.example.com DocumentRoot /var/www/monitor <Directory /var/www/monitor> AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/monitor.conf sudo a2dissite 000-default.conf sudo a2ensite monitor.conf sudo apache2ctl configtest sudo systemctl reload apache2

nginx: nginx không có .htaccess, nên lấy public/ làm gốc, còn assets/, sw.js, manifest.json (ở cấp cao hơn) được phục vụ bằng một quy tắc riêng:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # tự động xác định socket PHP-FPM sudo tee /etc/nginx/sites-available/monitor.conf > /dev/null <<'EOF' server { listen 80; server_name monitor.example.com; root /var/www/monitor/public; index index.php; # assets, service worker và manifest nằm ở cấp cao hơn public/ location ~ ^/(assets/|sw\.js|manifest\.json) { root /var/www/monitor; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:__PHPSOCK__; } } EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/nginx/sites-available/monitor.conf sudo ln -s /etc/nginx/sites-available/monitor.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

Tải tệp lên — SFTP/SCP (FileZilla, WinSCP) hoặc scp:

# Ví dụ qua scp từ máy tính cá nhân: scp -r ./monitor/* deploy@203.0.113.10:/var/www/monitor/
Đặt quyền cho tệp — đây là bước bắt buộc. Nếu tải lên dưới root hoặc qua SFTP, tệp thuộc về root, và máy chủ web (www-data) sẽ không đọc được — panel sẽ mở ra trống hoặc báo lỗi 403 (trong log: .htaccess unreadable / directory not executable). Lệnh bên dưới khắc phục điều này:
# Chuẩn hóa quyền cho toàn bộ webroot: thư mục do root tạo thì máy chủ web # (www-data) không truy cập được — thiếu bước này panel trả về trang trống hoặc 403. # Apache chạy dưới www-data; nếu bạn dùng người dùng web khác thì hãy thay thế. cd /var/www/monitor # Tạo các thư mục làm việc TRƯỚC chown — nếu không các thư mục mới sẽ vẫn là root:root # và khi chmod 750 máy chủ web (www-data) sẽ không ghi được vào đó. sudo mkdir -p data/lynis data/logwatch tmp logs sudo chown -R www-data:www-data /var/www/monitor sudo find /var/www/monitor -type d -exec chmod 755 {} \; sudo find /var/www/monitor -type f -exec chmod 644 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 750 data tmp logs

3) Hãy mở quyền tải tệp lên qua SFTP cho chính bạn. Sau lệnh ở trên, mọi tệp đều thuộc về www-data, trong khi FileZilla / WinSCP kết nối bằng người dùng của bạn — khi đó việc tải lên sẽ thất bại với SSH_FX_PERMISSION_DENIED (Permission denied). Đăng nhập bằng root để tải lên là không được — đăng nhập root đã bị tắt ở bước 05. Hãy chọn một trong hai phương án.

Phương án A — ACL chỉ dành cho người dùng của bạn (khuyến nghị). Chỉ mình bạn có quyền ghi; máy chủ web vẫn không thể ghi đè mã của bảng điều khiển:

sudo apt install -y acl # Quyền ghi cho người dùng của bạn trên toàn bộ thư mục bảng điều khiển: sudo setfacl -R -m u:deploy:rwX /var/www/monitor # Cũng quy tắc đó làm mặc định — cho tệp và thư mục tạo về sau: sudo setfacl -R -d -m u:deploy:rwX /var/www/monitor

Phương án B — qua nhóm www-data. Đơn giản hơn, nhưng máy chủ web cũng có quyền ghi vào tệp của bảng điều khiển: nếu PHP có lỗ hổng thì mã có thể bị thay thế. Thứ tự các lệnh rất quan trọng — config.php và các thư mục làm việc được đóng lại sau cùng:

sudo usermod -aG www-data deploy # Quyền ghi cho nhóm + setgid (bit 2): 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 không ghi đè được. sudo find /var/www/monitor -type d -exec chmod 2775 {} \; sudo find /var/www/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 2750 /var/www/monitor/data /var/www/monitor/tmp /var/www/monitor/logs
Sau phương án B, hãy kết nối lại trong FileZilla (Máy chủ → Ngắt kết nối, rồi đăng nhập lại) — nhóm mới chỉ có hiệu lực khi đăng nhập mới, trước đó bạn vẫn chưa có quyền. Kiểm tra: id deploy — trong danh sách nhóm phải xuất hiện www-data; ls -ld /var/www/monitor — quyền drwxrwsr-x, chữ s thay cho x nghĩa là setgid đã được đặt.

11. Cơ sở dữ liệu

Tạo cơ sở dữ liệu và người dùng, sau đó nhập lược đồ. Dán toàn bộ khối vào terminal. monitor_dbmonitor_user là tên ví dụ, bạn có thể đặt tùy ý; hãy ghi nhớ tên cơ sở dữ liệu, người dùng và mật khẩu — bạn sẽ nhập chúng vào config.php ở bước tiếp theo:

# 1. Cơ sở dữ liệu. Mật khẩu chỉ đặt MỘT lần trong DBPASS và được chèn vào mọi dòng. # Dán TOÀN BỘ khối vào terminal; sudo mysql đăng nhập root qua unix-socket # (không cần mật khẩu root). ĐỪNG dùng `sudo mysql -u root -p` tương tác # với copy-paste — khi dán, các dòng SQL sẽ đi vào phần hỏi mật khẩu và mất. DBPASS='CHOOSE_A_PASSWORD' # ← chỉ sửa dòng này sudo mysql <<SQL CREATE DATABASE IF NOT EXISTS monitor_db CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'monitor_user'@'localhost' IDENTIFIED BY '$DBPASS'; GRANT ALL ON monitor_db.* TO 'monitor_user'@'localhost'; FLUSH PRIVILEGES; SQL # Kiểm tra (phải hiển thị monitor_db): mysql -u monitor_user -p"$DBPASS" -e "SHOW DATABASES;" # Nhập chính mật khẩu này vào config.php → DB_PASS.
Không cần nhập lược đồ — bảng điều khiển tự tạo các bảng và tài khoản admin ở lần truy cập trình duyệt đầu tiên (từ database/db.sql) nếu cơ sở dữ liệu trống. Chỉ cần nhập lược đồ thủ công khi tự động khởi tạo không hoạt động.
Nếu đã dùng trình cài đặt qua trình duyệt public/start_db.php — hãy xóa nó ngay sau khi cài đặt: nó cho phép tạo lại cơ sở dữ liệu mà không cần đăng nhập. Khi tệp còn nằm trong thư mục gốc của bảng điều khiển hoặc trong public/, bảng điều khiển sẽ hiển thị cảnh báo màu đỏ.

12. Cấu hình config.php

config.php ở thư mục gốc của bảng điều khiển (/var/www/monitor/config.php) là tệp duy nhất bạn cần chỉnh sửa thủ công. Mọi thiết lập của bảng điều khiển được khai báo trong đó bằng hằng số define(). Mở nó bằng trình soạn thảo:

sudo nano /var/www/monitor/config.php

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

// --- Cơ sở dữ liệu (từ bước 11) --- define('DB_HOST', 'localhost'); // giữ nguyên define('DB_NAME', 'db_name'); // đã tạo ở bước 11 define('DB_USER', 'user'); // đã tạo ở bước 11 define('DB_PASS', 'db_password'); // đã đặt ở bước 11 define('DB_CHARSET', 'utf8mb4'); // giữ nguyên // --- Ứng dụng --- define('APP_URL', 'https://monitor.example.com'); // địa chỉ bảng điều khiển, 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 không hoạt động trước khi phải đăng nhập lại, giây (28800 = 8 giờ)

Cần thay đổi những gì:

  • DB_NAME, DB_USER, DB_PASS — đúng tên cơ sở dữ liệu, người dùng và mật khẩu mà bạn đã đặt khi tạo CSDL ở bước 11 (nếu giữ ví dụ — monitor_db / monitor_user). Đừng động vào DB_HOSTDB_CHARSET.
  • APP_URL — địa chỉ đầy đủ của bảng điều khiển kèm https://, không có dấu gạch chéo ở cuối và không có www. Phải khớp với tên miền mà bạn kích hoạt giấy phép (bước 16), nếu không khóa sẽ bị từ chối.
  • TIMEZONE — múi giờ của bạn (danh sách — timedatectl list-timezones). Chỉ ảnh hưởng đến cách bảng điều khiển hiển thị ngày tháng; không ảnh hưởng đến thời điểm chạy tác vụ cron (ở đó dùng múi giờ của hệ thống).
  • SESSION_LIFETIME — sau bao nhiêu giây không hoạt động thì bảng điều khiển yêu cầu đăng nhập lại (mặc định 8 giờ). Ví dụ 3600 = 1 giờ, 86400 = một ngày.
  • Khối ghi log lỗi (display_errors, log_errors, error_log) — giữ nguyên mặc định.

Lưu tệp (Ctrl+O, Enter, rồi Ctrl+X) và khởi động lại PHP-FPM — nếu không, do OPcache các thay đổi sẽ không được áp dụng:

sudo systemctl restart php*-fpm
config.php là tệp bí mật (chứa mật khẩu CSDL). Nó nằm ở thư mục gốc của bảng điều khiển, cũng chính là thư mục gốc web, nhưng đã được bảo vệ: quyền 640 (đặt ở bước 10) và cấm rõ ràng trong tệp .htaccess gốc. Đừng đăng 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.
Phân tích chi tiết mọi tham số — trong FAQ: “Tệp config.php — mọi thiết lập của bảng điều khiển”.

13. Cấu hình sudo cho máy chủ web

PHP chạy dưới tài khoản của máy chủ web, vốn không có quyền chạy các lệnh hệ thống. Quyền được cấp hạn hẹp: sudo có chọn lọc cho từng tiện ích cụ thể và đọc log qua nhóm (không cần sudo). Khi lớp web bị xâm nhập vẫn không có được quyền root.

Trong các ví dụ, www-data là tài khoản Apache tiêu chuẩn. Nếu bạn dùng tài khoản khác (một số bảng điều khiển chạy PHP dưới tài khoản riêng) thì hãy thay ở mọi nơi. Kiểm tra: ps -o user= -C php-fpm | sort -u.

1. Tạo /etc/sudoers.d/monitor bằng sudo visudo -f /etc/sudoers.d/monitor rồi dán vào (xóa các dòng của những mô-đun không dùng):

# UFW — trạng thái và luật (trang “Tường lửa”) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw status, /usr/sbin/ufw status verbose, /usr/sbin/ufw status numbered www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw allow [0-9]*, /usr/sbin/ufw deny [0-9]*, /usr/sbin/ufw --force delete [0-9]* # Fail2ban — trạng thái, ban và gỡ ban (banned trả về ban của mọi jail bằng một lệnh; # ban/unban cần cho các nút trên dashboard) www-data ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *, /usr/bin/fail2ban-client banned, /usr/bin/fail2ban-client set * banip *, /usr/bin/fail2ban-client set * unbanip * # Cập nhật bảo mật (thẻ “Cập nhật”). Chỉ đọc, nhưng phải chạy từ root: # cache của apt (~70 MB) chỉ root truy cập được, không phải root sẽ dựng lại mỗi lần gọi # (4.2 giây CPU so với 0.01 giây). Không có wildcard — đúng một lệnh này, không cài gì cả. www-data ALL=(ALL) NOPASSWD: /usr/bin/apt list --upgradable # IPset (bản đồ tấn công, dashboard) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ipset list -t ipsum # CrowdSec www-data ALL=(ALL) NOPASSWD: /usr/bin/cscli decisions list *, /usr/bin/cscli alerts list *, /usr/bin/cscli bouncers list *, /usr/bin/cscli scenarios list * # Auditd — tìm sự kiện + đọc các dòng cuối của nhật ký (đường dẫn chính xác) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ausearch -m * www-data ALL=(ALL) NOPASSWD: /usr/bin/tail -n 300 /var/log/audit/audit.log # Monit / ModSecurity / AppArmor / PSAD www-data ALL=(ALL) NOPASSWD: /usr/bin/monit status www-data ALL=(ALL) NOPASSWD: /usr/sbin/apache2ctl -M www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec www-data ALL=(ALL) NOPASSWD: /usr/sbin/aa-status www-data ALL=(ALL) NOPASSWD: /usr/sbin/psad --Status # Cổng đang mở (log kernel/SSH/Falco đọc KHÔNG cần sudo — qua nhóm # systemd-journal, xem mục 2; KHÔNG nên và không an toàn khi cấp sudo cho journalctl) www-data ALL=(ALL) NOPASSWD: /usr/bin/ss -tuln, /usr/sbin/ss -tuln, /bin/ss -tuln # PostgreSQL (chỉ khi bạn dùng) — script chỉ đọc cố định, # tạo theo FAQ “PostgreSQL không hiển thị”; nếu không có thì xóa dòng này www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
sudo chmod 440 /etc/sudoers.d/monitor sudo visudo -c # phải là "parsed OK"

2. Truy cập log và nhật ký systemd. Các mô-đun đọc trực tiếp /var/log/fail2ban.log, auth.log, ufw.log, apache2/*, aide (trên Debian/Ubuntu các log này thuộc nhóm adm). Sự kiện kernel, SSH và Falco lấy từ journald bằng lệnh journalctl không cần sudo, qua nhóm systemd-journal. Thêm tài khoản web vào cả hai nhóm rồi khởi động lại PHP-FPM:

sudo usermod -aG adm,systemd-journal www-data sudo systemctl restart php*-fpm # bắt buộc, nếu không nhóm sẽ không áp dụng

3. Nếu ClamAV hoặc Suricata ghi log không thuộc nhóm adm (có khi là root:root) — hãy cấp quyền qua ACL:

sudo apt install acl sudo setfacl -R -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null sudo setfacl -d -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null

4. Lớp bọc ModSecurity. Nhật ký kiểm tra của WAF (/var/log/apache2/modsec_audit.log) thuộc root với quyền 640, tài khoản web không thể đọc trực tiếp. Trang ModSecurity lấy chế độ engine, sự kiện và danh sách luật đang hoạt động qua một script chỉ đọc cố định — chính script này đã được cho phép trong sudoers ở dòng phía trê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
Không có tệp /etc/modsecurity/modsecurity.conf thì WAF không hoạt động: gói chỉ đặt modsecurity.conf-recommended, còn engine luật vẫn tắt — cách bật xem FAQ → “Cài đặt ModSecurity”.
Tài khoản trong mọi dòng sudoers phải trùng với tài khoản của pool FPM: trên Apache/Debian thông thường là www-data, còn trong HestiaCP pool của site chạy dưới chủ site (ví dụ admin) — hãy kiểm tra grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.

5. Nếu phía trước Apache có Nginx (HestiaCP, ISPmanager và các bảng điều khiển khác — ở đó Nginx proxy PHP sang Apache, còn nội dung tĩnh thì tự trả về). Các thư mục nội bộ được bảo vệ bằng tệp .htaccess, nhưng Nginx không đọc chúng: bất kỳ tệp tĩnh nào (.json, .txt, .log, .dat) nó sẽ trả về trực tiếp, bỏ qua Apache. Cache và dữ liệu của bảng điều khiển sẽ rò ra ngoài — chẳng hạn tmp/modsec_cache.json chứa sự kiện WAF và IP kẻ tấn công. Hãy thêm luật chặn vào cấu hình site của Nginx:

location ^~ /data/ { deny all; } location ^~ /tmp/ { deny all; } location ^~ /logs/ { deny all; } location ^~ /includes/ { deny all; } location ^~ /cron/ { deny all; } location ^~ /database/ { deny all; } location = /config.php { deny all; }
Tiền tố ^~ là bắt buộc: nó được chọn trước luật regex cho nội dung tĩnh bên trong location /, nếu không luật chặn sẽ không hiệu lực.
Trong HestiaCP hãy đặt nội dung này thành tệp riêng /home/<user>/conf/web/<domain>/nginx.ssl.conf_deny (và nginx.conf_deny cho HTTP) — cấu hình site có nạp nginx.ssl.conf_* và khi dựng lại sẽ không ghi đè các tệp này. Áp dụng: sudo nginx -t && sudo systemctl reload nginx.
Kiểm tra: curl -s -o /dev/null -w '%{http_code}\n' https://monitor.example.com/tmp/modsec_cache.json — phải là 403. Nếu Apache chạy không có Nginx (tự lắng nghe 80/443) thì không cần thêm gì — .htaccess là đủ.
Hãy kiểm tra đường dẫn tới các tệp nhị phân bằng which (ví dụ which ufw cscli ausearch ss). Chỉ chỉnh sửa sudoers qua visudo. Danh sách tất cả cơ sở dữ liệu MySQL được bật bằng một GRANT riêng (FAQ → “Chỉ thấy một CSDL”).

14. Giới hạn truy cập theo IP

Giới hạn truy cập vào monitor theo địa chỉ IP — ngay cả khi lộ URL, trang đăng nhập vẫn không mở được. Có thể làm ở mức máy chủ web (ví dụ cho nginx bên dưới) hoặc ngay trong dashboard (“Cài đặt” → “Giới hạn truy cập theo IP”). Nếu bạn dùng Apache, hãy dùng giới hạn trong dashboard.

Nếu website nginx đã được cấu hình theo bước 10, đừng thêm khối thứ hai location / — hãy chèn các dòng allow/deny vào khối đã có. Hai location / giống nhau trong cùng một server { } là lỗi cấu hình, nginx sẽ không khởi động lại được.
# Trong cấu hình nginx (bên trong server { }): # Giữ mở đường dẫn ACME của Let's Encrypt, bỏ qua giới hạn IP — # để việc cấp và tự động gia hạn SSL (bước 15) không phụ thuộc bộ lọc IP. location ^~ /.well-known/acme-challenge/ { allow all; } location / { allow 203.0.113.10; # ← nhập IP của bạn allow 10.0.0.0/8; # mạng nội bộ (nếu cần) deny all; try_files $uri $uri/ /index.php?$query_string; } # Tải lại nginx: sudo nginx -t && sudo systemctl reload nginx

15. Cấp SSL (HTTPS)

Dashboard chỉ hoạt động qua HTTPS. Phiên đăng nhập dùng cookie bảo mật, còn WebAuthn (2FA) chỉ chạy trên HTTPS. Không thể đăng nhập qua http://.

Chứng chỉ miễn phí (Let's Encrypt). DNS của tên miền phải trỏ sẵn về máy chủ. Lệnh tùy theo máy chủ web:

# Apache: sudo certbot --apache -d monitor.example.com # nginx — CHỈ khi bạn thực sự dùng nginx. Trên Apache thì ĐỪNG chạy: # apt sẽ kéo nginx về và chiếm cổng 80, gây xung đột với Apache. # sudo apt install python3-certbot-nginx # sudo certbot --nginx -d monitor.example.com # certbot sẽ tự ghi HTTPS vào config và thiết lập tự động gia hạn
certbot sẽ hỏi gì: e-mail → đồng ý Terms (Y) → gửi e-mail cho EFF (tùy bạn). Sau đó nó tự cấp chứng chỉ, ghi <VirtualHost *:443>, thiết lập chuyển hướng http→https và tự động gia hạn.
DNS phải trỏ về máy chủ TRƯỚC khi chạy certbot (xác minh quyền sở hữu qua cổng 80). Kiểm tra: dig +short monitor.example.com → IP máy chủ. Mở cổng 80/443: sudo ufw allow 80,443/tcp.

Sau khi cấp: https://monitor.example.com mở ra với biểu tượng khóa, http:// chuyển hướng sang https:// (APP_URL trong config.php đã được đặt ở bước 12).

16. Đăng nhập và thiết lập ban đầu

Mở https://monitor.example.com, đăng nhập admin / useradmin và làm theo danh sách kiểm tra:

  1. Đổi mật khẩu admin — mục “Người dùng” trong menu.
  2. Bật WebAuthn (2FA) — “Khóa WebAuthn” → đăng ký khóa/passkey (cần HTTPS). Hãy đăng ký ngay hai khóa: nếu mất khóa duy nhất bạn sẽ không thể đăng nhập bằng nó. Chi tiết.
  3. Giới hạn truy cập theo IP — “Cài đặt” → “Giới hạn truy cập theo IP” (nhập IP của bạn trước khi bật, nếu không bạn sẽ tự khóa quyền truy cập).
  4. Nhập giấy phép — kích hoạt mã ARCIVEO-… từ tài khoản cá nhân cho tên miền của bạn và dán khóa vào “Cài đặt” → “Giấy phép”. Chi tiết.
  5. Thiết lập thông báo — Telegram và/hoặc Email trong “Cài đặt”. Chi tiết.
  6. Xóa trình cài đặt public/start_db.php nếu còn (bước 11).

17. Công cụ bảo mật (tùy chọn)

Dashboard đã hoạt động. Các công cụ được cài theo nhu cầu — cần cái nào thì cài cái đó, dashboard sẽ hiển thị trạng thái ngay. Lệnh cài đặt cho từng công cụ có trong tài liệu tham khảo (các mục riêng theo từng công cụ):

18. Cron và bảo trì

Chỉ cấu hình một lần, cũng là tùy chọn nhưng nên làm. Các lệnh chi tiết có trong tài liệu tham khảo:

  1. Tác vụ Cron (báo cáo, cập nhật danh sách, kiểm tra);
  2. Sao lưu dữ liệu;
  3. Cập nhật và di chuyển dashboard;
  4. Khôi phục quyền truy cập — phòng khi mất khóa/mật khẩu.
Có gì đó không hoạt động hoặc hiển thị "không có dữ liệu"? Hãy xem nhóm "Chẩn đoán" trong tài liệu tham khảo.
Arcivéo - Security Monitor © 2026