Vai al contenuto
EuroVPSEuroVPS

Importanti file di log Linux da monitorare su RHEL 9 e 10

Una guida pratica ai file di log che contano su Red Hat Enterprise Linux 9 e 10 e sui sistemi compatibili (Oracle Linux, AlmaLinux, Rocky Linux): dove si trova ciascuno, cosa vi dice, i comandi per leggerlo e come conservarlo e centralizzarlo.

Di EuroVPS Engineering

Aggiornato il 22 ottobre 202611 min di lettura
Immagine di copertina: file di log Linux

Ogni problema su un server Linux lascia una traccia da qualche parte: un accesso fallito, un servizio che non è partito, un aggiornamento di pacchetti che ha cambiato una libreria, un disco che ha iniziato a dare errori. Sapere quale log contiene quale traccia è la differenza tra una diagnosi di cinque minuti e un pomeriggio passato a tirare a indovinare. Questa guida copre i log che contano su Red Hat Enterprise Linux 9 e 10, e sui sistemi compatibili come Oracle Linux, AlmaLinux e Rocky Linux, con i comandi che usiamo per leggerli.

Due posti in cui vivono i log su RHEL: il journal e /var/log

Su RHEL 9 e 10 quasi tutto viene raccolto prima da systemd-journald. Il journal è un archivio strutturato e indicizzato che si legge con journalctl. In un'installazione standard, rsyslog legge poi il journal e scrive i familiari file di testo in /var/log: messages, secure, cron, maillog e boot.log. Alcuni servizi tengono comunque i propri file, in particolare il demone di audit, i web server, i database e dnf.

Verificate che rsyslog sia presente e in esecuzione prima di fare affidamento sui file di testo. Le immagini minimali e quelle per container potrebbero non includerlo:

rpm -q rsyslog
systemctl status rsyslog
# if it is missing:
dnf install rsyslog
systemctl enable --now rsyslog

La maggior parte dei file in /var/log è leggibile solo da root, quindi eseguite i comandi che seguono come root o con sudo.

Il journal di systemd

Il journal è il primo posto in cui guardare, perché contiene l'output di ogni unit di systemd, del kernel e di tutto ciò che logga tramite syslog, con timestamp precisi e metadati su cui potete filtrare.

Comandi che vale la pena memorizzare

# everything from one service, newest last
journalctl -u nginx

# follow a service live, like tail -f
journalctl -u sshd -f

# only errors and worse, from the current boot
journalctl -p err -b

# warnings and worse from the previous boot (useful after a crash)
journalctl -p warning -b -1

# a time window
journalctl --since "1 hour ago"
journalctl -u php-fpm --since "2026-10-22 08:00" --until "2026-10-22 09:00"

# kernel messages only (the replacement for reading a dmesg file)
journalctl -k -b

# search message text
journalctl -u sshd -g "Failed password"

# list the boots the journal knows about
journalctl --list-boots

Si possono combinare più opzioni -u, cosa utile quando una richiesta attraversa più di un servizio: journalctl -u nginx -u php-fpm --since "10 min ago".

Rendete il journal persistente

In un'installazione RHEL predefinita il journal è conservato in /run/log/journal, in memoria, e sparisce dopo un riavvio. Che è esattamente il momento in cui ne avete più bisogno. Per conservarlo su disco, create un drop-in e riavviate journald:

mkdir -p /etc/systemd/journald.conf.d
vi /etc/systemd/journald.conf.d/persistent.conf
[Journal]
Storage=persistent
SystemMaxUse=2G
systemctl restart systemd-journald
journalctl --flush
ls -ld /var/log/journal
journalctl --disk-usage

SystemMaxUse limita lo spazio che il journal può usare; scegliete un valore adatto al disco. Dopo il riavvio successivo, journalctl --list-boots mostra più di una voce e journalctl -b -1 funziona.

/var/log/messages: il log generale di sistema

È il file che raccoglie tutto: avvii e arresti dei servizi, messaggi del kernel, avvisi hardware e dei driver, cambiamenti di rete e qualsiasi altra cosa loggata a livello info o superiore che non appartenga alle categorie posta, autenticazione o cron. È l'equivalente testuale di una larga parte del journal, ed è comodo per grep e per gli strumenti che si aspettano file semplici.

tail -f /var/log/messages
grep -iE "error|fail|oom" /var/log/messages | tail -50
grep -i "out of memory" /var/log/messages

