Introduzione
Questo articolo tratta di come fare per fornire servizi limitati, allocati su
server Linux, in maniera molto sicura. Per condurre il discorso verrà utilizzato
come esempio un server che deve fornire un servizio CVS accessibile in scrittura
tramite SSH.
Fino a che un computer è scollegato da qualunque rete i rischi che si corrono
di intrusione e danni in genere sono piuttosto limitati, potendo, l'attaccante,
ricorrere solamente ad un intervento diretto sulla macchina o tramite qualche
supporto removibile (floppy disk, cdrom, ecc.), che qualcuno inserisca nella
stessa.
Se il computer è collegato ad una rete, o addirittura ad internet, i rischi
che si corrono crescono in maniera esponenziale. Se si tratta di un desktop o
una workstation possiamo "blindarlo" tramite antivirus, antispyware e firewall
impedendo qualunque connessione dall'esterno non richiesta.
Ma se si tratta di un server che deve fornire dei servizi come possiamo fare?
È chiaro che almeno le porte tramite cui accedere ai servizi dovremo
lasciarle aperte e accessibili.
Il problema
A questo punto il nostro server sarà esposto a parecchi rischi tra i quali:
- Uso del servizio da parte di un soggetto non autorizzato.
- Intercettazione di password ed altri dati sensibili.
- Intrusione nel computer da parte di un estraneo tramite un bug del servizio stesso.
- Abuso da parte di un soggetto autorizzato dell'accesso disponibile.
I primi due problemi e in parte il terzo sono risolvibili utilizzando
per l'accesso al servizio il protocollo SSH, che, se settato e utilizzato
convenientemente è estremamente robusto.
Come noto SSH è un protocollo che consente una connessione criptata tra due
computer e può garantire anche l'identità delle due parti. Tramite esso possono
essere utilizzati, in modo più sicuro, moltissimi servizi intrinsecamente
insicuri, come shell remote, download e upload di files, servizi di
sincronizzazione, accesso a repository CVS (Concurrent Versions System), ecc.
Ma per quanto SSH sia un eccellente strumento non ci può proteggere da
eventuali abusi commessi da utenti autorizzati all'accesso, o da qualcuno che ha
sottratto loro le credenziali. Infatti una volta entrato l'utente può effettuare
tutte le operazioni previste da sistema, che sono davvero tante, e tentare di
sfruttare i bug di tutte le applicazioni disponibili, in genere tantissime.
Da qui l'idea di "ingabbiarlo" in una zona sterile, ove può fare solo ciò
che gli è stato espressamente consentito.
Strumenti
In questo articolo, oltre ad un sistema Linux, per raggiungere l'obiettivo
verranno usati i seguenti strumenti.
SSh
"SSh" significa "Secure Shell", e consente primariamente di accedere a
sistemi remoti aprendo una shell (o console o terminale) sicura, sia sotto il
profilo dell'identità dell'utente che si logga, che della sicurezza dei dati in
transito.
Il primo profilo viene garantito tramite un'autenticazione basata sullo
scambio di chiavi pubbliche, secondo lo schema della crittografia a chiave
asimmetrica, il secondo tramite la crittazione di tutti i dati in transito.
SSh è anche in grado di gestire in maniera sicura download e upload di file,
oltre a potere essere usato cone tunnel per tutta una serie di altri servizi.
SSh è ormai istallato di default in tutte le distribuzioni linux che conosco,
ma nel caso non lo fosse in genere si reperiscono sempre i pacchetti binari
adatti alla propria distribuzione, altrimenti si possono scaricaricare i
sorgenti da OpenSSh http://www.openssh.org e compilarli.
Per limiti di spazio non verrà affrontato in questa sede il funzionamento di
base di SSh, del resto se si è interessati alle "gabbie chroot" vuol dire che
si è già ad un certo livello. verranno di seguito indicati solo i impostazioni
necessari per fare funzionare la "gabbia".
chroot
"chroot" sta per "change root", cioè cambiare la directory radice del
sistema. Questo comando fa in modo che un dato utente si trovi ad operare in un
sotto sistema, in genere, più limitato del principale sul quale non può
influire. L'utente non amministratore non può uscire dalla "gabbia" chroot
creata e in cui è confinato, la sua sintassi è la seguente:
# chroot /path_to/dir_gabbia /path_to/comando
ad esempio:
# chroot /home/sandbox /bin/bash
con questo comando ci troveremo all'interno di /home/sandbox con a disposizione una shell bash, ma dobbiamo fare attenzione a due cose:
- root può sempre uscirne tramite un semplice comando "exit".
- La shell sarà disponibile solo se è già disponibile all'interno della "gabbia", cioè se all'interno di "/home/sandbox" vi è una directory "bin" con dentro il programma "bash". Ricordiamoci infatti che dentro la "gabbia" "/home/sandbox" corrisponde a "/".
Ad ogni modo nel nostro esempio non faremo uso diretto di "chroot", ma lo utilizzeremo tramite un modulo PAM.
PAM
PAM sta per "Plugable Authentication Module", in pratica si tratta di una
serie di moduli che si possono interfacciare con qualuque programma che li
supporti, e che consentono di gestire in maniera centralizzata ed efficiente le
fasi di pre, durante e dopo l'esecuzione dello stesso. Tipici programmi che
supportano PAM sono "login" e "ssh". Tutte le distribuzioni moderne
supportano PAM, ad ogni modo per accertarsene basta verificare che esista una
directory "/etc/pam.d/" oppure il file "/etc/pam.conf", invece per sapere se
una data applicazione supporta PAM basta digitare:
# ldd </path_to/nome_applicazione> | grep pam
se otteniamo una stringa di questo tipo:
libpam.so.0 => /lib/libpam.so.0 (0x5503c000)
vuol dire che l'applicazione li supporta.
Anche in questo caso non ci addentreremo in uno studio di PAM, trattandosi di un argomento vastissimo, comunque se nella vostra distribuzione PAM è incluso, è anche sicuramente in funzione.
Analizzeremo solo i impostazioni necessari al nostro scopo.
CVS
CVS sta per "Concurrent Versions System", ovvero sistema per la gestione di
versioni diverse e concorrenti di files. Si tratta di un software diffusissimo
per gestire i depositi dei sorgenti di programmi in sviluppo, soprattutto nel
caso in cui gli sviluppatori siano molti e distanti. Evita che vi siano
modifiche non coordinate, e consente di recuperare sempre le vecchie versioni.
Anche in questo caso non analizzeremo approfonditamente CVS, ma solo ciò che ci
interessa per il nostro scopo.
Procedura
I passi necessari sono i seguenti:
La gabbia
Scegliamo una locazione per la nostra "gabbia" per esempio:
/home/sandbox
Per il momento non creiamo la directory.
Al suo interno costruiremo un mini albero delle directory, che riproduca lo
stretto indispensabile per le funzioni che vogliamo consentire. Ci sono molto
modi per farlo, io suggerisco il seguente script che è tratto, e da me adattato,
dalla documentazione a corredo del modulo pam_chroot, che analizzeremo in
seguito.
#!/bin/sh
# Crea una directory ancora NON esistente
# crea un'ambiente chroot ove gli utenti possono loggarsi
# ed avere funzioni limitate
#
# Uso: loggarsi come root
# scegliere la posizione della directory chroot
# digitare: ./<nome_script>.sh /path_to/<directory>
# (c) 2002 Javier Fernandez-Sanguino <jfs@computer.org>
# Modificato da Shishii <shishii_at_shishii_dot_com>
[ -z "$1" ] && {
echo "Uso: $0 directory"
exit 1
}
id=`/usr/bin/id -u`
[ "$id" -gt 0 ] &&
echo "Attenzione: Devi essere root affinche' mknod funzioni"
dir=$1
[ -e "$dir" ] && {
echo "ERRORE: $dir esiste. specifica una directory non esistente"
exit 1
}
curdir=`/bin/pwd`
# crea la directory chroot
/bin/mkdir -p $dir
cd $dir
# crea un file system minimale
for i in bin dev lib home/test ; do
/bin/mkdir -p $i
done
# Procedura:
# Copia dal sistema principale
# nell'ambiente chroot gli eseguibili necessari
# Bin directory
for cmd in /bin/ls
/bin/pwd
/bin/true
/bin/false
/bin/rbash
/bin/bash
/usr/bin/cvs
; do
if [ $cmd ] ; then
cp -a $cmd bin/
fi
done
# Librarie necessarie per gli eseguibili scelti
# vengono copiate dal sistema principale in quello
# chroot
for lib in /lib/ld*
/lib/libacl*
/lib/libattr*
/lib/libc.*
/lib/libc-*
/lib/libdl*
/lib/libncurse*
/lib/librt*
/lib/libpthread*
/lib/libcrypt*
/lib/libnsl*
/usr/lib/libz*
/lib/libpam*
; do
if [ "$lib" ] ; then
cp -a $lib lib/
fi
done
# Crea nell'ambiente chroot i devices necessari
cd dev
# Consoles
/bin/mknod -m 644 tty1 c 4 1
/bin/mknod -m 644 tty2 c 4 2
/bin/mknod -m 644 tty3 c 4 3
/bin/mknod -m 644 tty4 c 4 4
/bin/mknod -m 644 tty5 c 4 5
/bin/mknod -m 644 tty6 c 4 6
# terminale per ssh e telnet
/bin/mknod -m 644 ttyp0 c 3 0
/bin/mknod -m 644 ttyp1 c 3 1
/bin/mknod -m 644 ttyp2 c 3 2
/bin/mknod -m 644 ttyp3 c 3 3
/bin/mknod -m 644 ttyp4 c 3 4
/bin/mknod -m 644 ttyp5 c 3 5
/bin/mknod -m 644 ttyp6 c 3 6
# Altri devices
/bin/mknod -m 444 urandom c 1 9
/bin/mknod -m 666 zero c 1 5
/bin/mknod -m 666 null c 1 3
cd $curdir
exit 0
Il codice è abbastanza auto esplicativo, ad ogni modo se si vuole adattare lo script per costruire una "gabbia" per un servizio diverso da CVS, oppure sorgono altri problemi, ad esempio nomi di librerie diversi da quelli indicati, ecc., basta tenere presente quanto segue:
- di seguito a "for cmd in" vanno inseriti gli eseguibili che ci servono.
- di seguito a "for lib in" vanno inserite le librerie necessarie per quegli eseguibili. Per verificare quali librerie usa un programma basta digitare:
# ldd /path_to/nome_programma
- si consiglia di inserire i nomi delle librerie con un asterisco dopo la
parte fondamentale del nome, in modo da includere anche sottovarianti e links.
Nella seconda parte termineremo la trattazione dell'argomento, vedendo come istallare CVS e configurare il sistema per attivare la "gabbia".
Nella prima parte abbiamo visto come affrontare il problema della creazione di account limitati e sicuri per la fruizione di servizi incanalabili tramite SSh, ad esempio CVS. Abbiamo creato la gabbia entro cui gli stessi potranno operare. Proseguiamo vedendo come istallare e impostare CVS e l'intero sistema.
CVS
Procediamo ora con l'installazione di CVS. Si possono usare i binari della propria distribuzione o la compilazione dei sorgenti. L'importante è inserire la directory del repository, cvs all'interno della sandbox, se la directory cvs e al suo interno la directory CVSROOT sono stati creati durante l'istallazione è sufficiente:
# mv cvs /home/sandbox/
o se cvs non è stata creata dall'istallazione, e quindi non
è avvenuta l'inizializzazione, provvediamo noi. L'inizializzazione
creerà anche la directory cvs/CVSROOT:
# mkdir /home/sandbox/cvs
# cvs -d /home/sandbox/cvs init
Scegliamo un gruppo di sistema idoneo, in genere src o staff
o altro a vostra scelta (nell'esempio usiamo src con group
id = 40) e impostiamo i seguenti proprietari e permessi:
# cd /home/sandbox
# chown -R root:src cvs
# chmod 775 cvs cvs/CVSROOT
# chmod g+s cvs cvs/CVSROOT
# cd cvs/CVSROOT
# chmod 444 *
# chmod 666 history val-tags
A questo punto pensiamo agli accessi al repository. Se si tratta di
un repository che serve sia da centro di sviluppo che di distribuzione
del codice, avremo due tipi di utenti: quelli che possono solo scaricare
i files e quelli dotati di accesso in scrittura... gli sviluppatori.
Per i primi la scelta più comoda è usare il server interno di CVS
(pserver). Pserver funziona tramite il demone inetd o il più moderno
xinetd.
Nel caso che il sistema usi inetd si deve inserire all'interno
di /etc/inetd.conf una stringa di questo genere (ricordandosi di aggiustare
i path):
cvspserver stream tcp nowait root /usr/sbin/tcpd /usr/sbin/cvs-pserver
Se invece il sistema usa xinetd, allora si dovrà aggiungere all'interno
della diretory /etc/xinetd.d un file chiamato cvspserver,
con un contenuto simile:
# default: on
# description: The cvspserver
service cvspserver
{
port = 2401
socket_type = stream
protocol = tcp
wait = no
user = root
passenv = /home/sandbox/cvs/CVSROOT
server = /usr/bin/cvs
server_args = -f -allow-root=/home/sandbox/cvs pserver
disable = no
}
In entrambe i casi il demone sarà in ascolto sulla porta 2401.
A questo punto creiamo un account solo per l'uso di pserver, aggiungendo
questa riga al file /etc/passwd:
anonymous:x:1001:40:Utente CVS:/home/nonexistent:/bin/false
come si vede si tratta di un utente che appartiene al gruppo src
(40), ha una home inesistente, e non può connettersi ad una shell.
Adesso creiamo in /home/sandbox/cvs/CVSROOT il file passwd, con il seguente contenuto:
anonymous:
poi, nella stessa directory creiamo un file readers, con il seguente contenuto:
anonymous
Quindi chiunque vorrà scaricare files dal repository per connettersi
userà:
$ cvs -d :pserver:anonymous@<host>:/home/sandbox/cvs login
CVS Password: <solo invio>
$ cvs -d :pserver:anonymous@<host>:/home/sandbox/cvs ceckout <modulo>
Come abbiamo visto ciò può avvenire senza grossi pericoli (a parte
qualche bug di CVS), in quanto l'utente anonymous non può
connettersi alla macchina ne da remoto ne in locale.
Il discorso cambia se non vogliamo dare accesso a tutti o per gli
utenti che devono avere accesso in scrittura. CVS prevede un meccanismo
per consentire ciò tramite pserver, ma le password circoleranno in
chiaro, così come il resto della comunicazione, e questo è molto pericoloso
soprattutto se nel repository vi sono files che devono rimanere riservati.
La soluzione può fornirla lo stesso CVS che consente di stabilire
una connessione criptata tramite SSh, ma per sfruttare questa possibilità
gli utenti devono avere un vero e proprio account di sistema, con
tanto di shell, il che, come si può intuire, non sempre è raccomandabile.
Per salvare capra e cavoli useremo SShh per connetterci a
CVS, ma dentro una gabbia chroot.
Creazione degli account limitati
Procediamo con la creazione degli account degli utenti da ingabbiare. Per creare gli account possiamo usare il comando:
# adduser <nome utente>
se intendiamo usare le impostazioni di default contenute in /etc/adduser.conf,
altrimenti useremo:
# useradd -c "Descrizione e/o vero nome" -d /home/<userid>
-g 40 -m -s rbash <userid>
Il flag -c consente di inserire i nome vero dell'utente o
una descrizione, ad esempio: Utente CVS; il flag -d
consente di creare la direcory home dell'utente (non indicate direttamente
/home/sandbox/home/<userid> in quanto dopo il chroot il percorso risulterebbe
sbagliato); il flag -g setta il gruppo del nuovo utente;
-s indica la shell che avrà a disposizione, rbash
è una shell ridotta.
Subito dopo spostiamo la directory home dell'utente all'interno della
sandbox:
# mv /home/<userid> /home/sandbox/home
Ora attribuiamo una password all'utente:
# passwd <userid>
Passiamo ora all'attivazione della gabbia.
SSh e PAM
Per attivare la gabbia dobbiamo ora configurare adeguatamente
SSh e i moduli PAM.
Per prima cosa accertiamoci di avere istallato il modulo PAM pam_chroot.so,
se è istallato lo dovreste trovare in /lib/security, se non
è istallato dovete cercare il pacchetto libpam-chroot per
la vostra distribuzione ed istallarlo, oppure scaricare i sorgenti
e compilarli da qui http://http.us.debian.org/debian/pool/main/libp/libpam-chroot/libpam-chroot_0.9.orig.tar.gz.
Impostiamo per prima cosa il file di configurazione del server SSh in modo che usi i moduli PAM per l'autenticazione. Il file /etc/ssh/sshd_config deve riportare anche questi impostazioni:
UsePAM yes
UsePrivilegeSeparation no
Un file sshd_config di esempio funzionante può essere questo:
# Package generated configuration file
# See the sshd(8) manpage for details
# What ports, IPs and protocols we listen for
Port 22
# Use these options to restrict
#which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
#Privilege Separation is turned on for security
#comunque si devono disattivare altrimenti non funziona chroot
UsePrivilegeSeparation no
# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 768
# Logging
SyslogFacility AUTH
LogLevel INFO
#LogLevel DEBUG3
# Authentication:
LoginGraceTime 600
PermitRootLogin no
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
#AuthorizedKeysFile %h/.ssh/authorized_keys
# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# Uncomment if you don't trust ~/.ssh/known_hosts for
#RhostsRSAAuthentication
#IgnoreUserKnownHosts yes
# To enable empty passwords, change to yes (NOT RECOMMENDED)
PermitEmptyPasswords no
# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
# Change to yes to enable tunnelled clear text passwords
PasswordAuthentication yes
# To change Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#AFSTokenPassing no
#KerberosTicketCleanup no
# Kerberos TGT Passing does only work with the AFS kaserver
#KerberosTgtPassing yes
X11Forwarding no
X11DisplayOffset 10
PrintMotd yes
PrintLastLog yes
KeepAlive yes
#UseLogin no
#MaxStartups 10:30:60
#Banner /etc/issue.net
Subsystem sftp /usr/lib/sftp-server
UsePAM yes
riavviamo il server SSh tramite:
# /etc/init.d/ssh restart
Configuriamo ora il modulo PAM di SSh che è /etc/pam.d/ssh, introducendo al penultimo posto, tra le disposizioni impartite:
session required pam_chroot.so
Un file ssh di esempio funzionante può essere questo:
# PAM configuration for the Secure Shell service
# Disallow non-root logins when /etc/nologin exists.
auth required pam_nologin.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
auth required pam_env.so # [1]
# Standard Un*x authentication.
@include common-auth
# Standard Un*x authorization.
@include common-account
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
session optional pam_motd.so debug # [1]
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so debug standard noenv # [1]
# Lastlog
session optional pam_lastlog.so debug
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# mia aggiunta
session required pam_chroot.so
# Standard Un*x password updating.
@include common-password
Infine impostiamo il file di configurazione di pam_chroot.so e cioè /etc/security/chroot.conf, in questo file dovremo inserire l'userid degli utenti da ingabbiare e la directory chroot, ad esempio:
ciccio /home/sandbox
Per evitare di dovere modificare questo file ad ogni creazione di
nuovo utente basta che per gli utenti da ingabbiare usiamo
nell'userid un prefisso, ad esempio chroot_ciccio, chroot_caio,
poi impostiamo in /etc/pam.d/ssh
session required pam_chroot.so use_regex
e in /etc/security/chroot.conf impostiamo:
^chroot_* /home/sandbox
D'ora in poi gli utenti il cui userid inizia con chroot_
verranno ingabbiati.
Conclusioni
A questo punto la gabbia è attiva per cui gli utenti che devono
accedere in scrittura al repository CVS potranno farlo in sicurezza
tramite:
$ export CVS_RSH=ssh
$ cvs :ext:<userid>@<host>:/home/sandbox/cvs ceckout <modulo>
$ Password: <password>
ma se cercheranno di utilizzare il loro account SSh riusciranno a
loggarsi, ma verificheranno che si trovano in un sistema con una shell
limitata con a disposizione solo pochi comandi interni e i comandi
esterni alla shell ls, pwd e cvs, senza possibilità di portarsi
nel sistema principale e quindi senza possibilità di fare danni.
Come detto all'inizio abbiamo usato come esempio il problema di account
CVS con scrittura, ma quando detto si può adattare facilmente a qualunque
servizio incanalabile tramite SSh.