Syslog-ng: log remoti e cifratura

Registrare le operazioni svolte sul server e' un'attivita' di importanza fondamentale, sia come fonte di informazioni utili per risolvere eventuali malfunzionamenti, che come forma di controllo del sistema, verificando che esso non stia subendo attacchi o tentativi di intrusione.
Un attacker abile certamente sara' in grado di occultare al meglio le proprie tracce, ad esempio cancellando i segni del propria operato dai file dove il log server registra tutte le attivita'. Emerge quindi la necessita' di proteggere questi file nel migliore modo possibile, e quindi non memorizzandoli esclusivamente sulla macchina che si desidera monitorare:  in caso di violazione della stessa essi sarebbero a piena disposizione dell'attacker.
Una prima possibilita', potrebbe essere quella di mantenere anche una copia cartacea dei log stessi. Certamente l'attacker, a meno di non avervi accesso fisicamente non potrebbe cancellare le sue tracce dalle stampe dei log. Questa scelta comporterebbe pero' un incremento mostruoso delle scartoffie, oltre che problemi pratici per la loro analisi ed archiviazione.
Scartando questa ipotesi, si potrebbe pensare all'utilizzo di un secondo log server, in modo da replicare anche in remoto i log che vengono memorizzati localmente. In un simile caso l'attacker dovrebbe cancellare le sue tracce in due posti diversi. Inoltre, presupponendo che il log server remoto preveda tutti i possibili accorgimenti ai fini della sicurezza del sistema, tra cui l'installazione solo del software strettamente necessario, non sarebbe cosi' semplice per l'attacker cancellare le proprie tracce anche da esso.
Si presume inoltre che tale log server non sia direttamente connesso ad internet, ma sia accessibile esclusivamente dalla rete interna, minimizzando quindi la sua esposizione.
Negli esempi riportati la macchina da monitorare e' l'host paranoid, mentre il log server remoto stargazer.
Su entrambe le macchine e' stato installato syslog-ng con il comando apt-get install syslog-ng .
Sull'host paranoid e' stato modificato il file /etc/syslog-ng/syslog-ng.conf, nel seguente modo:

- sources: definisce tutte le “sorgenti” dalle quali raccogliere i dati da registrare nei log. Una sorgente e' un driver che raccoglie i messaggi usando un metodo predefinito. Nel mio caso sono state definite le seguenti sorgenti per l'host paranoid:

source s_all
    {
    # messaggi generati da Syslog-NG
    internal();
    #standard Linux log source
    unix-stream("/dev/log" max-connections(10));
    # messaggi dal kernel
    file("/proc/kmsg" log_prefix("kernel: "));
    };

La prima sorgente internal() indica i messaggi generati dallo stesso syslog-ng, in modo da potere registrare anche avvertimenti ed errori relativi al log-server, come ad esempio il suo arresto o riavvio.
Unix-stream apre invece un socket unix sul quale ascoltare i messaggi generati dal sistema. Per evitare attacchi di tipo DoS viene imposto anche un limite al massimo numero di connessioni accettate con l'opzione max-connections. Il valore 10 e' piuttosto ristretto, ma comunque adatto alla mia situazione, trattandosi di un server con poco carico di lavoro. Nel caso di sistemi con maggiore occupazione potrebbe essereopportuno aumentare questo valore.
L'ultimo driver file() infine legge i messaggi che il kernel scrive in /proc/kmesg.

- destination: le destinazioni costituiscono i punti dove inviare e memorizzare i messaggi raccolti dalle sources se le regole di filtraggio vengono soddisfatte. Come per le sorgenti esistono diversi driver che definiscono come distribuire tali messaggi.