Una riga come Out of memory: Killed process significa che il kernel ha terminato un processo perché il server ha esaurito la RAM. La risposta è dare più memoria al carico di lavoro o ridurre ciò che consuma (meno worker PHP-FPM o di database, una cache, la correzione di un leak), non nascondere il sintomo.

/var/log/secure: il log di sicurezza Linux per autenticazione e privilegi

Ogni accesso SSH, ogni password sbagliata, ogni sudo e su, e i messaggi PAM finiscono qui. Su RHEL, Oracle Linux, AlmaLinux e Rocky Linux questo è il file da tenere d'occhio per gli accessi al server.

# failed SSH passwords and the addresses they came from
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head

# successful logins
grep "Accepted" /var/log/secure

# who used sudo, and for what
grep "COMMAND=" /var/log/secure

Due strumenti collegati completano il quadro. last elenca gli accessi riusciti da /var/log/wtmp, e lastb quelli falliti da /var/log/btmp. Se gli account si bloccano dopo tentativi falliti, faillock --user alice mostra i tentativi e faillock --user alice --reset li azzera.

/var/log/audit/audit.log: la traccia di audit

Il sistema di audit di Linux registra a livello kernel gli eventi rilevanti per la sicurezza: accessi, cambi di privilegio, decisioni SELinux e qualsiasi file o chiamata di sistema scegliate di sorvegliare. Viene scritto da auditd e letto con ausearch e aureport anziché con grep, perché quegli strumenti ne capiscono il formato.

# summary of everything in the log
aureport --summary

# failed logins
aureport --login --failed

# authentication attempts
aureport -au

# today's events, with numeric IDs turned into names
ausearch -ts today -i

# failed login events only
ausearch -m USER_LOGIN -sv no -i

Sorvegliate i file che contano

Le regole di audit permettono di registrare ogni modifica a un file sensibile. Mettetele in un file sotto /etc/audit/rules.d/, per esempio /etc/audit/rules.d/50-watch.rules:

-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
augenrules --load
auditctl -l
ausearch -k sshd_config -i

L'ultimo comando mostra chi ha cambiato la configurazione SSH, quando, e con quale programma.

I denial di SELinux

RHEL gira con SELinux in modalità enforcing, e dovrebbe restare così. Quando SELinux blocca qualcosa, il denial viene registrato come record AVC nel log di audit. Guardate lì prima di cambiare qualsiasi altra cosa:

getenforce
ausearch -m AVC,USER_AVC -ts recent -i
ausearch -m AVC -ts today | audit2why

Per una spiegazione in linguaggio semplice con correzioni suggerite, installate setroubleshoot ed eseguite sealert sul log:

dnf install setroubleshoot-server
sealert -a /var/log/audit/audit.log

La maggior parte dei denial ha una causa semplice: un file con l'etichetta sbagliata (si corregge con restorecon -Rv <path>), contenuto messo in una directory non standard (etichettatela con semanage fcontext), oppure una funzione che richiede un boolean attivo (getsebool -a | grep httpd, poi setsebool -P). Esaurite queste strade prima di pensare a un modulo di policy personalizzato.

/var/log/cron: i job pianificati

Ogni job avviato da crond e anacron viene loggato qui, con l'utente e il comando. Quando un backup o un report «non è partito», questo file vi dice se è stato avviato o no.

grep CROND /var/log/cron | tail -20
grep "(root) CMD" /var/log/cron | tail -20

Molte attività di manutenzione di RHEL girano ormai come timer di systemd anziché come job cron. Elencateli con systemctl list-timers e leggete il loro output con journalctl -u <name>.service, per esempio journalctl -u logrotate.service.

/var/log/maillog: la consegna della posta

Postfix e gli altri software di posta loggano qui ogni messaggio che accettano, inoltrano, rinviano o respingono. Se un'applicazione web dice di aver inviato un'e-mail e nessuno l'ha ricevuta, la risposta di solito è in questo file.

# the full path of one message, by recipient
grep "user@example.org" /var/log/maillog

# bounces and deferrals
grep -E "status=(bounced|deferred)" /var/log/maillog | tail -20

# what is still waiting in the queue
postqueue -p

Ogni messaggio Postfix ha un ID di coda (per esempio 4F2A81C0B3E). Cercate quell'ID con grep per vedere ogni passo della sua consegna, inclusa la risposta del server ricevente.

