FAQ

Ini adalah panduan untuk instalasi, konfigurasi, dan pemeliharaan Arcivéo Security Monitor. Bagian-bagiannya dikelompokkan: ikhtisar umum, penerapan dashboard, penyambungan alat keamanan, modul bawaan, dan diagnostik. Perintah dapat disalin dengan tombol di sebelah kanan.

Mulai dari mana

01. Instalasi panel — pilih metode

Instalasi panel tersedia di halaman langkah demi langkah tersendiri. Pilih metode:

Jika ragu, pilih yang otomatis. Panduan ini tetap menjadi sumber tunggal untuk SSL, alat, cron, dan diagnostik — halaman instalasi merujuk ke bagian-bagiannya tanpa menduplikasi apa pun.

Ikhtisar

02. Apa itu Arcivéo Security Monitor

Arcivéo Security Monitor — dashboard keamanan server. Mengumpulkan data dari alat yang terpasang (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco, dll.) dan menampilkannya dalam satu antarmuka dengan dashboard, peta serangan, dan halaman rinci untuk setiap alat.

Monitor bukan alat perlindungan aktif — ia tidak memblokir serangan sendiri. Tugasnya adalah menggabungkan informasi dari alat yang sudah berjalan dan menyajikannya dalam bentuk yang mudah dipahami.

03. Cara kerja monitor di server

Monitor bekerja hanya secara lokal — harus dipasang di server yang sama dengan yang dipantau. Tidak ada SSH atau API jarak jauh.

Semua perintah (fail2ban-client, ufw status, ipset list dsb.) dijalankan panel atas nama pengguna web server (biasanya www-data, di panel hosting — akun situs) dengan hak akses terbatas sudo — hanya untuk utilitas tertentu, tanpa akses root umum. Hasilnya diurai dan ditampilkan di browser.

Dua irama kerja. Halaman panel menanyai perkakas pada saat dibuka — Anda selalu melihat keadaan terkini. Selain itu, di latar belakang berjalan tugas terjadwal (cron):

  • setiap 5 menit — cuplikan beban untuk halaman “Performa”;
  • setiap 5 menit — sensor peristiwa keamanan: pemblokiran baru, peringatan IDS, perlindungan yang berhenti, port yang terbuka (bagian “Peristiwa keamanan”);
  • sekali sehari — laporan keamanan ke Telegram dan email, serta pemeriksaan berat (Lynis, AIDE, ClamAV, debsums, Logwatch);
  • setiap 15 menit — ringkasan server ke akun, jika diaktifkan.

Daftar lengkap tugas ada di bagian “Semua tugas cron dalam satu tempat”.

Untuk beberapa server, pasang monitor pada masing-masing secara terpisah dengan domain tersendiri. Agar semuanya terlihat dalam satu daftar, aktifkan ringkasan server (bagian “Ringkasan server di akun”).

04. Bagaimana Skor keamanan dihitung

Skor dimulai dari nilai maksimum dan berkurang untuk setiap masalah yang ditemukan:

  • UFW tidak aktif — −30
  • Fail2ban tidak berjalan (tidak ada jail aktif) — −25
  • Tidak ada kunci WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum tidak dimuat — −10
  • Ancaman ditemukan ClamAV — −20
  • Perubahan berkas AIDE — −15
  • SSL kedaluwarsa — −30, kedaluwarsa <14 hari — −15, <30 hari — −5
  • CrowdSec terpasang, tetapi tidak berjalan — −5
  • Suricata terpasang, tetapi tidak berjalan — −5
  • DBMS/cache (MySQL, PostgreSQL, Redis…) dapat diakses dari luar — −10
  • Login root via SSH diizinkan (PermitRootLogin yes) — −20
  • Login dengan kata sandi via SSH diizinkan (PasswordAuthentication yes) — −10
  • Pengaturan SSH kecil: X11Forwarding aktif, MaxAuthTries lebih dari 3, atau sshd_config bisa dibaca semua akun — −5
  • Pengamanan server web: respons memperlihatkan versi server web atau PHP, metode TRACE aktif, atau header Strict-Transport-Security atau X-Frame-Options tidak ada — −5
  • Pembaruan keamanan menunggu pemasangan — −5

Hasil: 80+ = Terlindungi, 60–79 = Perhatian, <60 = Terancam.

Pengurangan untuk ClamAV, AIDE, CrowdSec, dan Suricata hanya berlaku jika alat terpasang. Lynis dan AIDE tanpa basis data yang terinisialisasi ditampilkan sebagai “tidak ada data” dan tidak mengurangi skor. Jumlah serangan hari ini ditampilkan di dasbor, tetapi tidak memengaruhi Skor keamanan. Pengamanan server web tidak dihitung jika tidak ada situs yang menjawab pemeriksaan.

Pengaturan dan lisensi

05. WebAuthn — autentikasi dua faktor

WebAuthn — standar autentikasi tanpa kata sandi melalui kunci keamanan perangkat keras. Mendukung YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

Setelah masuk dengan kata sandi, sistem meminta konfirmasi melalui kunci keamanan yang terdaftar. Sekalipun kata sandi bocor — tanpa kunci fisik atau biometrik, masuk tidak dimungkinkan.

Untuk mengatur, buka Kunci WebAuthn di menu samping lalu klik “Daftarkan kunci”. Daftarkan dua kunci sekaligus: jika satu-satunya kunci hilang atau rusak, masuk ke dashboard dengan kunci itu tidak akan mungkin.

WebAuthn hanya bekerja melalui HTTPS. Pada koneksi HTTP, pendaftaran dan masuk dengan kunci tidak tersedia.

06. Notifikasi: Telegram, Email, webhook, dan push

Salurannya ada empat: Telegram, Email, webhook, dan push di peramban. Semuanya berdiri sendiri — aktifkan yang mana pun, bahkan keempatnya. Melalui saluran yang sama pula dikirim laporan keamanan harian, peringatan beban, dan temuan sensor peristiwa; semuanya diatur sekali saja di sini, pada bagian “Pengaturan”.

Telegram. Diperlukan token bot dan chat id:

  1. Di Telegram, kirim pesan ke @BotFather → /newbot → dapatkan token berbentuk 123456:ABC....
  2. Kirim pesan apa saja ke bot baru Anda (agar ia bisa membalas Anda).
  3. Cari tahu chat id Anda: kirim pesan ke bot @userinfobot, atau buka https://api.telegram.org/bot<TOKEN>/getUpdates dan temukan "chat":{"id":...}.
  4. Masukkan token dan chat id ke “Setelan” → Telegram, lalu klik “Simpan dan kirim tes”.

Email. Dua cara pilihan di “Setelan” → Email:

  • SMTP — host, port (465/SSL atau 587/TLS), login dan kata sandi kotak surat Anda;
  • Resend — API modern: masukkan kunci API (re_...) dan domain pengirim yang sudah diverifikasi.

Webhook. Satu alamat yang menerima POST sudah mencakup Slack, Discord, Mattermost, n8n, dan penangan buatan Anda sendiri. “Pengaturan” → Webhook: tempelkan alamatnya dan aktifkan sakelarnya.

  • ke Slack dan Mattermost dikirim {"text": …}, ke Discord {"content": …} (Discord menolak pesan yang lebih panjang dari 2000 karakter, jadi teksnya dipotong);
  • alamat lain mana pun menerima JSON terstruktur: text, event, severity, source, count, host, ts — teks pesan tidak perlu diurai lagi.

Pemberitahuan push. Pesan tiba langsung di peramban, dan jika panel sudah ditambahkan ke layar utama ponsel, tampil seperti pemberitahuan aplikasi biasa — tanpa bot dan tanpa server surel di tengahnya. “Pengaturan” → Pemberitahuan push: aktifkan sakelarnya dan tekan “Hubungkan perangkat ini” pada setiap perangkat yang perlu menerima pemberitahuan. Penghitung “Perangkat terhubung” menunjukkan berapa jumlahnya.

Push hanya bekerja lewat HTTPS (sama seperti WebAuthn), dan izin diperlukan dua kali: pertama di peramban — permintaannya muncul setelah tombol “Hubungkan perangkat ini” — lalu di sistem operasi itu sendiri. Jika di Windows sakelar utama Pengaturan → Sistem → Pemberitahuan dimatikan, peramban tetap memberi izin, panel melaporkan pengiriman, tetapi di layar tidak muncul apa pun. Hal yang sama terjadi pada mode “Jangan ganggu” dan pemberitahuan peramban yang dimatikan di macOS (Pengaturan Sistem → Pemberitahuan).
Setiap saluran punya tombol “Kirim tes” sendiri yang langsung memeriksanya. Jadwal laporan otomatis berjalan lewat cron (bagian “Semua tugas cron”): cron memicu pengiriman, dan salurannya diambil dari pengaturan.

Status laporan: “PERHATIAN” atau “OK”. Judul menjadi “PERHATIAN” hanya saat ada masalah nyata atau tindakan yang tertunda: ClamAV menemukan ancaman, perubahan file di AIDE, event kritis Falco (Emergency/Alert/Critical dalam 24 jam terakhir), layanan mati di Monit, perlu reboot, SSL akan kedaluwarsa (≤14 hari) atau ada pembaruan keamanan yang menunggu. Derau latar — percobaan brute force SSH oleh bot, IP yang dibanned fail2ban, alert Suricata, peringatan Lynis, serta permintaan ModSecurity yang sudah ditangkis — tidak menaikkan status, jadi angka-angka seperti itu dalam laporan bukan berarti “PERHATIAN” dengan sendirinya.

07. Lisensi — memasukkan dan mengaktifkan

Modul pemantauan terperinci (Lynis, UFW, ModSecurity, peta serangan, AIDE, ClamAV, dll.) terbuka jika ada lisensi aktif. Tanpanya, dasbor, pengaturan, dan akun tetap berfungsi, sedangkan modul menampilkan kartu “Perlu lisensi”.

Setelah membeli di akun, Anda punya kode aktivasi berformat ARCIVEO-XXXX-XXXX-XXXX-XXXX. Kode itu perlu “diaktifkan” ke domain panel Anda — ini mengubah kode menjadi berkas lisensi bertanda tangan (blok [license]) yang Anda tempelkan ke panel.

Cara mengaktifkan (3 langkah):

  1. Ambil kode aktivasi. Akun my.arciveo.com → bagian “Lisensi” / “Aktivasi lisensi” — salin kode ARCIVEO-….
  2. Aktifkan kode ke domain Anda. Di akun yang sama, buka “Aktivasi lisensi”, masukkan: kode aktivasi, email Anda, dan domain panel (alamat tempat monitor terbuka, mis. monitor.example.com). Klik aktifkan — sistem akan membuat berkas lisensi yang terikat ke domain itu dan menampilkannya di kolom dengan tombol “Salin”.
  3. Tempel kunci ke panel. Salin seluruh teks lisensi → di panel buka “Pengaturan” → blok “Lisensi”, tempel dan klik “Simpan”. Modul langsung terbuka.

Panel memverifikasi kunci secara kriptografis: tanda tangan, keterikatan ke domain, dan masa berlaku.

Domain saat aktivasi harus sama persis dengan alamat panel. Ambil dari konstanta APP_URL di config.php dan masukkan hanya nama host — tanpa https:// dan tanpa awalan www. Aktivasi hanya sekali: kode berubah menjadi lisensi untuk domain yang dimasukkan dan tidak dapat diaktifkan ulang — jika domain salah, kunci tidak cocok dengan panel Anda, dan kode terbuang. Karena itu masukkan domain dengan teliti.
Jika masa berlaku habis atau domain berubah — peringatan akan muncul di bagian atas panel. Lisensi terikat ke domain selamanya dan tidak dapat dipindahkan ke domain lain: untuk masa berlaku baru atau domain baru diperlukan kunci baru (dibeli di akun dan diaktifkan sekali pakai).

08. Berkas config.php — semua pengaturan panel

Semua parameter utama panel diatur dalam satu berkas config.php di root (di samping folder public/) berupa konstanta define() biasa. Berkas dibuat saat instalasi; menyuntingnya secara manual jarang diperlukan — biasanya hanya saat mengganti domain, memindahkan, atau menyambung ke basis data lain. Setelah setiap perubahan mulai ulang PHP-FPM (jika tidak, perubahan tak akan diterapkan karena OPcache).

Isikan nilai Anda pada bagian yang disorot; sisanya biarkan apa adanya:

// --- Basis data --- define('DB_HOST', 'localhost'); // biarkan define('DB_NAME', 'db_name'); // yang Anda tetapkan saat membuat BD define('DB_USER', 'user'); // yang Anda tetapkan saat membuat BD define('DB_PASS', 'db_password'); // yang Anda tetapkan saat membuat BD define('DB_CHARSET', 'utf8mb4'); // biarkan // --- Aplikasi --- define('APP_URL', 'https://monitor.example.com'); // alamat panel, tanpa garis miring di akhir define('TIMEZONE', 'Asia/Jakarta'); // zona waktu Anda // --- Waktu sesi --- define('SESSION_LIFETIME', 28800); // idle sampai masuk ulang, detik (28800 = 8 jam)

Basis data. Kredensial koneksi ke MySQL/MariaDB:

  • DB_HOST — host DBMS, hampir selalu localhost;
  • DB_NAME — nama basis data panel;
  • DB_USER — pengguna BD (akses hanya ke basisnya sendiri);
  • DB_PASS — kata sandi pengguna ini;
  • DB_CHARSET — enkode koneksi, biarkan utf8mb4.

Aplikasi.

  • APP_URL — alamat lengkap panel (mis. https://monitor.example.com). Harus sama dengan domain tempat lisensi diaktifkan — jika tidak, kunci akan ditolak (lihat bagian “Lisensi”);
  • TIMEZONE — zona waktu PHP: hanya memengaruhi cara panel menampilkan tanggal dan waktu. Tidak memengaruhi waktu jalannya tugas cron — di sana berlaku zona sistem (lihat “Semua tugas cron”).

Waktu sesi. SESSION_LIFETIME — batas waktu idle sesi dalam detik (bergulir: diperbarui saat ada aktivitas). Bawaannya 28800 = 8 jam; setelah durasi tak aktif ini, panel akan meminta masuk kembali. Misalnya, 3600 = 1 jam, 86400 = sehari.

Pencatatan galat. Galat tak pernah ditampilkan ke pengunjung, melainkan ditulis ke logs/php_errors.log — terlihat di halaman “Log aplikasi”. Baris-baris ini (display_errors=0, log_errors=1, jalur error_log) biasanya tak perlu diubah — pengaturan ditetapkan langsung di berkas dan tidak bergantung pada php.ini.

config.php — berkas rahasia. Di dalamnya ada kata sandi BD. Berkas ini terletak di root panel (di samping public/), dan web-root (DocumentRoot) panel ini adalah root panel itu sendiri, bukan public/. Berkas ini tidak “bocor” dengan sendirinya: di .htaccess root ada larangan eksplisit untuknya (Require all denied) — server mengembalikan 403. Bahkan tanpa aturan ini, sumbernya tak akan bocor: ini PHP — server mengeksekusinya, bukan menyajikannya sebagai teks. Untuk berjaga-jaga: jangan unggah ke repositori publik dan jangan kirim ke dukungan dengan kata sandi asli. Izin berkas — 640.
Saat memindahkan atau memulihkan akses, berkas ini adalah sumber utama kredensial: nama BD, pengguna, dan kata sandi diambil dari sini (lihat bagian “Pembaruan dan pemindahan panel” dan “Pemulihan akses”).

09. API hanya-baca (JSON dan Prometheus)

Panel dilindungi login berkata sandi dan, bila diaktifkan, oleh penyaring IP. Bagi sebuah program keduanya tidak cocok, karena itu sistem luar punya pintu masuk sendiri — dua alamat hanya-baca yang dilindungi token. Token tidak mengubah apa pun di server: dengannya hanya bisa diambil angka-angka.

Mengaktifkan — “Pengaturan” → “API hanya-baca”: sakelar “Aktifkan API”; tokennya dibuat otomatis pada penyimpanan pertama. Tombol “Token baru” menerbitkan token lain dan pada saat itu juga memutus semua yang memakai token sebelumnya.

  • /api/status.php — JSON: skor keamanan, keadaan modul, pengukuran beban terakhir beserta proses teratasnya, dan peristiwa keamanan selama 24 jam terakhir;
  • /api/metrics.php — angka yang sama dalam format teks Prometheus (metrik arciveo_*: arciveo_security_score, arciveo_fail2ban_banned, arciveo_cpu_busy_pct, dan seterusnya).
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

Jika klien Anda tidak bisa memasang header, token juga diterima sebagai parameter — ?token=…. Perlu diingat, dalam bentuk itu token akan tercatat di log akses server web, jadi header lebih dianjurkan.

Prometheus. Blok siap pakai untuk scrape_configs:

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
Jawabannya disimpan dalam cache selama 5 menit: di balik angka-angka itu ada pengumpulan berat yang sama seperti di balik dasbor. Karena itu pengambilan yang sering tidak membebani server — nilainya sekadar berulang sampai penyegaran berikutnya — tetapi selang di bawah satu menit juga tidak ada gunanya.
401 — token salah atau tidak dikirim. 404 dengan teks api disabled — API dimatikan di pengaturan. Permintaan yang ditolak dicatat di log aplikasi bersama IP pengirimnya, sehingga upaya menebak token terlihat pada halaman “Log aplikasi”.

10. Ringkasan server di akun

Panel dipasang pada tiap server secara terpisah dan menjawab dengan baik pertanyaan “bagaimana keadaan mesin ini”. Pertanyaan “bagaimana keadaan semua mesin saya” tidak dijawabnya — untuk itu setiap panel dapat mengirim ringkasan singkat ke akun, tempat semua server terlihat dalam satu daftar.

Yang diambil di akun my.arciveo.com → bagian “Server saya”: di sana diterbitkan token (berbentuk fk_…) dan dicantumkan alamat penerima https://my.arciveo.com/api/fleet.php. Tokennya satu untuk satu akun — sama untuk semua server Anda.

Yang diatur di panel — “Pengaturan” → “Ringkasan server”: alamat penerima, token, nama server (bagaimana ia akan tertulis di daftar; jika dibiarkan kosong, dipakai domain panel), dan sakelar “Kirim ringkasan”. Tombol “Kirim sekarang” menguji sambungan seketika, tanpa menunggu jadwal.

Selanjutnya ringkasan dikirim lewat cron, setiap 15 menit:

# setiap 15 menit — ringkasan server ke akun 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Apa persisnya yang dikirim: nama dan domain server, sistem operasi, lama menyala, versi PHP; skor keamanan; keadaan modul (UFW, jumlah jail Fail2ban, pemblokiran hari ini, apakah CrowdSec, Suricata, dan Falco berjalan, ancaman ClamAV, perubahan AIDE, indeks Lynis, pembaruan yang menunggu, apakah perlu boot ulang, sisa hari sebelum sertifikat kedaluwarsa, jumlah port terbuka yang berbahaya); pengukuran beban terakhir (load, CPU, RAM, disk, inode); penghitung peristiwa selama 24 jam terakhir dan peristiwa kritis terakhir. Log, kata sandi, maupun isi berkas tidak ada dalam ringkasan.

Servernya yang mengirim, bukan akun yang bertanya. Karena itu tidak perlu membuka jalan masuk ke jaringan Anda dari luar, dan panel yang ditutup penyaring IP pun tetap melapor. Simpul dikenali dari domain panel, jadi daftarnya tidak berganda setelah panel dimulai ulang atau IP berubah.
Server yang di akun ditandai diam berarti ringkasannya sudah lama tidak datang. Periksa berurutan: sakelar “Kirim ringkasan”, baris cron (sudo crontab -l), dan jawaban tombol “Kirim sekarang”. Galat pengiriman dicatat di log aplikasi dengan tanda FLEET — di sana terlihat apakah tokennya ditolak (HTTP 401) atau sambungannya sendiri yang tidak terjalin.

Alat keamanan

11. Firewall UFW

UFW (Uncomplicated Firewall) — antarmuka sederhana untuk nftables/iptables. Menutup semua port masuk kecuali yang diizinkan secara eksplisit. Halaman “Firewall UFW” menampilkan status dan aturan.

sudo apt install ufw # Izinkan SSH (wajib SEBELUM mengaktifkan!) dan web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Tutup basis data dari luar (akses hanya lokal) sudo ufw deny 3306 # Aktifkan dan periksa sudo ufw enable sudo ufw status verbose
Sebelum ufw enable, wajib izinkan SSH (ufw allow OpenSSH), jika tidak Anda akan kehilangan akses ke server.
“Eksposur eksternal” pada dashboard memperhitungkan UFW: port yang ditutup dengan aturan deny tidak dianggap dapat diakses dari luar.
Skipping adding existing rule — ini bukan kesalahan. Dengan begitu UFW memberi tahu bahwa aturan yang sama persis sudah ada, dan tidak menambahkannya lagi. Saat menjalankan ulang konfigurasi otomatis (yang bersifat idempoten), ini adalah pesan normal — tidak perlu ditanggapi.

12. Instalasi Fail2ban

Memblokir IP secara otomatis setelah jumlah percobaan login gagal melebihi batas. Menganalisis log SSH, nginx, Apache, dan layanan lainnya.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Periksa status: sudo fail2ban-client status
Konfigurasi kerja (jail.local dengan puluhan jail dan autoban dari ipsum) — ada di bagian berikutnya.

13. Konfigurasi kerja Fail2ban + ipsum

Instalasi dasar ada di atas. Di sini adalah konfigurasi kerja yang menghasilkan puluhan jail aktif dan ribuan blokir: pengaturan umum, jail utama, dan autoban IP berbahaya dari daftar ipsum.

Berkas /etc/fail2ban/jail.local — pengaturan umum dan jail terpenting:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Ban progresif: tiap pengulangan makin lama bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # ban permanen untuk brute-force SSH findtime = 3600 # Residivis: yang kena beberapa kali ban akan diban selamanya [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 # Layanan web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … dan jail lainnya per layanan (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
Pada ignoreip wajib masukkan IP Anda sendiri dan jaringan tepercaya, jika tidak Anda bisa memblokir diri sendiri. Setelah perubahan: sudo fail2ban-client reload.

Pemuatan otomatis daftar blokir ipsum — ke cron root (sudo crontab -e): level 1 (100+ ribu IP) dimuat ke set ipsum, yang diblokir di firewall (selengkapnya di bagian “Daftar blokir IPset”):

# 04:00 — pembaruan ipset ipsum (level 1, cakupan maksimal): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Set harus bernama ipsum — itulah yang dibaca dasbor (kartu “IPset ipsum”). Level: levels/1.txt — cakupan maksimal, levels/3.txt — lebih akurat (3+ sumber).

Mengapa “Monitor keamanan” dibagi menjadi dua zona. Perlindungan bekerja di dua tingkat, dan dasbor tidak mencampurnya:

  • Serangan nyata (reaktif) — semua yang ditangkap fail2ban: upaya peretasan langsung (jail sshd, apache-*, nginx-* dsb.) dan residivis berat (jail recidive — yang sudah diban beberapa kali). Ini IP yang benar-benar menyerang Anda — mereka ada di peta serangan dan “Garis waktu”.
  • Blokir preventif (proaktif) — daftar blokir publik berisi IP berbahaya yang diketahui ipset ipsum, diblokir di firewall dengan aturan DROP. Sebagian besar alamat ini bahkan tidak pernah menyentuh server Anda — mereka diblokir lebih awal; penghitung “IPset ipsum” menunjukkan berapa banyak yang diblokir secara preventif.

Bedanya sederhana: reaktif — “mereka menyerang lalu diban”, preventif — “mereka diblokir bahkan sebelum mencoba”. Dulu recidive diisi secara artifisial dengan list-3 ipsum (dari sinilah muncul pembagian lama “recidive daftar”); kini recidive hanya berisi residivis asli, sedangkan preventif sepenuhnya di firewall.

14. Daftar blokir IPset (ipsum)

ipsum — daftar publik IP berbahaya yang diperbarui setiap hari. Monitor menampilkan jumlah alamat yang dimuat di dasbor dan peta serangan serta memperhitungkannya dalam Skor Keamanan (−10 jika kumpulan tidak dimuat).

Varian minimal tanpa fail2ban — kumpulan ipsum tersendiri dengan pemblokiran lewat iptables:

# Buat kumpulan (sekali saja): sudo ipset create ipsum hash:ip # Skrip pembaruan /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, setiap hari pukul 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Varian lanjutan dengan fail2ban-recidive — di bagian “Konfigurasi kerja Fail2ban + ipsum”.
ipset tersimpan di memori dan hilang saat reboot. Cron harian saja akan membuat kumpulan kosong sejak reboot hingga eksekusi berikutnya (dasbor menampilkan 0). Muat kumpulan juga saat start — pindahkan pemuatan ke skrip dan pasang pada @reboot. Sekalian perintah create … -exist menetapkan batas maxelem 300000 (secara default 65536 — level 1 tidak muat, akan muncul “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 — setiap hari pukul 04:00 DAN setiap start: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Cara kerja saat instalasi otomatis. Skrip memuat seluruh daftar level 1 (100+ ribu IP) ke kumpulan ipsum dan, jika firewall dikelola oleh penginstal (VPS baru — profil “Penuh”/“Ringan”), menautkan kumpulan ke UFW dengan aturan DROP — trafik dari IP ini benar-benar diblokir. Aturan ditempatkan setelah ESTABLISHED,RELATED, sehingga koneksi yang sedang berjalan (termasuk SSH Anda) tidak terputus — hanya koneksi baru dari daftar yang dipotong. Kumpulan dipulihkan saat pemuatan layanan ipsum-load.service sebelum firewall (jika tidak, UFW tidak akan aktif), dan diperbarui oleh cron pada pukul 04:00. Pada server yang sudah dikonfigurasi (panel, firewall sendiri), penginstal tidak menyentuh firewall — di sana ipsum tetap menjadi daftar untuk dasbor dan peta serangan, sedangkan aturan DROP dapat ditambahkan manual bila diinginkan (varian minimal dengan iptables … --match-set ipsum … -j DROP — di atas). Saat instalasi otomatis, Anda tidak perlu melakukan apa pun secara manual.

15. Instalasi CrowdSec

Pengganti modern untuk Fail2ban dengan threat intelligence kolektif: pemblokiran dari komunitas plus aturan buatan sendiri. Membutuhkan bouncer terpisah untuk menerapkan pemblokiran ke firewall.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer untuk iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Periksa status: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Status “Tidak berjalan” di panel = layanan terpasang, tetapi service tidak aktif (monitor memeriksanya melalui systemctl is-active crowdsec). Aktifkan: sudo systemctl enable --now crowdsec; jika gagal, lihat sudo journalctl -u crowdsec -n 30. Aturan yang sama berlaku untuk layanan apa pun berstatus “Tidak berjalan” (Suricata, Falco, Monit, MySQL).
“0 skenario” atau “0 bouncers” di dasbor. CrowdSec bawaan hampir kosong — tanpa koleksi ia tidak mendeteksi apa pun, dan tanpa bouncer yang terdaftar pemblokiran tidak diterapkan ke firewall. Pasang koleksi dasar dan pastikan bouncer ada di daftar:
# Koleksi dasar (Linux + SSH + server web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer harus ada di daftar dan berstatus koneksi aktif: sudo cscli bouncers list
Di log bouncer muncul stream halted / pemblokiran tidak diterapkan. Ini kunci api yang terlantar: bouncer telah dihapus dari cscli bouncers list, tetapi kunci lamanya masih ada di /etc/crowdsec/bouncers/*.yaml. Daftarkan ulang bouncer dan tuliskan kunci baru:
sudo cscli bouncers add fw-bouncer # menampilkan api_key baru # tuliskan kunci ini ke api_key: di /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
Instalasi otomatis (profil “Perlindungan penuh”) secara otomatis memasang koleksi dan mendaftarkan firewall-bouncer — secara manual ini hanya diperlukan saat instalasi manual atau setelah intervensi manual pada CrowdSec.

16. Instalasi AIDE

AIDE (Advanced Intrusion Detection Environment) membuat snapshot sistem berkas dan pada setiap pemeriksaan melaporkan perubahan di /etc, /bin, /usr. Setelah instalasi, inisialisasi basis data wajib dilakukan (aideinit).

sudo apt install aide # Inisialisasi basis data (5–15 menit): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: direktori /var/lib/aide dibuat dengan mode 700 (pemilik _aide), # dan dasbor (www-data) tidak melihat basis data → menampilkan “Belum diinisialisasi”. # Buka direktori agar dapat dilewati (berkas basis data tetap 600): sudo chmod 755 /var/lib/aide # Pemeriksaan pertama DENGAN MENULIS ke log yang dibaca monitor. # Di Ubuntu/Debian aide membutuhkan --config eksplisit (jika tidak “missing configuration”; # biner aide.wrapper tidak disertakan pada versi baru): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Selama aideinit terminal berhenti selama 5–15 menit pada baris Running aide --init... — ini normal (hashing seluruh sistem berkas, beban pada disk). Jangan hentikan dengan Ctrl+C. Jika proses “menggantung” tetapi tidak menulis apa pun — mungkin ia menunggu jawaban atas permintaan tersembunyi Overwrite existing aide.db.new [Yn]? (tekan Y). Periksa aktivitas dari sesi lain: pgrep -af aide.
Kesalahan aideinit: “21_aide_spamassassin … printf: invalid number” (return code 20) — bug yang dikenal pada snippet konfigurasi AIDE di Ubuntu 22.04. Basis data tidak dibuat. Keluarkan snippet yang rusak lalu ulangi:
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
Status pada dasbor. “Belum diinisialisasi” = panel tidak melihat berkas basis data: entah aideinit belum dijalankan, atau (Ubuntu 24.04) direktori /var/lib/aide dibuat dengan mode 700 dan tidak dapat diakses www-data — diperbaiki dengan sudo chmod 755 /var/lib/aide (lihat blok di atas). “Belum ada pemeriksaan” = basis data ada, tetapi pemeriksaan belum pernah dijalankan — ini bukan kesalahan. Hasilnya dibaca monitor dari /var/log/aide/aide.log.
Pemeriksaan rutin → log untuk panel. /etc/cron.daily/aide bawaan pada Ubuntu/Debian baru mungkin tidak menulis /var/log/aide/aide.log dalam bentuk yang diperlukan (dan aide.wrapper sudah tidak ada di dalamnya). Lebih andal menambahkan cron sendiri dengan --config eksplisit — ia menulis log sebagai root dengan mode 644, dan monitor membacanya tanpa grup tambahan:
# sudo crontab -e — pemeriksaan harian pukul 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Jalankan sekarang, tanpa menunggu jadwal: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Instalasi otomatis sudah melakukan semua ini: chmod 755 /var/lib/aide dan cron pemeriksaan pukul 02:00 — tidak perlu apa pun secara manual.
Lakukan inisialisasi pertama pada server yang bersih — sebelum memasang aplikasi web. Setelah perubahan yang sah, buat ulang basis data: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

17. Instalasi ClamAV

Pemindai antivirus untuk Linux. Sangat berguna untuk memeriksa /var/www dari PHP-shell dan kode berbahaya.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Perbarui basis data signature: sudo freshclam # Pindai folder secara manual: sudo clamscan -r /var/www --infected
Daemon clamd menampilkan “Tidak aktif” setelah enable --now? Tiga penyebab umum:

1. Baris Example masih ada di konfigurasi — clamd menolak start selama baris itu ada:

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

2. Basis data signature belum diunduh — clamd tidak start tanpanya:

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

3. Sedang memuat saja — clamd memuat ~8 juta signature ke memori selama 30–60 detik. Tunggu dan periksa: systemctl is-active clamav-daemon (status activating → masih memuat).

Diagnostik: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Di dashboard “File diperiksa: 0” / “Pemindaian terakhir: —”? Daemon clamd hanya menyimpan signature di memori, ia sendiri tidak memindai apa pun secara terjadwal. Dashboard menampilkan hasil pemindaian terjadwal, jadi diperlukan cron yang memindai dan menulis log. Instalasi otomatis memasang wrapper /usr/local/bin/clamav-scan.sh dan cron pada 01:30 — setelah menjalankan pertama kali, “File diperiksa” dan “Pemindaian terakhir” akan terisi. Jalankan langsung tanpa menunggu jadwal: sudo /usr/local/bin/clamav-scan.sh.

18. Instalasi Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — pemindai malware untuk ancaman web: PHP-shell, backdoor web, downloader. Menggunakan mesin ClamAV dan melengkapinya dengan signature miliknya sendiri.

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 # Perbarui signature: sudo maldet -u # Pindai /var/www: sudo maldet -a /var/www
LMD dan ClamAV bekerja baik berpasangan. Laporan terakhir: maldet --report.
Saat instalasi mungkin muncul baris update-rc.d: error: unable to read /etc/init.d/maldet — baris ini tidak berbahaya. maldet tidak menggunakan init.d, pembaruan signature dan pemindaian dijalankan melalui /etc/cron.daily/maldet. Jika di bawah terlihat installation completed — semuanya sudah terpasang.
Di halaman LMD tertulis “Tidak terpasang”, padahal sudah terpasang? maldet dipasang bukan melalui apt, melainkan di /usr/local/maldetect, dan saat open_basedir aktif keberadaannya diperiksa melalui shell — lihat bagian “Halaman kosong padahal data ada di server”.

19. Instalasi Suricata

Sistem deteksi intrusi jaringan: menganalisis lalu lintas pada tingkat paket dan mengenali ribuan signature serangan. Melengkapi ModSecurity (yang bekerja pada tingkat HTTP, sedangkan Suricata pada tingkat TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Unduh aturan terbaru: sudo suricata-update sudo systemctl enable --now suricata
Suricata “Aktif”, tetapi dashboard tidak menampilkan alert / jumlah kejadian 0? Suricata menulis /var/log/suricata/eve.json sebagai root dengan mode 750 pada direktori, dan server web (www-data) tidak dapat membacanya. Buka akses lintas direktori — file di dalamnya tetap terlindungi:
sudo chmod o+rx /var/log/suricata
Instalasi otomatis melakukan ini sendiri — tidak perlu manual.

20. Instalasi Falco

Menangkap system call melalui eBPF/kernel module dan mendeteksi anomali secara real-time: shell dari nginx, pembacaan /etc/passwd oleh proses web, penulisan ke /bin, dsb.

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 membaca peristiwa Falco melalui journalctl -u falco (tanpa sudo — lewat grup systemd-journal). Pastikan www-data ada di grup ini — lihat “Konfigurasi sudo” (poin 2) pada halaman instalasi manual.
“0 peristiwa dalam 24 jam” adalah hal normal, bukan kesalahan. Falco bersifat event-driven: ia diam selama semuanya baik-baik saja, dan hanya mencatat peristiwa saat terjadi anomali (shell dari proses web, pembacaan /etc/passwd, penulisan ke direktori sistem). Nol peristiwa kritis dalam sehari pada server yang tenang adalah kondisi yang sehat.
Untuk panel, output ke file lebih andal. Pembacaan melalui journalctl memerlukan izin atas jurnal; agar panel melihat peristiwa secara stabil, instalasi otomatis mengaktifkan file_output pada Falco → /var/log/falco/falco.log dan mengatur UMask=0022 pada layanan (log dapat dibaca web server). Pada instalasi baru, ini tidak perlu dikonfigurasi secara manual.

21. Instalasi ModSecurity (WAF)

ModSecurity — firewall web (WAF) untuk Apache atau Nginx. Memblokir serangan di lapisan aplikasi: injeksi SQL, XSS, path traversal, pemindai.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Set aturan OWASP Core Rule Set: sudo apt install modsecurity-crs # WAJIB: tanpa file ini mesin aturan dalam keadaan mati 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 # Pemeriksaan: harus mengembalikan 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Instalasi paket saja tidak melindungi apa pun. Apache memuat konfigurasi dengan baris IncludeOptional /etc/modsecurity/*.conf, sedangkan paket hanya meletakkan modsecurity.conf-recommended — file ini tidak cocok dengan pola *.conf. Jika tidak disalin ke modsecurity.conf, SecRuleEngine tetap Off: modul termuat, aturan CRS termuat, tetapi trafik tidak diperiksa dan log audit tidak dibuat. Mode antara DetectionOnly hanya menulis peristiwa ke log tanpa memblokir permintaan — dashboard menampilkannya dengan warna kuning.

Akses dashboard ke log audit. Log /var/log/apache2/modsec_audit.log dimiliki oleh root (hak 640), pengguna web tidak bisa membacanya. Dashboard mengambil data melalui pembungkus — buatlah:

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 # di /etc/sudoers.d/monitor (pengguna = yang menjalankan PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Pembungkus mengambil direktif SecRuleEngine terakhir tanpa indentasi: baris berindentasi berada di dalam blok <LocationMatch>/<Directory> (misalnya mematikan WAF untuk phpMyAdmin) dan tidak menentukan mode global.
Pengguna di sudoers harus sama dengan pengguna pool FPM: pada Apache/Debian biasa itu adalah www-data, di HestiaCP pool situs berjalan sebagai pemilik situs (misalnya admin) — periksa dengan grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Jika situs berada di belakang proxy Nginx (HestiaCP), Apache melihat proxy itu sendiri sebagai klien — dashboard mengambil IP asli penyerang dari header X-Forwarded-For. Yang masuk ke statistik hanya transaksi dengan aturan yang terpicu: direktif SecAuditLogRelevantStatus menulis semua respons 4xx/5xx ke log audit, sehingga 403/500 biasa pun ikut masuk — dashboard tidak menghitungnya sebagai peristiwa WAF.
Blok ---RULES--- dibutuhkan oleh bagian “Semua aturan aktif” — dashboard menampilkan bukan hanya aturan yang terpicu, melainkan seluruh aturan CRS + kustom yang termuat. Tiga path dalam loop for f in … adalah lokasi khas untuk aturan CRS dan tambahan lokal; jika tata letak Anda berbeda (paket meletakkan file di direktorinya sendiri, atau aturan kustom tidak berada di /etc/modsecurity/custom-rules.conf), temukan path sebenarnya dengan perintah sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null dan masukkan ke daftar. Jika pembungkus versi lama (tanpa bagian ini) — bagian tersebut hanya akan menampilkan peringatan “tidak tersedia”, sisa halaman berfungsi seperti sebelumnya.

22. Instalasi Auditd

Auditd (Linux Audit Daemon) mencatat panggilan sistem pada level kernel: login dan logout, perintah sudo, upaya autentikasi yang gagal, perubahan berkas. Monitor menampilkan login, upaya yang gagal, dan perintah sudo hari ini.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Periksa status dan peristiwa: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Monitor membaca peristiwa melalui ausearch (/usr/sbin/ausearch) dan bila perlu dari /var/log/audit/audit.log dengan perintah tail. Keduanya harus ada di sudoers.

23. Instalasi Monit

Memantau layanan (nginx, php-fpm, mysql, dll.) dan memulai ulang saat gagal. Bisa mengirim peringatan ke email.

sudo apt install monit sudo systemctl enable --now monit # Konfigurasi: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Monitor mengambil daftar layanan melalui monit status. Di /etc/monit/monitrc antarmuka HTTP harus diaktifkan (blok set httpd dengan allow localhost), jika tidak monit status akan mengembalikan error.
Di dasbor tertera “0 layanan dipantau”? Ada dua penyebab. (1) Antarmuka HTTP dimatikan — di monitrc baris set httpd dikomentari (secara default tertulis sebagai # set httpd port 2812 …). Hapus komentar pada blok itu dan izinkan localhost. (2) httpd yang aktif saja tidak memantau apa pun — Monit hanya menghitung yang dijelaskan pada stanza check; tanpa itu daftarnya kosong meski antarmuka berjalan. Konfigurasi minimal yang berfungsi:
# /etc/monit/conf.d/00-httpd — antarmuka HTTP untuk localhost: set httpd port 2812 use address localhost allow localhost # contoh stanza check (yang dipantau): 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 # periksa sintaks (Control file syntax OK) sudo systemctl reload monit sudo monit status
Instalasi otomatis meletakkan conf.d siap pakai dengan httpd di 2812 beserta seperangkat pemeriksaan — pada instalasi baru tidak perlu diatur manual.
Layanan berstatus “Bermasalah”? Monitor hanya menampilkan status dan sengaja tidak memulai ulang layanan dari panel web (itu akan menjadi eksekusi perintah root jarak jauh di panel keamanan). Diagnostik dan mulai ulang — lewat SSH via Monit:
sudo monit status <service> # penyebab error sudo monit restart <service> # mulai ulang via Monit # jika Monit tidak berhasil menghidupkan layanan — lihat unit-nya sendiri: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. Instalasi PSAD (deteksi pemindaian port)

PSAD menganalisis log iptables dan mendeteksi pemindaian port serta serangan jaringan, memberikan tingkat ancaman (1–5) ke setiap sumber. Melengkapi fail2ban dan Suricata.

sudo apt install psad # PSAD membaca log iptables — logging harus diaktifkan (UFW melakukannya sendiri). # Untuk iptables murni, tambahkan aturan LOG ke rantai INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Monitor membaca data melalui psad --Status (perlu ada di sudoers). Tanpa logging iptables, halaman akan kosong — ini normal selama belum ada pemindaian.

25. AppArmor / SELinux (kontrol akses)

Mandatory Access Control membatasi file dan sumber daya apa saja yang bisa diakses sebuah program, bahkan jika program itu telah diretas. Di Ubuntu/Debian, AppArmor dipakai secara default (biasanya sudah terpasang dan aktif).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # periksa profil
Monitor membaca status melalui aa-status (perlu ada di sudoers). Menampilkan jumlah profil dalam mode enforce/complain dan proses tanpa profil.

“Profil dimuat” lebih banyak daripada enforce + complain — itu normal. Pada AppArmor 4.x (Ubuntu 24.04 dan lebih baru) muncul mode unconfined: profil dimuat ke kernel, tetapi tidak membatasi apa pun. Ubuntu menandai puluhan profil seperti ini untuk program yang memakai user namespaces (peramban, klien torrent, dan sejenisnya). Ketika profil semacam itu ada, kartu “Profil dimuat” menjadi kuning dan menampilkan jumlahnya — misalnya unconfined: 90 dari 120 yang dimuat dan 26 dalam enforce. Yang benar-benar melindungi hanya profil dalam enforce; di Ubuntu 22.04 (AppArmor 3.x) mode ini tidak ada sehingga angkanya selalu cocok.

sudo aa-status | grep -E "profiles are" # rincian per mode sudo aa-enforce /etc/apparmor.d/profile-name # ubah profil ke enforce
Mengubah ke enforce profil yang sengaja dibiarkan Ubuntu dalam unconfined hanya layak dilakukan secara sadar: profil itu dimatikan bukan karena kesalahan, melainkan karena kalau tidak, program itu sendiri jadi rusak. Profil dalam complain berbeda halnya: di situ aturannya sudah ditulis dan hanya belum diterapkan.

26. Instalasi debsums (integritas paket)

debsums memverifikasi bahwa file paket yang terpasang cocok dengan checksum dari repositori — mendeteksi biner sistem yang dipalsukan (melengkapi AIDE). Pemindaian penuh memakan 1–2 menit, sehingga dijalankan lewat cron, dan dashboard membaca hasilnya dari data/debsums/debsums.log lalu mengelompokkannya sendiri per kategori (hanya biner dan pustaka yang penting).

Tugas ditempatkan di cron root (sudo crontab -e). Pembungkus siap pakai debsums-scan.sh diletakkan di /usr/local/bin/ (chmod +x; lihat ringkasan tugas cron) dan menulis laporannya sendiri ke data/debsums/ dashboard.

sudo apt install debsums # Baris cron (setiap hari 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Pembungkus debsums-scan.sh menemukan sendiri data/ dashboard — path tidak perlu dituliskan.

Perubahan di /etc/ (konfigurasi) dan /usr/share/ (sumber daya) pada server biasanya wajar — dashboard menandainya dengan warna tersendiri. Yang mengkhawatirkan adalah perubahan pada biner dan pustaka (/bin, /sbin, /usr/lib dsb.) — kartu “Biner / pustaka” menampilkan tepat perubahan tersebut.

27. Konfigurasi laporan Lynis

Lynis dijalankan manual atau lewat cron. Laporan harus disimpan ke folder data/lynis/ proyek — monitor membaca file lynis-report.dat.

# Jalankan sekali (isi dengan path root panel Anda): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Audit harian — baris cron (wrapper lynis-scan.sh siap pakai di /usr/local/bin/, lihat ringkasan): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Setelah dijalankan pertama kali, halaman “Audit Lynis” langsung menampilkan hardening index, peringatan, dan rekomendasi.
Tombol “Jalankan audit” di halaman Lynis. Tombol ini menjalankan lynis-scan.sh di latar belakang langsung dari panel (tanpa menunggu cron): menampilkan “Memindai…” dan setelah selesai laporan diperbarui otomatis. Untuk itu, pengguna web memerlukan baris sudoers agar bisa menjalankan skrip — installer menambahkannya otomatis ke /etc/sudoers.d/monitor. Jika panel dipasang manual/sebelumnya, tambahkan baris tersebut dengan pengguna yang sama yang sudah tercantum di file:
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

28. Konfigurasi laporan Logwatch

Logwatch harus menyimpan laporan harian ke folder data/logwatch/ proyek dalam format .txt. Monitor menampilkan laporan terbaru dan arsipnya.

# Harian (6:00) — baris cron (wrapper siap pakai logwatch_daily.sh di /usr/local/bin/, lihat ringkasan): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Modul dashboard

29. Monitor jaringan (bawaan)

Monitor jaringan tidak perlu diinstal — ini halaman dashboard bawaan. Halaman ini menampilkan status jaringan server dari sumber lokal:

  • antarmuka dan trafik — dari /proc/net/dev;
  • status link (UP/DOWN) dan IP — via ip;
  • koneksi dan port yang mendengarkan — via ss;
  • peristiwa jaringan kernel selama 24 jam — via journalctl -k.

Tiga sumber pertama bekerja tanpa sudo, jadi antarmuka, trafik, koneksi, dan port langsung terlihat. Blok “Peristiwa kernel” memakai journalctl -k — dibaca melalui grup systemd-journal (“Konfigurasi sudo”, poin 2), tidak perlu sudo. Untuk memastikan semuanya bisa diakses pengguna web:

# Pemeriksaan sebagai www-data (PHP berjalan di bawahnya): 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
Blok “Peristiwa jaringan kernel” menampilkan peristiwa stack jaringan kernel (perubahan link up/down, error carrier, “network unreachable”). Entri firewall UFW BLOCK tidak masuk ke sini — ada di halaman “Firewall UFW” dan “Peta serangan”. Blok kosong dengan centang hijau = tidak ada gangguan jaringan selama sehari.

30. Disk dan SMART

Halaman bawaan menampilkan tiga hal:

  • Sistem berkas — pemakaian partisi (df); skala memerah saat ≥90%;
  • Penyimpanan — daftar disk (lsblk), hanya yang nyata (loop/snap disembunyikan);
  • Kesehatan (SMART) — status disk dan atribut (smartctl).

Ruang dan daftar perangkat langsung berfungsi tanpa pengaturan. Untuk SMART diperlukan paket smartmontools. Proses web tidak memiliki akses langsung ke perangkat disk, sehingga SMART diambil lewat cron ke berkas data/disk/smart.txt, lalu dibaca oleh dashboard.

Tugasnya ada di cron root (sudo crontab -e). Pembungkus siap pakai smart-scan.sh ditaruh di /usr/local/bin/ (chmod +x; lihat ringkasan tugas cron) dan langsung menulis ke data/disk/ dashboard.

sudo apt install smartmontools # Baris cron (setiap 30 menit): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Pembungkus smart-scan.sh menemukan sendiri data/ dashboard — jalurnya tidak perlu ditulis. Di dalamnya lsblk -e7,11 mengecualikan loop/cdrom.

Pada disk virtual (QEMU/KVM dan sejenisnya) biasanya hanya tersedia status umum “kesehatan: OK”, sedangkan suhu, jam operasi, dan sektor yang dialihkan bisa kosong — ini normal. Pada server fisik semua atribut ditampilkan.

31. Performa (CPU/RAM/Jaringan/Disk)

Halaman ini menampilkan riwayat beban server selama 24 jam terakhir — Load Average, pemakaian CPU dan waktu tunggu I/O, RAM/Swap, trafik jaringan (terima/kirim), I/O disk (baca/tulis), pengisian disk dan inode, deskriptor file yang terbuka serta koneksi MySQL, ditambah jumlah koneksi TCP dan proses saat ini. Jendela 24 jam adalah bawaannya: tombol di bagian atas mengubahnya menjadi 7 hari atau 30 hari.

Data dikumpulkan oleh cron/collect_metrics.php — setiap 5 menit menulis satu snapshot “mentah” dari penghitung (/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') ke tabel basis data system_metrics; persentase dan kecepatan dihitung sendiri oleh halaman berdasarkan selisih antara snapshot berdekatan (pengisian disk/inode/deskriptor/koneksi MySQL — nilai sesaat, tanpa perhitungan ulang). Sudo tidak diperlukan — sumber dibaca tanpa hak root.

Dua kedalaman penyimpanan. Cuplikan “mentah” lima menit bertahan satu hari (ditambah satu jam cadangan agar titik tertua pada grafik masih punya pendahulu untuk perhitungan). Setiap jam yang selesai dilipat menjadi satu baris; deret per jam itu disimpan 30 hari dan justru itulah yang dibaca tampilan “7 hari” dan “30 hari”. Dengan begitu riwayat sebulan hanya menelan sekitar 720 baris, bukan sekitar 8600.

Pada periode 7 dan 30 hari yang ditampilkan adalah rata-rata per jam, bukan pengukuran satu per satu — lonjakan lima menit menjadi halus pada skala itu, dan halaman tersebut memberi tahu hal ini. Untuk Load Average, baris per jam menyimpan rata-rata sekaligus puncak jam tersebut, sehingga grafiknya menampilkan dua garis. Tepat setelah pembaruan, periode panjang masih kosong (“belum ada riwayat untuk periode ini”) — deret per jam mulai terkumpul sejak saat itu.
# Baris cron (setiap 5 menit): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Pembungkus collect-metrics-all.sh (lihat ringkasan tugas cron) secara otomatis menemukan semua instans panel yang terpasang di server dan menjalankan cron/collect_metrics.php masing-masing atas nama pemilik situs.

Selama kolektor belum berjalan setidaknya dua kali (sekitar 10 menit pertama setelah pemasangan), halaman menampilkan “data sedang dikumpulkan” — grafik membutuhkan minimal satu pasang titik berdekatan untuk menghitung kecepatan dan persentase.

Proses teratas. Di bawah grafik ada dua tabel berisi lima proses yang paling banyak memakai CPU:

  • “Proses pada pengukuran terakhir” — apa yang membebani server pada saat cuplikan terakhir (waktunya tertera di judul kartu);
  • “Proses pada puncak hari ini” — lima proses yang sama, tetapi diambil pada pengukuran dengan Load Average tertinggi dalam 24 jam terakhir. Inilah jawaban atas pertanyaan “tadi malam itu apa”, ketika lonjakan pada grafik sudah lama berlalu.

Kedua tabel selalu menggambarkan 24 jam terakhir, bahkan saat grafik menampilkan satu bulan: cuplikan proses hanya dimiliki baris lima menit, dan baris itu tidak disimpan lebih dari sehari.

Proses milik pengukuran itu sendiri (ps, skrip PHP pengumpul, dan rantai cron/sudo di atasnya) dikeluarkan dari daftar — jika tidak, mereka akan menempati baris teratas pada setiap cuplikan: ps pada saat dijalankan menunjukkan CPU hampir 100 %. Tugas cron lain yang berjalan pada menit yang sama justru tampil, dan itulah gunanya tabel ini.

Peringatan beban (bagian “Pengaturan” → “Peringatan beban”) — saat ambang CPU/RAM/disk/inode terlampaui, panel mengirim pemberitahuan melalui semua saluran yang aktif (Telegram, Email, webhook, push — sama dengan laporan harian; untuk peringatan tidak perlu mengaktifkan apa pun secara terpisah), lalu satu lagi ketika metrik kembali normal. Selama ambang masih terlampaui, panel tidak mengirim berulang-ulang: pemberitahuan berikutnya baru datang setelah siklus “kembali normal → terlampaui lagi”.

Ambang batas diperiksa oleh collect_metrics.php yang sama pada setiap kali dijalankan (setiap 5 menit) — tidak perlu cron terpisah. Status “sudah diberi tahu / belum” disimpan di data/alerts_state.json, ambang batas — di pengaturan panel.

32. Peristiwa keamanan (sensor setiap 5 menit)

Laporan harian menjawab pertanyaan “bagaimana keadaan server”. Sensor menjawab pertanyaan lain — “apa yang baru saja terjadi”. Setiap 5 menit ia menanyai sumber-sumber cepat dan mencatat hanya yang berubah sejak pemeriksaan sebelumnya; halaman “Peristiwa keamanan” di menu menampilkan daftar temuan.

Pemeriksaan lambat — Lynis, debsums, pembaruan paket, masa berlaku SSL — tidak disentuh sensor: angkanya toh hanya berubah sekali sehari dan tetap menjadi urusan laporan harian.

Apa yang diperhatikannya — sembilan sumber:

  • Fail2ban — pemblokiran baru (daftar alamat yang diblokir dibandingkan, jadi pembukaan blokir bukan peristiwa, dan blokir yang berlaku dilaporkan tepat satu kali);
  • CrowdSec — keputusan baru;
  • Falco — peristiwa tingkat Emergency, Alert, dan Critical;
  • Suricata — peringatan baru; tanda [Priority: 1] menaikkan temuan menjadi kritis;
  • ClamAV — baris FOUND baru pada log pemindaian;
  • AIDE — perubahan berkas menurut pemeriksaan terakhir (jadi tidak lebih sering daripada pemeriksaan itu berjalan sesuai jadwalnya sendiri);
  • SSH — login berhasil sebagai root (kritis) dan login dari alamat yang belum pernah terlihat (peringatan);
  • Layanan perlindungan — UFW, Fail2ban, CrowdSec, Suricata, Falco: satu pesan saat layanan berhenti dan satu lagi saat berjalan kembali;
  • Port — port yang menjadi dapat dijangkau dari luar; jika di baliknya ada basis data atau cache, temuan itu kritis.

Perkakas yang tidak terpasang, atau yang lognya tidak dapat dibaca panel, sekadar diam — itu bukan kesalahan dan bukan peristiwa.

Pemeriksaan pertama tidak mengirim apa pun. Setiap sumber menyimpan tanda “sudah dibaca sampai mana” (berkas data/secwatch_state.json), dan jalannya yang pertama hanya menuliskan tanda itu. Kalau tidak, mengaktifkan pemantauan akan langsung mengirim pemberitahuan tentang segala hal yang terkumpul di log selama berbulan-bulan.

Pengaturan — “Pengaturan” → “Pemantauan peristiwa”:

  • Aktifkan pemantauan — selama masih mati, tugas cron berjalan sia-sia dan daftarnya tidak terisi;
  • Beri tahu mulai dari — “Peringatan” atau “Kritis”. Temuan di bawah ambang tetap masuk daftar, tetapi tidak masuk pemberitahuan;
  • Jeda, menit — jarak minimum antar pemberitahuan, bawaannya 15;
  • Jam tenang — jendela dalam format 23-7 saat peringatan tidak dikirim.
Temuan kritis langsung dikirim — tidak ditahan oleh jeda maupun jam tenang: justru untuk itulah sensor 5 menit ini ada. Meski begitu ada pengamannya: paling banyak 6 pemberitahuan per jam, untuk berjaga-jaga bila sebuah sumber mulai “berkedip”.

Pemberitahuan berjalan lewat saluran yang sama dengan laporan harian (Telegram, Email, webhook, push) — tidak perlu mengaktifkan apa pun secara terpisah. Semua temuan ditulis ke daftar terlepas dari apakah pemberitahuan terkirim atau tidak, dan disimpan selama 90 hari.

Daftar peristiwa. Penyaring: periode (24 jam, 7, 30, atau 90 hari), tingkat kepentingan, dan sumber; ditampilkan 500 catatan terakhir. Tombol “Periksa sekarang” menjalankan pemeriksaan yang persis sama dengan cron — praktis dipakai tepat setelah pengaturan, supaya tidak perlu menunggu lima menit; tombol itu juga menyebutkan alasannya bila tidak ada pemberitahuan yang terkirim (“di bawah ambang kepentingan”, “jam tenang”, “tidak ada saluran pemberitahuan yang dikonfigurasi”). Penghitung merah di samping butir menu adalah jumlah peristiwa kritis dalam 24 jam terakhir.

Kartu alamat. Mengeklik baris di daftar peristiwa atau di blok Alamat terkait membuka semua yang diketahui tentang satu alamat: kronologi peristiwanya, negaranya, dan di baris pertama sebuah kesimpulan. Baris daftar membuka kartu bila peristiwa itu menyangkut tepat satu alamat; bila alamatnya beberapa, yang menjadi tautan adalah alamat di dalam baris. Kesimpulannya ada empat:

  • Akses mungkin terjadi — ada login berhasil dari alamat itu dan alamat yang sama juga tertangkap pertahanan lain, atau tepat sesudahnya AIDE, ClamAV bekerja atau ada port baru terbuka. Satu-satunya kasus yang layak diperiksa segera;
  • Masuk dari alamat ini — ada login dan tidak ada yang lain. Biasanya itu pemilik dari tempat baru, karena itu kartunya tidak diwarnai merah;
  • Diblokir — alamat tertangkap dan diblokir: pertahanan sudah bekerja, tidak ada yang perlu dilakukan;
  • Perlu diperiksa — ada deteksi tetapi tidak ada pemblokiran. Jika alamat terus kembali, tutup secara manual.

Di bawah kronologi ada blok Apa yang terjadi di server pada waktu yang sama: peristiwa yang sama sekali tidak punya alamat. AIDE tidak tahu siapa yang mengubah berkas, ClamAV tidak tahu siapa yang membawa berkas terinfeksi. Yang ditampilkan adalah yang jatuh dalam rentang aktivitas alamat dan enam jam sesudahnya. Justru login bersama apa yang menyusul itulah yang memisahkan percobaan dari akses yang benar-benar terjadi.

Keterkaitan dibangun berdasarkan alamat. Penyerang yang berganti alamat memutusnya. Kartu menampilkan apa yang dilihat pertahanan, dan tidak mengambil tugas membedakan pembobolan dari login Anda sendiri dari tempat baru.

Jadwal. Tugas ini dimasukkan ke crontab root (sudo crontab -e), digeser 2 menit dari pengumpulan metrik agar keduanya tidak mulai bersamaan:

# setiap 5 menit — pemeriksaan peristiwa keamanan 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Jalankan pemeriksaan hanya melalui wrapper, jangan pernah langsung dengan php cron/security_watch.php. Wrapper menemukan semua pemasangan panel di server dan menjalankan pemeriksaan atas nama pemilik situs — dengan hak sudo yang persis sama seperti yang dipakai panel dari web. Pemanggilan langsung dari crontab root akan membuat berkas keadaan data/secwatch_state.json dimiliki root, dan tombol “Periksa sekarang” di panel akan berhenti bekerja.
Jika server disiapkan dengan pemasangan otomatis, tugas ini sudah ditambahkan — tinggal mengaktifkan pemantauan di pengaturan.

33. Server web: permintaan dan kode respons

Halaman “Server web” mengurai log akses dan menunjukkan berapa banyak permintaan yang masuk, berapa bagian yang berakhir dengan galat, dan siapa yang mengirimkannya. ModSecurity menjawab pertanyaan “apa yang diblokir”, halaman ini menjawab “apa saja yang sebenarnya sampai ke situs”.

Yang terlihat: jumlah permintaan, RPS, porsi galat, penghitung 5xx tersendiri, dan volume data yang dikirim; grafik permintaan per jam; sebaran kode 2xx/3xx/4xx/5xx; alamat yang sering muncul, IP yang sering muncul, galat yang sering terjadi (kode plus jalur), dan klien (User-Agent). Periodenya: 24 jam atau 7 hari.

Sumbernya adalah log akses berformat combined, jadi Nginx maupun Apache sama-sama cocok dan tidak perlu modul tambahan. Panel mencari log di antara berkas standar:

  • /var/log/nginx/access.log
  • /var/log/apache2/access.log
  • /var/log/apache2/other_vhosts_access.log
  • /var/log/httpd/access_log

Selain itu, panel memeriksa berkas *access*.log dan *requests*.log di /var/log/nginx/, /var/log/apache2/, dan /var/log/httpd/. Dengan begitu, log yang ditulis situs ke berkasnya sendiri lewat direktif CustomLog juga ditemukan — misalnya arciveo-monitor_requests.log yang dibuat oleh penginstal Arcivéo.

Hanya satu log yang dibaca: bila Nginx berada di depan Apache, menjumlahkan dua log akan menghitung setiap permintaan dua kali. Yang diambil adalah log pertama dari daftar di atas yang ditulisi dalam sehari terakhir (berkas yang ditemukan lewat pola nama dicoba sesudahnya, dari yang terbaru); jika tidak ada yang ditulisi selama sehari, diambil log tidak kosong yang paling baru. Karena itu, log yang dikosongkan logrotate atau tertinggal dari server web yang dimatikan tidak lagi menutupi log yang aktif. Yang dibaca adalah potongan terakhir log (3 MB) dan hasilnya disimpan dalam cache selama 5 menit — jadi pada situs yang ramai, periode yang dipilih bisa saja tidak tercakup seluruhnya; cakupan sebenarnya tertulis di atas grafik.

“Panel tidak dapat membaca log server web”. Penyebabnya hampir selalu hak akses: log dan direktorinya milik grup adm, sedangkan pengguna web tidak termasuk di dalamnya. Penginstal Arcivéo menambahkannya ke grup itu; setelah instalasi manual, masalahnya diatasi wrapper kecil milik root yang hanya boleh melakukan satu hal — menampilkan log (halaman ModSecurity bekerja dengan cara yang sama). Wrapper memilih log dengan aturan yang sama seperti panel. Perintahnya ditampilkan langsung di halaman itu; ini dia juga. Tempelkan seluruhnya — ini satu perintah:
sudo tee /usr/local/bin/monitor-weblog >/dev/null <<'EOF' && sudo chmod 755 /usr/local/bin/monitor-weblog && echo 'www-data ALL=(root) NOPASSWD: /usr/local/bin/monitor-weblog' | sudo tee /etc/sudoers.d/monitor-weblog >/dev/null && sudo visudo -c #!/bin/sh L="/var/log/nginx/access.log /var/log/apache2/access.log /var/log/apache2/other_vhosts_access.log /var/log/httpd/access_log $(ls -t /var/log/nginx/*access*.log /var/log/apache2/*access*.log /var/log/apache2/*requests*.log /var/log/httpd/*access_log 2>/dev/null)" for f in $L; do [ -r "$f" ] && [ -n "$(find "$f" -size +0 -mmin -1440 2>/dev/null)" ] && exec tail -c 3000000 "$f"; done for f in $(ls -t $L 2>/dev/null); do [ -r "$f" ] && [ -s "$f" ] && exec tail -c 3000000 "$f"; done EOF

Pengguna web bisa saja berbeda dari www-data — periksa dengan pengguna apa pool PHP-FPM situs berjalan (ps -o user= -C php-fpm) dan cantumkan pengguna itu pada baris sudoers. Jika nama log tidak mengandung access maupun requests, tambahkan jalurnya di awal baris L=… di dalam wrapper.

“Tidak ada catatan untuk periode yang dipilih” — log sudah dibaca, tetapi tidak ditemukan baris yang cocok: entah situs memang tidak dibuka, entah log baru saja dirotasi (logrotate) dan dimulai dari awal.

Percobaan eksploit yang dikenal. Panel mencocokkan permintaan di log dengan tanda tangan sekitar dua puluh eksploit umum: PHP-CGI, penelusuran direktori di Apache, PHPUnit, Log4Shell, serta celah router, kamera, dan gateway VPN. Untuk setiap percobaan, panel menyatakan apakah itu bisa berhasil di server ini berdasarkan fakta: versi Apache dan PHP yang terpasang, ada tidaknya Java, dan kode respons server atas permintaan berkas. Hanya yang benar-benar bisa berhasil yang ditandai merah.

Pengamanan server web. Panel meminta setiap situs di server melalui 127.0.0.1 dan memeriksa apa yang diterima pengunjung: apakah versi server web dan PHP terlihat, apakah metode TRACE aktif, dan apakah header pelindung Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy dan Content-Security-Policy ada. Situs diambil dari alamat tempat panel dibuka dan dari berkas host virtual, jika panel bisa membacanya. Hasilnya diperbarui setiap 10 menit.

Jika ada yang kurang, satu perintah muncul di bawah tabel. Perintah ini membuat berkas terpisah, zz-arciveo-hardening.conf, dan memuat ulang server web hanya setelah uji konfigurasi lolos; header yang sudah dikirim situs tidak digandakan. CSP tidak ditawarkan sebagai satu baris: kebijakan disusun untuk situs tertentu, kalau tidak situs berhenti memuat skrip, gaya, dan fonnya sendiri. Membatalkan perubahan di Apache:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
Tanda tangan di halaman galat (ServerSignature) tidak ditandai jika versi server web sudah tersembunyi: tanpa versi, tanda tangan hanya menampilkan nama server dan nama host.

34. Peta serangan (GeoIP)

Halaman “Peta serangan” menentukan negara berdasarkan IP dengan perintah geoiplookup. Tanpa paket GeoIP, negara tidak terdeteksi dan titik pada peta tidak muncul:

sudo apt install geoip-bin geoip-database # Pemeriksaan: geoiplookup 8.8.8.8
Sudo tidak diperlukan — basis data /usr/share/GeoIP/GeoIP.dat dapat dibaca semua, hasilnya di-cache di tmp/geoip_cache.json. Petanya sendiri (Leaflet + tile OpenStreetMap) dimuat di browser — perlu internet pada komputer tempat dashboard dibuka.

35. Eksposur eksternal, pembaruan, dan pembaruan otomatis

Dua kartu bawaan pada dasbor yang menampilkan bukan status “aktif/nonaktif” sebuah alat, melainkan tingkat keamanan server yang sebenarnya. Tidak perlu instalasi, dibaca secara lokal tanpa sudo.

Eksposur eksternal — berapa banyak layanan yang mendengarkan semua antarmuka (0.0.0.0/[::]) dan dapat diakses dari luar. Menyorot merah bila DBMS atau cache terbuka ke luar (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — ini celah langsung (−10 pada Skor Keamanan). Sumber: ss -tuln.

Jika kartu berwarna merah — tutup DBMS dari dunia luar: ikatkan ke 127.0.0.1 (bind-address pada konfigurasi MySQL/PostgreSQL, bind 127.0.0.1 pada Redis) atau tutup port di UFW.
“Port terbuka” ≠ “dapat diakses dari luar”. Layanan yang mendengarkan 127.0.0.1 (loopback) hanya terlihat oleh server itu sendiri — dari luar tidak dapat dijangkau, meski port “terbuka”. Karena itu Postfix pada port 25 yang terikat ke loopback tetap aman: penyetelan otomatis mengatur inet_interfaces = loopback-only (plus smtpd_banner netral — menutup temuan Lynis MAIL-8818 soal pengungkapan versi). Kartu “Eksposur eksternal” hanya menghitung yang mendengarkan 0.0.0.0/[::] sebagai terbuka ke luar; layanan loopback tidak termasuk.
Lynis MAIL-8818 secara manual (jika Anda memasang mail sendiri): pada /etc/postfix/main.cf atur smtpd_banner = $myhostname ESMTP (tanpa versi dan OS) dan inet_interfaces = loopback-only, lalu sudo systemctl restart postfix.

Pembaruan keamanan — berapa banyak patch keamanan yang menunggu dipasang dan apakah perlu boot ulang setelah pembaruan kernel (−5 pada Skor Keamanan bila ada patch). Sumber: /usr/lib/update-notifier/apt-check, berkas /var/run/reboot-required. Daftar rincinya ada di halaman “Pembaruan keamanan”.

# Pasang pembaruan: sudo apt update && sudo apt upgrade # Periksa apa yang mendengarkan ke luar: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Kartu pembaruan bekerja pada Ubuntu/Debian (update-notifier-common). Jika apt-check tidak ada — monitor menghitung patch melalui apt-get -s upgrade.

Pembaruan keamanan otomatis (unattended-upgrades) — pada halaman “Pembaruan keamanan” ada kartu terpisah yang menampilkan apakah pemasangan otomatis patch keamanan aktif dan kapan terakhir dijalankan. Sudo tidak diperlukan — statusnya dibaca melalui apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # aktifkan # Periksa apa yang aktif: apt-config dump | grep Unattended-Upgrade

Pemeliharaan

36. Pencadangan

Cadangan adalah asuransi utama: kehilangan data lebih parah daripada peretasan mana pun. Ada dua hal yang diperlukan — cadangan server/situs dan secara terpisah cadangan basis data panel (di sana ada pengguna, kunci WebAuthn, pengaturan, lisensi).

Opsi A — HestiaCP: tab Backup pada pengguna → tombol pembuatan cadangan (atau terjadwal di pengaturan server). Cadangan mencakup situs dan basis datanya.

Opsi B — manual (cron): dump basis data + arsip direktori data/ panel:

# cron root (sudo crontab -e) — cadangan harian pada 2:30 (ganti dengan nama/jalur Anda): 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 # Hapus arsip yang lebih lama dari 14 hari: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Cadangan di server yang sama menyelamatkan dari kesalahan, tetapi bukan dari kehilangan server. Salin arsip ke penyimpanan eksternal (server lain, S3, rclone ke cloud). Pastikan pemulihan benar-benar berfungsi.

37. Pembaruan dan pemindahan panel

Pembaruan ke versi baru. Buat cadangan terlebih dahulu. Lalu unggah ulang berkas kode, dengan menyimpan data Anda:

  • timpa (kode): public/, includes/, assets/, cron/, database/, serta .htaccess di root (front controller — perutean tidak boleh dibiarkan dari versi lama), manifest.json, sw.js;
  • jangan disentuh: config.php (data DB), data/ (laporan), logs/, tmp/ (sesi dan cache).
# Setelah pengunggahan — reset cache PHP (jika opcache aktif): sudo systemctl reload php*-fpm
FileZilla menampilkan SSH_FX_PERMISSION_DENIED — Permission denied. File panel dimiliki www-data (begitulah pengaturannya saat instalasi), sedangkan klien SFTP terhubung sebagai pengguna Anda sendiri yang tidak punya hak tulis. Memberikan seluruh panel ke www-data “agar berfungsi” justru itulah yang menyebabkan error ini; di bawah ada tiga cara, mana pun menyelesaikan masalahnya.
# Opsi A (disarankan) — pisahkan pemilik: kode milik Anda, folder kerja milik server web. # Server web sama sekali tidak mendapat hak tulis ke KODE panel: 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 # Opsi B — ACL di atas pemilik saat ini (tidak memindahkan apa pun): 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 # Opsi C — lewat grup www-data. Lebih sederhana, tetapi hak tulis ke file panel # juga didapat server web (dengan celah di PHP kode bisa diganti): 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
Mengapa opsi A aman. Panel hanya menulis ke tiga direktori — data/ (laporan), tmp/ (sesi dan cache), logs/; ketiganya tetap dimiliki www-data. Sisanya adalah kode, dan server web hanya perlu membacanya, yang diberikan oleh grup www-data dengan hak 644. Keuntungan sampingan: saat ada celah di PHP, file panel sudah tidak bisa ditimpa. Pada panel hosting (HestiaCP dan sejenisnya) opsi A tidak diperlukan: di sana file situs memang sudah dimiliki akun yang Anda gunakan untuk masuk lewat SFTP, dan server web membacanya lewat grup.
Jebakan opsi B: setiap chmod berikutnya pada file akan mereset masker ACL, dan akses hilang tanpa pemberitahuan. Jika setelah “merapikan hak akses” pengunggahan kembali terhenti oleh Permission denied — ulangi kedua perintah setfacl.
Bit 2 pada opsi C adalah setgid: file yang diunggah lewat SFTP tetap berada di grup www-data, jika tidak panel tak bisa menimpanya. Setelah opsi C, sambungkan ulang di FileZilla — grup baru hanya berlaku pada login baru. Pemeriksaan: id deploy (grup www-data harus muncul) dan ls -ld /path/to/monitor (drwxrwsr-x — huruf s berarti setgid sudah aktif).

Pemindahan ke server lain:

  1. Di server baru, siapkan situs + HTTPS (lihat halaman instalasi manual).
  2. Salin semua berkas panel bersama config.php, data/.
  3. Pindahkan DB: mysqldump di server lama → impor di server baru; sesuaikan data DB di config.php.
  4. Ulangi di server baru: sudoers, keanggotaan grup adm, tugas cron.
  5. Lisensi terikat ke domain — jika domain sama, kunci akan tetap berfungsi.

38. Situs kedua di server ini (satu domain lagi)

Panel sudah berjalan dan di server yang sama diperlukan satu situs lagi — dengan domain sendiri di samping monitor. Setelah penyiapan otomatis, server adalah Apache + PHP-FPM biasa, jadi situs memerlukan tiga hal: direktorinya sendiri, vhost-nya sendiri, dan sertifikatnya sendiri. Panel tidak disentuh: panel mengawasi seluruh server, salinan kedua untuk domain kedua tidak diperlukan.

Bagian ini untuk server tanpa panel hosting. Jika HestiaCP atau panel serupa sudah terpasang, tambahkan domain di sana, bukan secara manual. Dan jika Anda berencana meng-hosting banyak situs (ditambah surel), pasang panel sebelum penyiapan otomatis: skrip akan mendeteksinya dan beralih ke mode aditif, sedangkan panel yang dipasang belakangan akan membangun ulang konfigurasi Apache, firewall, dan surel.
Apa yang diganti dalam perintah. site2.example.com adalah alamat situs baru. Ini bisa berupa domain tersendiri (mycompany.com) maupun subdomain (shop.mycompany.com) — bagi Apache tidak ada bedanya, yang berbeda hanya rekaman DNS (langkah 1) dan penerbitan sertifikat (langkah 5). site2 hanyalah nama pendek situs: begitulah nama direktori /var/www/site2 dan berkas konfigurasi site2.conf. Nama itu tidak berkaitan dengan domain, Apache mengambil alamat hanya dari ServerName; namanya bebas — misalnya untuk shop.mycompany.com praktis memakai shop: direktori /var/www/shop, konfigurasi shop.conf.

1. DNS. Di panel DNS pada registrar domain Anda, buat rekaman A ke IP server ini:

# Subdomain (shop.mycompany.com): Tipe: A Nama: shop Nilai: 203.0.113.10 TTL: 3600 # Domain tersendiri (mycompany.com) — dua rekaman, agar alamat dengan www juga berfungsi: Tipe: A Nama: @ Nilai: 203.0.113.10 TTL: 3600 Tipe: A Nama: www Nilai: 203.0.113.10 TTL: 3600

Beberapa menit kemudian periksa: dig +short site2.example.com harus mengembalikan IP server.

2. Direktori situs.

sudo mkdir -p /var/www/site2 sudo chown -R www-data:www-data /var/www/site2

3. vhost. Blok ditempelkan ke terminal secara utuh — jalur ke soket PHP-FPM ditentukan sendiri:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # deteksi otomatis soket PHP-FPM sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # Domain tersendiri: hapus tanda # pada baris di bawah agar situs juga terbuka dengan www. # Untuk subdomain baris ini tidak diperlukan. #ServerAlias www.site2.example.com DocumentRoot /var/www/site2 <Directory /var/www/site2> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/site2_error.log CustomLog ${APACHE_LOG_DIR}/site2_access.log combined </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/site2.conf

4. Aktifkan situs dan periksa konfigurasi sebelum memuat ulang:

sudo a2ensite site2.conf sudo apache2ctl configtest # diharapkan: Syntax OK sudo systemctl reload apache2

5. SSL. certbot sudah dipasang oleh penyiapan otomatis; pada tahap ini DNS harus sudah mengarah ke server:

# Subdomain — satu alamat: sudo certbot --apache -d site2.example.com # Domain tersendiri — sekaligus dengan www (kedua rekaman A dari langkah 1 harus mengarah ke # server, dan www harus ada di ServerAlias, jika tidak certbot tidak dapat memverifikasi alamat kedua): sudo certbot --apache -d site2.example.com -d www.site2.example.com

Domain ketiga dan seterusnya ditambahkan dengan cara yang sama — ulangi langkah 1–5 dengan mengganti domain dan direktori.

Siapa yang menjawab permintaan “asing”. Jika Host tidak cocok dengan satu pun ServerName (akses lewat IP atau domain orang lain yang diarahkan ke server Anda), Apache menyajikan situs aktif pertama secara alfabet dari /etc/apache2/sites-enabled/. Situs bawaan 000-default dinonaktifkan oleh penyiapan otomatis, sehingga peran “piket” itu tetap dipegang vhost panel (arciveo-monitor.conf) — hal ini tidak memengaruhi situs yang dibuka dengan namanya sendiri. Jika lewat IP harus terbuka situs lain, beri nama berkasnya agar lebih dulu secara alfabet (misalnya 000-site2.conf).
Perlindungan berlaku untuk seluruh server: UFW, Fail2ban, CrowdSec, ModSecurity, dan Suricata sudah berjalan dan menutupi situs baru secara otomatis, tidak perlu disetel terpisah. Porta 80/443 juga sudah terbuka. Situs memiliki lognya sendiri: site2_error.log dan site2_access.log di /var/log/apache2/.
Jangan letakkan direktori situs baru di dalam direktori panel — akar web panel diatur oleh .htaccess-nya sendiri (front controller), dan situs bersarang akan menyajikan sesuatu yang berbeda dari yang Anda harapkan. Simpan situs berdampingan: /var/www/monitor, /var/www/site2.

39. Pemulihan akses (kunci hilang, kata sandi, blokir IP)

Jika Anda tidak bisa masuk — semuanya bisa diperbaiki langsung di basis data dari server. Buka basis data (namanya — dari config.php):

sudo mysql MY_DB

Kunci WebAuthn hilang (faktor kedua gagal) — nonaktifkan 2FA, masuk dengan kata sandi, daftarkan kunci baru:

UPDATE users SET webauthn_enabled = 0;

Lupa kata sandi — tetapkan hash baru (buat di server lalu masukkan):

# Buat hash kata sandi baru: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # Di basis data (masukkan hash yang diperoleh): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Terkunci sendiri oleh filter IP — nonaktifkan pembatasan:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Akses ke basis data selalu tersedia: sudo mysql di server, atau phpMyAdmin / bagian basis data di panel hosting. Setelah pemulihan, aktifkan kembali WebAuthn dan filter IP.

40. Semua tugas cron dalam satu tempat

Ringkasan tugas ada di cron root server (ditambahkan lewat sudo crontab -e). Sisakan hanya baris untuk alat yang Anda gunakan; sesuaikan path dengan server Anda.

# Cron server monitor (root) — masukkan lewat: sudo crontab -e # 01:30 — pindai ClamAV pada path berisiko (web, home, temp) → kartu “File diperiksa” dan “Pemindaian terakhir” 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — cek integritas file AIDE (perlu --config eksplisit) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # saat start — pulihkan hak akses /var/lib/aide (file tmpfiles paket # aide-common.conf mengaturnya ke 0700, dan panel tak lagi melihat basis data) @reboot chmod 755 /var/lib/aide # 03:00 — audit keamanan Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — perbarui block-list ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # set ipsum saat start dinaikkan oleh service ipsum-load.service (SEBELUM firewall, jika tidak # UFW tak melihat set di before.rules) — bukan cron. Di sini hanya refresh harian di atas. # 06:00 — laporan Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # setiap 30 mnt — cek disk SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integritas paket debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — laporan terjadwal ke Email dan Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # setiap jam — perbarui daftar paket (untuk kartu “Pembaruan keamanan”) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # setiap 5 mnt — snapshot sumber daya (CPU/RAM/jaringan/disk) untuk halaman “Performa” */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # setiap 5 menit, digeser dari cuplikan di atas — sensor peristiwa keamanan: # pemblokiran baru, peringatan IDS, perlindungan yang berhenti, port yang terbuka. # Selama pemantauan belum diaktifkan di pengaturan, tugas ini tidak melakukan apa pun. 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # setiap 15 menit — ringkasan server ke akun, agar beberapa server terlihat dalam # satu daftar. Selama ringkasan belum diaktifkan, tugas ini tidak melakukan apa pun. 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Detail masing-masing ada di bagian terkait. Tugas backup (bagian sebelumnya) ditambahkan ke cron yang sama. Setelah pengeditan, periksa: sudo crontab -l dan pastikan layanan cron aktif.
Waktu cron = zona waktu server, bukan TIMEZONE dari config.php. Konstanta TIMEZONE hanya memengaruhi PHP (cara panel menampilkan tanggal), tetapi daemon cron menjalankan tugas berdasarkan waktu sistem OS. Jika zona server tidak sama dengan zona Anda, laporan “08:00” akan datang pada waktu yang salah. Contoh: server berada di zona lain (Asia/Dhaka, UTC+6), sedangkan Anda di Jakarta (UTC+7) → laporan “08:00” akan datang pukul 09:00 menurut waktu Anda. Periksa dan bila perlu sesuaikan zona sistem dengan zona Anda:
# Periksa zona server saat ini: timedatectl # Atur zona Anda (contoh) dan mulai ulang cron: sudo timedatectl set-timezone Asia/Jakarta sudo systemctl restart cron
Setelah itu baris 0 8 * * * akan berjalan pukul 08:00 waktu setempat. Jika tidak, Anda harus menggeser cron itu sendiri, tetapi saat pergantian ke waktu musim dingin/panas pergeseran akan melenceng lagi — karena itu lebih tepat mengatur zona sistem.
Skrip pembungkus siap pakai. Salinan kerjanya dan contoh crontab (crontab.txt) berada di folder system/ di samping proyek, di luar public_html. Ini bukan bagian dari situs — tidak perlu diunggah ke web-root; tempatkan di server sesuai path sistem (seperti pada crontab di atas):
  • lynis-scan.sh → /usr/local/bin/ (chmod +x) — menjalankan lynis audit system, selama pemindaian memasang flag /tmp/lynis-running dan menyalin lynis-report.dat ke data/lynis/ panel;
  • logwatch_daily.sh → /usr/local/bin/ (chmod +x) — membuat laporan harian Logwatch (sshd, fail2ban, sudo, postfix) di data/logwatch/;
  • smart-scan.sh → /usr/local/bin/ (chmod +x) — mengambil status disk (smartctl) di data/disk/;
  • debsums-scan.sh → /usr/local/bin/ (chmod +x) — memeriksa integritas paket (debsums) di data/debsums/;
  • clamav-scan.sh → /usr/local/bin/ (chmod +x) — pemindaian antivirus ClamAV pada path berisiko (web, home, temp); menulis ringkasan ke /var/log/clamav/scan.log, tempat halaman ClamAV membacanya (baris 01:30 pada crontab di atas);
  • load-ipsum.sh → /usr/local/bin/ (chmod +x) — memperbarui set ipset ipsum (level 1) di tempat, tanpa memutus aturan firewall yang aktif (baris 04:00 pada crontab di atas);
  • daily-report-all.sh → /usr/local/bin/ (chmod +x) — menjalankan laporan cron/daily_report.php panel (baris 08:00 pada crontab di atas);
  • daily_report.php — sudah termasuk dalam panel (cron/daily_report.php), dijalankan lewat daily-report-all.sh, tidak perlu dipasang terpisah;
  • collect-metrics-all.sh → /usr/local/bin/ (chmod +x) — menjalankan cron/collect_metrics.php panel (halaman “Performa”, baris */5 pada crontab di atas); collect_metrics.php sudah termasuk dalam panel, tidak perlu dipasang terpisah;
  • security-watch-all.sh → /usr/local/bin/ (chmod +x) — menjalankan cron/security_watch.php milik panel atas nama pemilik situs (halaman “Peristiwa keamanan”, baris 2-57/5 pada crontab di atas); security_watch.php sudah termasuk dalam panel dan tidak perlu dipasang terpisah;
  • fleet-push-all.sh → /usr/local/bin/ (chmod +x) — mengirim ringkasan server ke akun melalui cron/fleet_push.php milik panel (baris 3,18,33,48 di atas);
  • crontab.txt (system/cron/) — contoh tugas; masukkan baris yang diperlukan lewat sudo crontab -e.
Path skrip di crontab harus sama dengan tempat Anda meletakkannya.
Cara meletakkan skrip di /usr/local/bin/. Langsung dari FileZilla tak bisa menulis ke sana — direktori itu dimiliki root, dan klien SFTP akan mendapatkan SSH_FX_PERMISSION_DENIED. Urutannya begini: pertama unggah file ke /tmp (semua bisa menulis di sana), lalu pindahkan ke tempatnya dengan satu perintah:
# di FileZilla: pada kolom “Situs jarak jauh” masukkan /tmp dan unggah skrip ke sana, # lalu lewat SSH (install langsung mengatur pemilik dan hak akses, chown/chmod tak perlu): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # pemeriksaan: file di tempatnya, hak akses rwxr-xr-x, sintaks utuh bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Jangan tertukar direktori: yang diperlukan adalah /tmp di root server — bukan /var/tmp dan bukan tmp/ di dalam panel itu sendiri (yang terakhir dimiliki www-data dan tertutup dari pengguna Anda). Di pohon FileZilla /tmp adalah cabang tingkat atas, di sebelah var, bukan di dalamnya.
Memasang server dengan konfigurasi otomatis? Pembungkus ini dan tugas cron-nya sudah terpasang oleh skrip (di /usr/local/bin/, log — /var/log/arciveo-cron.log) — tidak perlu melakukan apa pun secara manual.
Tempat skrip mencari panel. Pembungkusnya bersifat domain-netral: menemukan instalasi panel dengan menelusuri /home/*/web/*/public_html dan /var/www/*, lalu meletakkan laporan di data/ masing-masing. Jika panel berada di path lain — tambahkan path itu ke baris for app in … di dalam skrip, jika tidak laporan Lynis/SMART/debsums/Logwatch tak akan sampai ke panel.
cron.log dan hak akses. File logs/cron.log pertama kali dibuat oleh cron root — file itu akan dimiliki root, dan tab “Log cron” di panel tak dapat membacanya maupun mengosongkannya. Buat file itu terlebih dahulu atas nama pengguna web (pemilik direktori situs; pada HestiaCP ini akun, misalnya admin) — dengan begitu cron root hanya menambahkan, tanpa mengubah pemilik:
# buat terlebih dahulu atas nama pengguna web (sebelum menambahkan baris cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # jika cron.log sudah dibuat oleh cron root — serahkan ke pengguna web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Cari tahu pemilik direktori: stat -c %U /path/to/monitor.
Pengelolaan dari panel. Di bagian “Sistem” ada halaman “Crontab” — Anda dapat melihat dan menambahkan tugas tanpa SSH. Panel hanya mengedit tugas yang ditambahkan lewat panel itu sendiri (blok terpisah di root-crontab, ditandai dengan komentar khusus); semua yang sudah ada di crontab (daftar di atas) ditampilkan di sana sebagai daftar read-only “Tugas server lainnya” dengan tombol “Salin ke editor” — tombol itu hanya memindahkan jadwal/perintah ke formulir penambahan, tanpa menyentuh baris aslinya. Untuk “memindahkan” tugas yang ada ke bawah kendali panel — salin ke editor, simpan, lalu hapus baris lama secara manual (sudo crontab -e), jika tidak tugas itu akan berjalan dua kali.
Pengaturan sekali di server. Halaman ini memerlukan skrip pembungkus berhak istimewa — bukan sudo crontab mentah (itu akan menjadi eskalasi langsung ke root bagi siapa pun yang mengakses sesi panel), melainkan skrip terbatas dengan dua perintah (list/set) yang hanya menyentuh bloknya sendiri di antara komentar khusus. Pasang sekali:
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
Pengguna web bisa berbeda dari www-data — periksa di bawah pengguna mana pool PHP-FPM situs berjalan (ps -o user= -C php-fpm), dan masukkan pengguna itu ke baris sudoers.
File baru terunggah dengan pemilik yang salah — halaman menjawab “Access denied.”. Jika file public/crontab_monitor.php diunggah lewat FTP/SFTP di bawah pengguna sistem yang berbeda (misalnya root) dari file situs lainnya, web-server tak akan dapat membacanya. Bandingkan pemilik dan hak akses dengan file di sebelahnya lalu samakan:
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

Diagnostik

41. Alat sudah terpasang, tapi tertulis “Belum terpasang”

Monitor mendeteksi keberadaan alat melalui dpkg-query — basis data paket APT. Jika alat tidak dipasang melalui apt (secara manual, dari snap, atau dari sumber), dpkg tidak dapat melihatnya.

# Periksa melalui dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Cari jalur ke biner: which ufw fail2ban-client auditctl # Uji sudo dari www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. Mengatasi masalah (500, tidak ada data)

Error 500 — periksa log PHP, nginx, dan monitor itu sendiri:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Log monitor: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Hak akses folder: ls -la data/ tmp/ logs/
Dashboard di panel hosting (HestiaCP, ISPmanager, cPanel)? Di sana PHP berjalan bukan sebagai www-data, melainkan sebagai akun pengguna (misalnya admin — pemilik direktori situs). Semua aturan sudo dan keanggotaan grup (adm, systemd-journal) harus didaftarkan ke pengguna itu, jika tidak modul akan menampilkan “Tidak aktif / 0” meski layanan berjalan. Untuk mengetahui pengguna PHP yang sebenarnya: ps -o user= -C php-fpm | sort -u atau pemilik direktori situs stat -c '%U' /path/to/monitor. Selanjutnya, pada semua perintah di bawah gantilah www-data dengannya. Instalasi otomatis mendeteksi pengguna web sendiri dan mendaftarkan sudoers ke pengguna tersebut.

Data tidak muncul — hampir selalu karena hak sudo belum diatur. Periksa perintah tertentu sebagai pengguna web (ganti www-data dengan milik Anda). Flag -n = tanpa kata sandi, seperti PHP — jika diminta kata sandi, berarti aturannya belum ada di 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
Modul menampilkan “Tidak aktif” / “0”, padahal alatnya berjalan (misalnya sudo aa-status di terminal menampilkan profil, tetapi halaman “AppArmor” menampilkan “Tidak aktif”). Penyebabnya: pengguna web tidak punya hak sudo untuk perintah modul tersebut. Periksa perintahnya dari daftar di atas: jika diminta kata sandi — tambahkan baris yang hilang ke /etc/sudoers.d/monitor (“Pengaturan sudo”). Perintah “baru” yang umum: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Jika halaman tertentu (Falco, ModSecurity, Auditd, port UFW yang terbuka) kosong — cocokkan dengan daftar di bagian tentang sudo: kemungkinan apache2ctl, ausearch, aa-status, atau ss belum diizinkan, atau pengguna web tidak ada di grup adm/systemd-journal (dari situ log fail2ban/auth/modsec dan journalctl dibaca — Falco dan event kernel).

43. Halaman kosong padahal data ada di server

Gejala: data ada di server (terlihat lewat shell), tetapi halaman menampilkan “tidak ada data” atau status yang salah — misalnya AIDE menulis “Belum diinisialisasi” padahal basisnya sudah dibuat.

Penyebabnya adalah open_basedir: banyak panel dan hosting membatasi pool PHP-FPM ke direktori domain, sehingga fungsi PHP file_exists(), file_get_contents(), filemtime() pada path sistem (/var/lib/aide, /var/log, /proc…) diblokir. Monitor menyiasatinya dengan membaca path tersebut lewat perintah sistem standar (cat, test, stat).

# Apakah file terlihat lewat shell (begitulah monitor membacanya): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Nilai open_basedir saat ini untuk pool domain: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Jika shell “melihat” file (VISIBLE) tetapi halaman tidak — ini open_basedir. Solusi yang tepat adalah pembacaan lewat perintah sistem (sudah diterapkan untuk AIDE dan Monitor Jaringan). Memperluas open_basedir ke /var, /proc tidak perlu dan kurang aman.

44. Halaman SSL tidak berfungsi

Monitor memeriksa sertifikat dengan terhubung langsung ke domain melalui port 443. Jika domain tidak dapat diakses dari server itu sendiri atau port ditutup oleh firewall, pemeriksaan akan gagal.

# Periksa sertifikat secara manual: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Periksa ketersediaan: curl -I https://monitor.example.com
Domain diambil oleh monitor secara otomatis dari konfigurasi nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) dan Apache (/etc/apache2/sites-enabled/) ditambah host saat ini dari HTTP_HOST.
Deteksi otomatis subdomain. Subdomain dideteksi secara otomatis dari log publik Certificate Transparency dan diperiksa melalui jaringan — bahkan jika ditempatkan di server lain. Tidak perlu menambahkan apa pun secara manual.

45. Hanya satu dari beberapa basis data yang terlihat

Monitor terhubung ke MySQL sebagai pengguna dari config.php yang hanya punya akses ke basis datanya sendiri. MySQL hanya menampilkan basis data yang punya hak akses di information_schema — karena itu yang lain tidak terlihat.

Agar monitor dapat melihat semua basis data, berikan pengguna ini hak baca saja (sekali sebagai root; ganti dengan nama pengguna dari config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* hanya memberi hak baca — tidak bisa mengubah, menghapus, atau membuat apa pun, jadi aman untuk pemantauan.
Tanpa GRANT ini, panel hanya melihat basis datanya sendiri — ini bukan kesalahan, melainkan batasan hak akses. Panel tidak menggunakan sudo mysql sama sekali: daftar basis data diambil melalui koneksi PDO-nya sendiri.

46. PostgreSQL tidak muncul di halaman “Basis data”

PostgreSQL memerlukan akses pada tingkat pengguna postgres, yang tidak dimiliki oleh pengguna web panel. Menjalankan sudo psql yang luas dari PHP tidak aman — sebagai gantinya panel memanggil pembungkus sempit tanpa parameter yang hanya mencetak versi, jumlah koneksi, dan daftar basis data beserta ukurannya. Buat pembungkus tersebut:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Security 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 # di /etc/sudoers.d/monitor (pengguna = yang menjalankan PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Jika tidak menggunakan PostgreSQL — hapus baris monitor-pgstat dari sudoers (langkah 13 pemasangan manual) dan jangan buat skripnya: kartu PostgreSQL hanya akan tetap tidak aktif.

47. Alert terpicu — apa yang harus dilakukan

Dashboard menunjukkan apa yang terjadi; di bawah ini — apa yang harus dilakukan pada situasi umum. Prinsip utama: jangan panik, cocokkan dengan aktivitas sah (tindakan Anda, pembaruan, cadangan), dan tanggapi sesuai tingkat keseriusannya.

  • Peta serangan / banyak ban fail2ban — ini normal untuk server mana pun di internet (bot terus-menerus mencoba SSH/web). Yang penting, ban berfungsi. Pastikan login SSH — hanya lewat kunci (kata sandi dinonaktifkan), dan IP Anda ada di ignoreip.
  • ModSecurity memblokir permintaan — WAF menangkis serangan ke situs, itu memang tugasnya. Jika trafik sah Anda ikut terblokir (false positive) — cari rule id di detailnya dan tambahkan pengecualian ke konfig CRS.
  • AIDE: file berubah — cocokkan daftarnya dengan apa yang Anda lakukan (pembaruan paket, penyuntingan konfig — normal). Perubahan biner sistem yang tidak Anda sentuh — patut diwaspadai. Setelah perubahan yang sah, perbarui basis data AIDE.
  • debsums: biner/pustaka berubah (di luar /etc, di luar /usr/share) — berpotensi disusupi. Cocokkan paketnya: debsums PACKAGE_NAME, jika ragu pasang ulang (apt install --reinstall).
  • ClamAV / maldet: ditemukan ancaman — periksa file di karantina, jangan membukanya. Jika ini web shell di direktori situs — isolasi server dan cari titik masuknya (plugin rentan, kebocoran akses).
  • Falco: peristiwa kritis (menjalankan shell dalam kontainer, akses ke file sensitif) — telaah peristiwanya: proses milik siapa, apa yang menjalankannya. Sering kali ini aktivitas admin yang sah.
  • Paparan eksternal: DBMS/cache merah — segera tutup: ikat layanan ke 127.0.0.1 atau tutup port di UFW. Ini celah nyata.
  • SSL akan/telah kedaluwarsa — perpanjang sertifikat (Let's Encrypt memperpanjang otomatis; jika tidak — periksa certbot renew atau pengaturan di dashboard).
  • Pembaruan keamanan menunggu — pasang: sudo apt update && sudo apt upgrade; setelah pembaruan kernel, boot ulang server.
Tanda peretasan nyata (proses/pengguna tak dikenal, biner berubah, spam keluar, tugas cron tak dikenal): putuskan server dari akses eksternal, ambil cadangan untuk analisis, dan jika datanya kritis, siapkan server bersih dari cadangan tepercaya — membersihkan rootkit secara andal itu sulit.