# destinazioni standard
destination df_auth { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/auth.log"); };
destination df_syslog { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/syslog"); };
destination df_cron { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/cron.log"); };
destination df_daemon { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/daemon.log"); };
destination df_kern { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/kern.log"); };
destination df_lpr { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/lpr.log"); };
destination df_mail { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/mail.log"); };
destination df_user { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/user.log"); };
destination df_uucp { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/uucp.log"); };
destination df_sshd { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/sshd.log"); };

# destinazioni per le varie facility e livelli severity.
# Usate solo alcune per mail.info, mail.crit e mail.warn
destination df_facility_dot_info { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/$FACILITY.info"); };
destination df_facility_dot_notice { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/$FACILITY.notice"); };
destination df_facility_dot_warn { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/$FACILITY.warn"); };
destination df_facility_dot_err { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/$FACILITY.err"); };
destination df_facility_dot_crit { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/$FACILITY.crit"); };
[...]

#debug e messaggi vari
destination df_debug { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/debug"); };
destination df_messages { file("/var/log/hostnames/$HOST/$YEAR/$MONTH/messages"); };

#file con messaggi critici, situazione di emergenza!!!!
destination df_emergency { file ("/var/log/hostnames/$HOST/$YEAR/$MONTH/log.emerg"); };

#stampa su consoles virtuali
destination du_all { usertty("*"); };

#logging remoto su stargazer
destination stargazer { tcp(“10.0.1.2” port(15514)); };

Nel file di configurazione vengono definiti diversi file ove memorizzare i messaggi, a seconda della loro categoria. E' infatti possibile definire dei filtri sui messaggi, in base ai quali scegliere dove memorizzarli. Da qui l'utilizzo di destinazioni diverse, in modo da favorire successivamente la leggibilita' e l'analisi dei log.
Quasi tutte le destinazioni usano il driver file(), e quindi provvedono a memorizzare i messaggi che rispetteranno determinati criteri all'interno del file indicato come argomento.
Viene inoltre fatto ricorso alle macro quali $HOST, $YEAR e $MONTH. Esse verranno rispettivamente espanse nell'hostname della macchina che ha generato il messaggio, l'anno ed il mese  in cui esso  e' stato ricevuto. Per default le macro $YEAR, $MONTH ecc. verrebbero espanse con l'anno e il mese in cui il messaggio e' stato generato, ma tale comportamento e' stato modificato dall'opzione use_time_recvd() come si vedra' successivamente.
Ricorrendo a queste macro e' quindi possibile organizzare automaticamente i log registrandoli in cartelle a seconda della loro provenienza e di quando siano stati generati. Ad esempio i log del mese di dicembre 2005 generati da paranoid saranno memorizzati sotto la directory /var/log/hostnames/paranoid/2005/12 .
L'ultima destinazione, a cui e' stato associato l'alias stargazer utilizza il driver tcp() , e  nel caso riportato consente di inviare i messaggi syslog alla porta tcp 15514 dell'indirizzo IP 10.0.1.2 ovvero l'indirizzo dell'host stargazer. Perche' questi messaggi possano essere ricevuti il log-server remoto all'indirizzo 10.0.1.2, dovra' avere tale porta tra i suoi sorgenti per la raccolta dei messaggi stessi.

- filter: specificano come syslog-ng debba eseguire l'instradamento dei diversi messaggi. In pratica essi sono costituiti da un'espressione booleana che verra' valutata per decidere se e dove un messaggio dovra' essere registrato. Ai filtri, come per destinazioni e sorgenti e' possibile associare dei nomi, in modo che essi possano venire utilizzati anche in diversi log statements.
I filtri sono stati impostati secondo i valori di facilitiy e severitiy indicate nella rfc 3164 (The BSD syslog protocol). Per la precisione i filtri definiti in base alle facility sono stati i seguenti:

#messaggi con facility 4 o 10 (autenticazione e sicurezza.)
filter f_auth { facility(auth, authpriv); };  
#messaggi con facility diversa da auth ed authpriv.
filter f_syslog { not facility(auth, authpriv, mail, news); };
#messaggi con facility 9, relativi al demone cron
filter f_cron { facility(cron); };
#messaggi relativi agli altri demoni di sistema (facility 3)
filter f_daemon { facility(daemon); };
#messaggi dal kernel (0)
filter f_kern { facility(kern); };
#messaggi relativi al sistema di stampa (facility 6)
filter f_lpr { facility(lpr); };
#messaggi relativi a mail  (facility 2 )
filter f_mail { facility(mail); };
#messaggi relativi allo user-space (facility 1)
filter f_user { facility(user); };
#sottosistema uucp
filter f_uucp { facility(uucp); };
#attivita' server ssh
filter f_sshd { facility(local0); };

Una seconda serie di filtri, sono stati invece definiti a seconda del livello di severity dei messaggi. Ad ogni livello di severity corrisponde un numero i cui significati sono rispettivamente messaggi di debug (livello 7), info (informazioni, livello 6), notice (informazioni significative, 5), warning (avvisi, 4), error (situazioni di errore, 3), critical (eventi critici, 2), alert (condizioni che richiedono intervento immediato, 1) ed infine emergency (sistema inutilizzabile, livello 0).
Tra i filtri definiti quindi :

#messaggi con severity < 6
filter f_at_least_info { level(info..emerg); };
#severity <5
filter f_at_least_notice { level(notice..emerg); };
#severity < 4
filter f_at_least_warn { level(warn..emerg); };
#severity <3
filter f_at_least_err { level(err..emerg); };
#severity < 2
filter f_at_least_crit { level(crit..emerg); };

#messaggi con severity debug e facility diversa da auth, authpriv,
# news e mail
filter f_debug { level(debug) and not facility(auth, authpriv, news, mail); };

# messaggi con severity da info a error, facility diversa da auth,
# authpriv,cron, daemon, news e  mail
filter f_messages {
level(info..error) and not facility(auth,authpriv,cron,daemon,mail,news); };

#messaggi con severity critica
filter f_critical{ level(crit); };

# messages con severity emergenza!!!
filter f_emerg { level(alert, emerg); };

Alcuni filtri sono  presenti perche' inclusi nella configurazione di default di syslog-ng, ma senza essere effettivamente utilizzati, come ad esempio tutti gli f_at_least_*. Per completezza si e' preferito lasciarli definiti, nel caso dovessero tornare utili in seguito. Inoltre, non venendo inseriti in alcun log statement non dovrebbero avere effetti negativi relativamente alle prestazioni del log server.

- log:  log path o log statements, definiscono come collegare tra loro sorgenti destinazioni e filtri. In pratica uno statement indica la destinazione per i messaggi provenienti dalle sorgenti specificate e che rispettino il filtro filter.
Tutti i log statements vengono processati nell'ordine in cui sono definiti all'interno di syslog-ng.conf, e quindi un messaggio potra' anche essere inviato a destinazioni diverse se rispetta i rispettivi statements.

#registra messaggi con facility auth e authpriv in auth.log
log { source(s_all); filter(f_auth); destination(df_auth); };
#registra il messaggi che rispsettano f_syslog in df_syslog
log { source(s_all); filter(f_syslog); destination(df_syslog); };
#registra messaggi relativi allla facility 9 in cron.log
log { source(s_all);  filter(f_cron);  destination(df_cron);};
#registra messaggi con facility 3 in daemon.log
log { source(s_all); filter(f_daemon); destination(df_daemon);};
#registra i messaggi del kernel in kern.log
log {source(s_all); filter(f_kern); destination(df_kern); };
# registra i messaggi del sistema di stampa in lpr.log
log { source(s_all); filter(f_lpr); destination(df_lpr); };
#messaggi con facility mail in mail.log
log { source(s_all); filter(f_mail); destination(df_mail); };
#messaggi relativi allo user-space in user.log
log { source(s_all); filter(f_user); destination(df_user); };
#messaggi dal server sshd
log { source(s_all); filter(f_sshd); destination(df_sshd); };
# messaggi con facility mail e severity pari almeno ad in info in
# mail.info
log { source(s_all); filter(f_mail); filter(f_at_least_info); destination(df_facility_dot_info); };
# messaggi con facility mail e severity pari almeno a warning in
# mail.warn
log { source(s_all); filter(f_mail); filter(f_at_least_warn); destination(df_facility_dot_warn); };
# messaggi con facility mail e severity pari almeno a errori in
# mail.err
log { source(s_all); filter(f_mail); filter(f_at_least_err); destination(df_facility_dot_err); };
# messaggi severity debug e facility diversa da auth, authpriv,
# news e mail,vanno in debug
log { source(s_all); filter(f_debug); destination(df_debug); };
# severity tra info e error e facility diversa da
#  auth,authpriv,cron,daemon,mail,news registrati  nel file
#  messages
log { source(s_all); filter(f_messages); destination(df_messages); };
# messaggi con severity critica in log.emerg
log { source(s_all); filter(f_critical); destination(df_emergency);  };
# messaggi con severity emergency e alert  in log.emerg e sulle
# tty
log { source(s_all); filter(f_emerg); destination(df_emergency); destination(du_all); };

Come si puo' notare all'interno dei filtri e dei files di destinazione e' presente anche un filtro con facility pari a local0. Esso e' relativo all'attivita' del servizio ssh., che e' stato configurato in modo da generare messaggi con questo livello di facility personalizzato, in modo da registrarli in un file separato, sshd.log appunto.

Vengono infine definite una serie di opzioni, nella sezione options del file di configurazione. Tra le opzioni modificate rispetto alla configurazione di default:
- sync(3) : indica il numero minimo di linee che debbono essere presenti nella coda di output dei messaggi, prima che vengano scritte in un file.
- group(sys_operators) e perm(0640): impone che i nuovi files creati da syslog-ng appartengano al gruppo sys_operators con permessi di lettura e scrittura per il proprietario e sola lettura per gli appartenenti al gruppo. Il proprietario dei files e' per default l'utente root, ma volendo e' possibile cambiarlo tramite l'opzione owner.
- dir_group(sys_operators) e dir_perm(0750): specifica che le nuove cartelle create da syslog-ng appartengano al gruppo sys_operators, ed abbiano permessi di lettura scrittura ed esecuzione per il proprietario (ovvero root), e di sola lettura ed esecuzione per gli appartenenti al gruppo. E' possibile cambiare l'owner delle directory tramite l'opzione dir_owner.
- check_hostname(yes) : consente di eseguire una verifica sull'hostname dei messaggi ricevuti, se esso contiene caratteri validi o meno.
- keep_hostname(no) : specifica se fidarsi o meno dell'hostname contenuto nei messaggi syslog. Impostandolo a no il valore di hostname dei messaggi dovrebbe venire riscritto a seconda della provenienza dei pacchetti che li contengono.
- use_time_recvd(yes): utilizzata per l'espansione delle macro, indica di sostituire le macro relative al tempo con il momento della ricezione del messaggio e non quello della spedizione del precedente log-server della catena.

La configurazione di syslog-ng su stargazer e' pressoche' identica a quella effettuata per paranoid. Le uniche differenze sono la mancanza della destinazione destination stargazer { tcp(“10.0.1.2” port(15514)); }; e l'aggiunta tra le sorgenti di tcp(ip("10.0.1.2" port(15514) keep-alive(yes)); .
In questo modo il log-server su stargazer registrera' anche i messaggi in arrivo sulla sua porta tcp 15514. Anche in questo caso l'indirizzo IP specificato non e' 192.168.1.2, ma 10.0.1.2, ovvero quello dell'interfaccia virtuale usata per la vpn.
Un'altro aspetto considerato per predisporre il sistema di logging e' stata la cifratura dei messaggi trasferiti. In caso contrario un eventuale attacker in grado di sniffare il traffico tra gli host paranoid e stargazer potrebbe avere la possibilita' di osservare i messaggi inviati e raccogliere informazioni relative alle attivita' effettuate.
Per cifrare il canale tra le due macchine le mia scelta iniziale era stata stunnel, ma non sono riuscito in alcun modo a farlo funzionare. Secondo i log generati non riusciva ad accettare connessioni dall'esterno, riportando un codice di errore 111.
In alternativa ho deciso di ricorrere a openvpn per la creazione di una vpn tra i miei due host, anche se forse si tratta di una soluzione un po' sovradimensionata rispetto alle mie necessita'.
Su entrambe le macchine e' quindi stato installato openvpn con il comando apt-get install openvpn.
Per prima cosa e' stata generata su stargazer la chiave da utilizzare con il comando openvpn --genkey --secret vpn.key, copiata all'interno di /etc/openvpn, ed assegnato il permesso di sola lettura all'utente root (chmod 400 /etc/openvpn/vpn.key).
Per evitare che essa potesse essere cancellata erroneamente le e' stato associato anche l'attributo immutable con chattr +i /etc/openvpn/vpn.key .
La chiave e' poi stata copiata anche su paranoid, associandole gli stessi permessi.
Per fare funzionare la vpn e' stato necessario l'utilizzo del dispositivo di rete virtuale tun/tap.  Non e' stato necessario creare il device con il comando mknod, poiche' a quanto pare aveva gia' provveduto a farlo debconf dopo l'installazione del pacchetto openvpn.
Il file di configurazione per stargazer, /etc/openvpn/server.conf, e' stato il seguente :

#server vpn
dev tap
lport 5000
ifconfig 10.0.1.2 255.255.255.0
secret /etc/openvpn/vpn.key
verb 2

L'opzione dev indica il device utilizzato per il tunnel, lport specifica la porta usata lato server per la vpn, ifconfig l'indirizzo IP e la netmask dell'interfaccia virtuale, secret il percorso al file contenente la chiave, ed infine verb definisce il livello di verbosity.
Sull'host paranoid le operazioni eseguite sono state le medesime, a differenza di alcune differenze in /etc/openvpn/client.conf:

#client vpn
remote 192.168.1.2
dev tap
rport 5000
ifconfig 10.0.1.4 255.255.255.0
secret /etc/openvpn/vpn.key
verb 1

Remote specifica il vero indirizzo IP della macchina a cui ci si andra' a connettere, ovvero stargazer, rport la porta utilizzata all'altra estremita' per creare il tunnel, secret il file della chiave e ifconfig l'indirizzo dell'interfaccia virtuale su paranoid.
Infine, su stargazer, e' stato necessario abilitare l'ip forwarding, con il comando echo 1 > /proc/sys/net/ipv4/ip_forward. Per automatizzare l'operazione si potrebbe inserire il comando in qualche script eseguito automaticamente all'avvio del sistema.
Eseguite queste operazioni si e' avviata la vpn su entrambe le macchine con la linea di comando /etc/init.d/openvpn start.
Configurando in questo modo la vpn, un eventuale attacker che dovesse sniffare il traffico tra le due macchine vedrebbe solo dei pacchetti UDP con porta sorgente e destinazione pari a 5000, il cui contenuto sarebbe per lui privo di significato a causa della cifratura.
Per verificare il corretto funzionamento e' stato controllato con ethereal lo scambio dei messaggi syslog tra paranoid e stargazer durante il login di un utente sul primo host.
Come ci si poteva aspettare il traffico visibile era costituito dai pacchetti UDP della vpn, mentre su stargazer il login e' stato correttamente registrato nel file /var/log/hostnames/paranoid/2005/12/auth.log :

Dec 28 11:02:02 paranoid login[456]: (pam_unix) session opened for user andrea by LOGIN(uid=0)
Dec 28 11:02:02 paranoid login[456]: `andrea' logged in  on `tty2'

Privacy Policy