/var/log/dnf.log e la history di dnf: cosa è cambiato

Quando qualcosa si rompe dopo una manutenzione, la prima domanda è cosa sia cambiato. Su RHEL 9 e 10 le operazioni sui pacchetti sono gestite da dnf, che scrive /var/log/dnf.log (l'esecuzione di dnf stessa), /var/log/dnf.rpm.log (ogni pacchetto installato, aggiornato o rimosso) e /var/log/dnf.librepo.log (i download dai repository). La vista più rapida è il database della history:

# every transaction, newest first
dnf history

# exactly what one transaction did
dnf history info last
dnf history info 42

# every transaction that touched a package
dnf history list nginx

# packages changed recently, from the log file
grep -E "Install|Upgrade|Erase" /var/log/dnf.rpm.log | tail -30

# does anything need a restart after updates (dnf-plugins-core)
dnf needs-restarting -r

Se un aggiornamento va annullato, dnf history undo <id> riporta indietro una singola transazione, a patto che i pacchetti precedenti siano ancora disponibili nei repository.

/var/log/boot.log e il journal del boot

/var/log/boot.log contiene i messaggi di stato dell'avvio che vedreste sulla console. È utile per un'occhiata rapida, ma il registro completo di un boot è nel journal:

cat /var/log/boot.log
journalctl -b -p err
journalctl -b -1 -p err      # previous boot, needs the persistent journal
systemctl --failed           # units that did not start
systemd-analyze blame | head # what took longest

I log del web server e di PHP

Ogni web server tiene i propri log di accesso e di errore:

  • nginx: /var/log/nginx/access.log e /var/log/nginx/error.log, più eventuali file per sito che definite con access_log ed error_log.
  • Apache httpd: /var/log/httpd/access_log, /var/log/httpd/error_log, e ssl_access_log e ssl_error_log quando è installato mod_ssl.
  • PHP-FPM: /var/log/php-fpm/error.log per il master FPM e /var/log/php-fpm/www-error.log per gli errori PHP del pool predefinito. Il pool può anche scrivere uno slow log con lo stack trace di ogni richiesta lenta, una volta impostato request_slowlog_timeout.
# top client addresses (default nginx log format)
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

# count of server errors (5xx) by URL
awk '$9 ~ /^5/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

# the latest errors
tail -50 /var/log/nginx/error.log
tail -50 /var/log/php-fpm/www-error.log

Una raffica di risposte 502 o 504 nel log di accesso, accompagnata da «upstream timed out» o «connect() failed» nel log di errore di nginx, punta a PHP-FPM: o tutti i worker erano occupati, o uno script è durato troppo.

I log dei database

  • MariaDB (il database nei repository RHEL): /var/log/mariadb/mariadb.log, impostato da log-error in /etc/my.cnf.d/mariadb-server.cnf.
  • MySQL dai repository RHEL: /var/log/mysql/mysqld.log.
  • PostgreSQL: /var/lib/pgsql/data/log/, un file per giorno della settimana come impostazione predefinita.

Il log degli errori mostra problemi di avvio, crash, tabelle corrotte ed errori di replica. Per le prestazioni, abilitate lo slow query log. Per MariaDB, aggiungete alla sezione [mysqld] di /etc/my.cnf.d/mariadb-server.cnf e riavviate:

slow_query_log = 1
slow_query_log_file = /var/log/mariadb/mariadb-slow.log
long_query_time = 1

Poi riassumetelo con mysqldumpslow -s t /var/log/mariadb/mariadb-slow.log | head -40 per vedere quali query costano più tempo complessivo.

Tenere i log sotto controllo con logrotate

RHEL ruota i file di log con logrotate, eseguito una volta al giorno da un timer di systemd (logrotate.timer). Le impostazioni globali sono in /etc/logrotate.conf (settimanale, quattro rotazioni, nomi di file datati come messages-20261018), e ogni pacchetto aggiunge le proprie regole sotto /etc/logrotate.d/. I file di sistema (messages, secure, cron, maillog) sono coperti da /etc/logrotate.d/rsyslog.

systemctl list-timers logrotate.timer
cat /etc/logrotate.d/nginx

# dry run: show what would happen, change nothing
logrotate -d /etc/logrotate.d/nginx

# force a rotation now, verbosely
logrotate -vf /etc/logrotate.d/nginx

Per la vostra applicazione, aggiungete un file come /etc/logrotate.d/myapp:

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp myapp
    postrotate
        /usr/bin/systemctl reload myapp.service >/dev/null 2>&1 || true
    endscript
}

Tenete i log applicativi sotto /var/log: i file lì ricevono l'etichetta SELinux che logrotate è autorizzato a gestire. Se la directory dei log è scrivibile da un utente diverso da root, aggiungete una riga su myapp myapp così che logrotate possa ruotarla in sicurezza.

Il journal si ruota da solo entro il limite SystemMaxUse. Per ridurlo a mano, usate journalctl --vacuum-time=30d oppure journalctl --vacuum-size=1G.

Centralizzare i log con rsyslog

I log che vivono solo sul server che descrivono hanno due debolezze: dovete accedere a ogni macchina per leggerli, e un attaccante che ottiene root può modificarli. Inoltrare i log di ogni server a un collettore centrale risolve entrambe, e vi permette di cercare in un solo posto durante un incidente. Il collettore può essere un altro server RHEL con rsyslog, oppure una piattaforma di log come Graylog.

Sul collettore

Create /etc/rsyslog.d/10-collector.conf. Ascolta sulla porta TCP 20514, che la policy SELinux etichetta già come porta syslog (verificate con semanage port -l | grep syslogd), e scrive un file per host e per programma:

module(load="imtcp")

template(name="RemotePerHost" type="string"
         string="/var/log/remote/%HOSTNAME:::secpath-replace%/%PROGRAMNAME:::secpath-replace%.log")

ruleset(name="remote") {
    action(type="omfile" dynaFile="RemotePerHost")
}

input(type="imtcp" port="20514" ruleset="remote")

Aprite la porta solo ai vostri server, qui attraverso la zona internal di firewalld, poi validate e riavviate:

firewall-cmd --permanent --zone=internal --add-source=10.0.10.0/24
firewall-cmd --permanent --zone=internal --add-port=20514/tcp
firewall-cmd --reload
rsyslogd -N1
systemctl restart rsyslog
ss -tlnp | grep 20514

Se scegliete una porta non ancora etichettata, aggiungetela prima con semanage port -a -t syslogd_port_t -p tcp <port>, sul collettore e sui mittenti. Aggiungete una regola logrotate per /var/log/remote//.log come mostrato sopra.

Su ogni server

Create /etc/rsyslog.d/90-central.conf. TCP con una coda assistita da disco significa che un riavvio del collettore o un'interruzione di rete ritarda i messaggi anziché perderli:

*.* action(type="omfwd" target="logs.example.net" port="20514" protocol="tcp"
           template="RSYSLOG_SyslogProtocol23Format"
           queue.type="LinkedList" queue.filename="fwd_central"
           queue.maxDiskSpace="1g" queue.saveOnShutdown="on"
           action.resumeRetryCount="-1")
rsyslogd -N1
systemctl restart rsyslog

Per inviare anche gli eventi di audit, impostate active = yes in /etc/audit/plugins.d/syslog.conf e riavviate auditd con service auditd restart (auditd non accetta un riavvio tramite systemctl).

Dimostrate che arriva

Inviate un messaggio di prova e trovatelo sul collettore:

# on the server
logger -t logtest "central logging test from $(hostname)"

# on the collector
tail /var/log/remote/*/logtest.log

Ripetete la prova dopo ogni modifica al collettore o al firewall, e create un allarme sul silenzio: un server che ha smesso di inviare log è facile da non notare, perché nei log non c'è nulla che ve lo dica.

Una rapida checklist quotidiana

  • journalctl -p err -b: qualcosa di nuovo a livello error dal boot?
  • aureport --login --failed e lastb | head: accessi falliti insoliti?
  • ausearch -m AVC -ts today: denial SELinux?
  • dnf history: è cambiato qualcosa che non avevate pianificato?
  • tail /var/log/nginx/error.log e il log degli errori del database: problemi applicativi?
  • journalctl --disk-usage e df -h /var/log: c'è spazio per continuare a loggare?

Leggerli richiede pochi minuti. Automatizzarli, con un collettore centrale e allarmi sui pattern che contano, è ciò che trasforma i log da archivio in preallarme.

Gli ingegneri EuroVPS costruiscono, gestiscono e sorvegliano pipeline di log come questa per i clienti sui nostri server managed.

Dall'archivio del blog EuroVPS, pubblicato per la prima volta il 19 aprile 2020.

Tutte le note dal campo