Disclaimer :
La versione originale di questo articolo è stata pubblicata da IBM
developerWorks ed è di proprietà di Westtech Information Services. Questo
documento è una versione aggiornata dell'articolo originale, e contiene
numerosi miglioramenti apportati dal Gentoo Linux Documentation team.
Questo documento non è mantenuto attivamente.
|
OpenSSH gestione delle chiavi, Parte 3
1.
L'inoltro dell'agent e i miglioramenti a keychain
Molti usano l'eccellente OpenSSH come un sostituto sicuro e cifrato per i
venerandi telnet e rsh. Una delle opportunità più intriganti di OpenSSH è la sua
abilità di consentire l'autenticazione degli utenti tramite i protocolli di
autenticazione RSA e DSA, che sono basati su una coppia di "chiavi" numeriche
complementari. Una delle principali attrattive dell'autenticazione RSA e DSA è
la promessa di essere capace di stabilire connessioni a sistemi remoti senza
fornire una password. Per maggiori informazioni, si possono leggere le parti
precedenti di questa serie sulla gestione delle chiavi OpenSSH, che riguardano,
rispettivamente, autenticazione
RSA/DSA (Parte 1) e ssh-agent e keychain
(Parte 2).
Da quando la Parte 2 è stata pubblicata su developerWorks nel settembre 2001, e
più tardi citata su Slashdot e Freshmeat (vedi Risorse più avanti in questo articolo per i link a
questi siti), molte persone hanno iniziato a usare keychain, e questo ha
alimentato numerosi cambiamenti. Ho ricevuto qualcosa come 20 patch di qualità
da sviluppatori di tutto il mondo. Ho incorporato molte di queste patch nel
codice di keychain, che ora è alla versione 1.8 (vedi Risorse). Invio i miei sinceri ringraziamenti a tutti
coloro che hanno proposto patch, segnalato bug, suggerito funzionalità e inviato
note di apprezzamento.
Stringere le maglie alla sicurezza ssh
Nel mio ultimo
articolo ho dedicato qualche tempo ad analizzare i benefici e i
bilanciamenti alla sicurezza che si ottengono eseguendo ssh-agent. Pochi giorni
dopo che il secondo articolo è apparso su developerWorks, ho ricevuto una e-mail
da Charles Karney della Sarnoff Corporation, che mi ha informato con cortesia
delle possibilità di inoltro del nuovo OpenSSH agent, che esamineremo fra poco.
Inoltre, Charles ha evidenziato che eseguendo ssh-agent su macchine non fidate è
leggermente pericoloso: se qualcuno riesce ad ottenere l'accesso al sistema come
root, allora le chiavi decifrate possono essere estratte dall'ssh-agent. Anche
se l'estrazione delle chiavi può essere genericamente difficile, è nelle
possibilità dei "cracker" professionisti. Ed il solo fatto che il furto delle
chiavi è possibile significa che occorre compiere alcuni passi per garantire che
questo non accada.
Per formulare una strategia per proteggere le nostre chiavi private, occorre
all'inizio classificare le macchine a cui abbiamo accesso in una di due
categorie. Se un particolare host è ben affidabile o isolato -- in cui
l'ottenimento dell'accesso come root è davvero improbabile -- allora quella
macchina deve essere considerata un host affidabile (in inglese, trusted). Se,
invece, una macchina è usata da molte altre persone o se si hanno dubbi sulla
sicurezza del sistema, allora la macchina deve essere considerata non affidabile
(in inglese, untrusted). Per difendere le chiavi private dall'estrazione,
ssh-agent (e quindi keychain) non devono mai essere eseguiti su un host non
affidabile. In questo modo, anche se la sicurezza del sistema è compromessa, non
ci sarà alcun ssh-agent a disposizione dell'intruso che gli consenta
l'estrazione delle chiavi.
Tuttavia, questo crea un problema. Se non possiamo eseguire ssh-agent su
macchine inaffidabili, allora come possiamo stabilire connessioni ssh sicure e
senza password da questi sistemi? La risposta è semplicemente usare ssh-agent e
keychain sulle macchine affidabili, e usare le nuove opportunità di inoltro
dell'autenticazione di OpenSSH per estendere l'autenticazione senza password
alle macchine inaffidabili. In poche parole, l'inoltro dell'autenticazione
funziona consentendo a sessioni ssh remote di contattare un ssh-agent in
esecuzione in un host affidabile.
Inoltro dell'agent di autenticazione
Per aver un'idea di come funziona l'inoltro dell'autenticazione, consideriamo
un'ipotetica situazione in cui un utente drobbins ha un laptop affidabile
chiamato lappy, un server affidabile chiamato trustbox, e due altri sistemi non
affidabili a cui deve accedere, chiamati notrust1 e notrust2, rispettivamente.
Attualmente, l'utente usa ssh-agent con keychain sulle quattro macchine, come
segue:
Figura 1.1: ssh-agent in esecuzione su macchine affidabili e non affidabili |
 |
Il problema con questo approccio è che se qualcuno ottiene l'accesso come root
su notrust1 o notrust2, allora è ovviamente possibile per quella persona
estrarre le chiavi dall'ora vulnerabile processo ssh-agent. Per proteggersi,
allora, drobbins ferma l'esecuzione di ssh-agent e keychain sugli host non
affidabili notrust1 e notrust2. Di fatto, per essere estremamente cauto,
drobbins ha deciso di usare ssh-agent e keychain solo su lappy. Questo limita
l'esposizione delle sue chiavi private decriptate, proteggendolo dal furto
delle chiavi private:
Figura 1.2: ssh-agent in esecuzione solo su lappy; una configurazione più sicura |
 |
Certamente, il problema con questo approccio è che drobbins ora può stabilire
connessioni senza password solo da lappy. Vediamo invece come abilitare
l'inoltro dell'autenticazione e aggirare questo problema.
Supponendo che tutte le macchine eseguano versioni recenti di OpenSSH, possiamo
aggirare il problema utilizzando l'inoltro dell'autenticazione. L'inoltro
dell'autenticazione consente che processi ssh remoti contattino l'ssh-agent in
esecuzione sulla propria macchina affidabile -- invece che richiedere che una
versione di ssh-agent sia in esecuzione sulla stessa macchina dalla quale si
vuole iniziare il dialogo ssh. Questo generalmente consente di eseguire
ssh-agent (e keychain) su una singola macchina, e significa che tutte le
connessioni ssh originate (direttamente o indirettamente) da questa macchina
useranno l'ssh-agent locale.
Per abilitare l'inoltro dell'autenticazione, aggiungiamo la riga seguente al
/etc/ssh/ssh_config di lappy e trustbox. Si osservi che questo è il
file di configurazione di ssh (ssh_config), non il file di
configurazione del demone sshd (sshd_config):
Codice 1.1: Aggiungiamo questa riga al file /etc/ssh/ssh_config |
ForwardAgent Yes
|
Ora, per trarre vantaggio dall'inoltro dell'autenticazione, drobbins può
connettersi da lappy a trustbox, e poi da trustbox a notrust1 senza fornire
parole chiave per nessuna delle connessioni. Entrambi i processi ssh attingono
all'ssh-agent in esecuzione su lappy:
Codice 1.2: Le infiltrazioni di lappy |
$ ssh drobbins@trustbox
Last login: Wed Sep 26 13:42:08 2001 from lappy
Welcome to trustbox!
$ ssh drobbins@notrust1
Last login: Tue Sep 25 12:03:40 2001 from trustbox
Welcome to notrust1!
$
|
Se si prova una configurazione simile e ci si accorge che l'inoltro dell'agent
non funziona, si può provare ssh -A invece del semplice vecchio ssh per
abilitare esplicitamente l'inoltro dell'autenticazione. Ecco un grafico di
quello che accade dietro le quinte quando ci autentichiamo in trustbox e
notrust1 usando l'inoltro dell'autenticazione:
Figura 1.3: L'inoltro dell'autenticazione in azione |
 |
Come si vede, quando ssh si è connesso a trustbox, ha mantenuto una connessione
con l'ssh-agent in esecuzione su lappy. Quando una connessione ssh è stabilita
da trustbox a notrust1, questo nuovo processo ssh ha mantenuto la connessione di
autenticazione del precedente ssh, estendendo di fatto la catena. Se questa
catena di autenticazione può essere estesa o meno oltre notrust1 verso altri
host dipende da come il /etc/ssh/ssh_config di notrust1 è
configurato. Via via che l'inoltro dell'agente è abilitato, tutte le parti della
catena possono autenticarsi usando l'ssh-agent in esecuzione sull'affidabile
lappy.
Vantaggi dell'inoltro della connessione dell'agent
L'inoltro dell'autenticazione offre diversi vantaggi di sicurezza non esaminati
finora. Per convincermi dell'importanza dell'inoltro della connessione
dell'agent, Charles Karney mi ha messo a disposizione questi tre vantaggi di
sicurezza:
-
La chiave privata è conservata solo nella macchina affidabile. Questo
impedisce a utenti malintenzionati di prelevare le chiavi private cifrate
dal disco e di tentare di svelare la cifratura.
-
ssh-agent è in esecuzione solo sulla macchina affidabile. Questo impedisce a
un intruso di eseguire un dump della memoria di un processo ssh-agent remoto
e successivamente estrarre le chiavi private decifrate dal dump.
-
Poiché è necessario digitare la parola chiave solo sulla macchina
affidabile,si impedisce che i programmi che catturano gli eventi della
tastiera (keystroke loggers) catturino di nascosto la parola chiave così
come viene digitata.
L'unico svantaggio dell'inoltro dell'agent di autenticazione è che non consente
alle attività in cron di trarre vantaggio dall'autenticazione RSA/DSA. Una
soluzione a questo problema è configurare tutte le attività in cron che hanno
bisogno di autenticazione RSA/DSA in modo che vengano eseguite da una macchina
affidabile della LAN. Se necessario, queste attività cron possono usare ssh per
connettersi a sistemi remoti dove eseguire backup automatici, sincronizzare
file, e così via.
Ora che abbiamo esaminato l'inoltro dell'agent di autenticazione, andiamo a
vedere i recenti miglioramenti apportati allo stesso script keychain.
Miglioramenti alle funzionalità di keychain
Grazie alle proposte di patch di molti utenti, sono stati apportati molti
miglioramenti al codice di keychain. Molte delle patch proposte dagli utenti
riguardavano le funzionalità. Per esempio, si ricorderà che keychain crea un
file ~/.ssh-agent; il nome di questo file è stato cambiato in
~/.ssh-agent-[hostname] in modo che keychain funzioni con home
directory montate tramite NFS alle quali si può accedere da diversi host.
Oltre al file ~/.ssh-agent-[hostname], c'è ora un file
~/.ssh-agent-csh-[hostname] che può essere utilizzato dalle shell
csh-compatibili. Infine, una nuova opzione --nocolor è stata aggiunta in
modo da poter disattivare la "colorizzazione" se si una un terminale non vt100.
Correzioni alle compatibilità con le shell
Mentre i miglioramenti alle funzionalità sono stati significativi, la maggior
parte delle correzioni ha riguardato la compatibilità con le shell. Come si è
visto, mentre keychain 1.0 richiede bash, le versioni successive sono state
modificate per lavorare con qualsiasi shell sh-compatibile. Questa modifica
consente a keychain di lavorare in modo trasparente in quasi tutti i sistemi
UNIX, compresi Linux, BSD, Solaris, IRIX e AIX, come anche in altre piattaforme
UNIX. La transizione verso sh e la compatibilità generale UNIX è stata non solo
un persorso accidentato, ma anche una straordinaria esperienza di apprendimento.
Creare uno script che possa essere eseguito su tutte queste piattaforme è
stato un problema di difficile soluzione, soprattutto perché semplicemente non
ho accesso alla maggior parte di questi sistemi! Fortunatamente, molti utenti di
keychain in tutto il mondo possono avere accesso ad altri sistemi, e molti hanno
fornito una grande assistenza identificando problemi di compatibilità e
sottoponendo patch per risolverli.
Ci sono in sostanza due tipi di problemi di compatibilità che devono essere
risolti. Per prima cosa, ho bisogno di essere certo che keychain usi solo
espressioni e operatori nativi e pienamente supportati da tutte le
implementazioni sh, incluse tutte le popolari shell UNIX sh, gratuite e
commerciali, zsh (in modalità sh-compatibile) e bash nelle versioni 1 e 2. Ecco
qualcuna delle correzioni per la compatibilità tra le shell proposte dagli
utenti che sono state applicate al codice di keychain:
Poiché le vecchie shell sh non supportano la convenzione ~ per far riferimento
alla directory home, le linee che usano ~ sono state modificate per usare
invece $HOME:
Codice 1.3: Trasformare in $HOME |
hostname=`uname -n`
pidf=${HOME}/.ssh-agent-${hostname}
cshpidf=${HOME}/.ssh-agent-csh-${hostname}
|
Inoltre, ogni riferimento al comando source è stato cambiato in . per assicurare
la compatibilità con la purista /bin/sg di NetBSD, che non supporta il comando
source:
Codice 1.4: Accontentare NetBSD |
if [ -f $pidf ]
then
. $pidf
else
SSH_AGENT_PID="NULL"
fi
|
Lungo la strada, ho anche applicato qualche correzione per migliorare le
prestazioni. Un programmatore shell di buon senso mi ha informato che invece di
"toccare" un file scrivendo touch foo, si può fare invece:
Codice 1.5: Creare i file |
> foo
|
Facendo riferimento a una sintassi nativa di shell invece che usare un programma
esterno, si evita un fork() e lo script diventa un po' più efficiente. > foo
dovrebbe funzionare in tutte le shell sh-compatibili; tuttavia non sembra
supportato da ash. Questo non dovrebbe essere un problema per la maggior parte
delle persone, poiché ash è più una shell da disco di emergenza che qualcosa da
usare per il lavoro quotidiano.
Rilasci degli eseguibili diversi tra piattaforme
Far sì che uno script lavori sotto diversi sistemi operativi UNIX richiede
qualcosa in più che aderire ad una pura sintassi sh. Occorre ricordare che molti
script invocano anche comandi esterni, come grep, awk, ps e altri, e questi
comandi devono essere invocati nel modo più rispettoso possibile degli standard.
Ad esempio, mentre l'echo incluso in molte versioni di UNIX riconosce l'opzione
-e, Solaris non ha quell'opzione -- semplicemente stampa -e nello
stdout quando eseguito. Quindi, per andare incontro a Solaris, keychain ora
riconosce se echo -e funziona:
Codice 1.6: Accorgersi di Solaris |
if [ -z "`echo -e`" ]
then
E="-e"
fi
|
Qui sopra, E è impostato a -e se l'escaping -e è supportato. Quindi echo
può essere invocato come segue:
Codice 1.7: echo migliorato |
echo $E Usage: ${CYAN}${0}${OFF} [ ${GREEN}options${OFF} ] ${CYAN}sshkey${OFF} ...
|
Usando echo $E invece di echo -e, l'opzione -e può essere
dinamicamente abilitata o disabilitata a secondo.
pidof, ps
Probabilmente la più importante correzione di compatibilità ha riguardato la
modalità con cui keychain individua i processi ssh-agent attualmente in
esecuzione. In precedenza, ho utilizzato il comando pidof per questo scopo, ma
ho dovuto rimuoverlo perché molti sistemi non hanno un pidof. In realtà, pidof
non è comunque la miglior soluzione, visto che elenca tutti i processi ssh-agent
in esecuzione sul sistema, indipendentemente dall'utente, quando invece siamo
interessati a tutti i processi ssh-agent di proprietà del corrente, effettivo
UID.
Quindi, invece di far riferimento a pidof, abbiamo preferito utilizzare una pipe
in cui ps invia l'output a grep che lo invia ad awk, allo scopo di estrarre i
necessari id dei processi. Questa è una correzione proposta da un utente:
Codice 1.8: Usare una pipe è meglio di pidof |
mypids=`ps uxw | grep ssh-agent | grep -v grep | awk '{print $2}'`
|
Questa pipe imposta la variabile mypids ai valori di tutti i processi ssh-agent
di proprietà dell'utente corrente. Il comando grep -v grep fa parte della pipe
per assicurarci che il processo grep ssh-agent non faccia parte della nostra
lista di PID.
Mentre questo è un buon approccio sul piano concettuale, l'uso di di ps
scoperchia un nuovo ricettacolo di problemi, visto che le opzioni di ps non sono
standardizzate tra derivati di UNIX come vari BSD e System V. Ecco un esempio:
mentre ps uxw funziona sotto Linux, non funziona invece sotto IRIX. E mentre
ps -u username -f funziona sotto Linux, IRIX e Solaris, non funziona
invece sotto BSD, che comprende solo le opzioni ps in stile BSD. Per aggirare il
problema, keychain determina automaticamente se il ps del sistema corrente
funziona con la sintassi di BSD o System V prima di eseguire la pipe ps:
Codice 1.9: Individuare BSD o System V |
psopts="FAIL"
ps uxw >/dev/null 2>&1
if [ $? -eq 0 ]
then
psopts="uxw"
else
ps -u `whoami` -f >/dev/null 2>&1
if [ $? -eq 0 ]
then
psopts="-u `whoami` -f"
fi
fi
if [ "$psopts" = "FAIL" ]
then
echo $0: unable to use \"ps\" to scan for ssh-agent processes.
Report KeyChain version and echo system configuration to drobbins@gentoo.org.
exit 1
fi
mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1
|
Per garantire il funzionamento sia del comando ps di System V che con quello di
BSD, lo script esegue una esecuzione "secca" di ps uxw, ignorando l'output.
Se il codice di errore di questo comando è zero, sappiamo che ps uxw funziona e
l'opzione di ps viene impostata normalmente. Invece, se ps uxw restituisce un
codice di errore diverso da zero (che indica che stiamo usando opzioni di stile
BSD), allora eseguiamo un secco ps -u `whoami` -f, di nuovo ignorando
l'output. A questo punto, auspicabilmente lo script ha trovato una variante di
BSD o di System V di ps da utilizzare. Se non ha trovato neanche quella, lo
script stampa un messaggio di errore e esce. Ma è molto probabile che uno dei
due comandi ps abbia funzionato, nel qual caso viene eseguita la linea finale
del frammento di codice riportato qui sopra, la nostra pipe ps. Utilizzando
l'espansione della variabile $psopts subito dopo ps, abbiamo la possibilità di
passare le opzioni corrette al comando ps.
Questa linea pipe ps contiene anche una vera perla di grep, che mi è stata
gentilmente inviata da Hans Peter Verne. Si osservi che grep -v grep non
fa più parte della linea pipe; piuttosto, è stata rimossa e grep
"ssh-agent" è stato cambiato in grep "[s]sh-agent". Questo singolo
comando grep si conclude dopo aver fatto la stessa cosa di grep ssh-agent |
grep -v grep; riuscite a capire perché?
Codice 1.10: Un trucco pulito per grep |
mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1
|
Sorpresi? Se abbiamo pensato che le linee estratte da grep "ssh-agent" e
grep "[s]sh-agent" dovrebbero le stesse, abbiamo ragione. E allora
perché generano diversi risultati quando l'output di ps è inviato in pipe a quei
comandi? Ecco come funziona: quando si usa grep "[s]sh-agent", si cambia il modo
con cui il comando grep appare nella lista generata dal processo ps. In questo
modo, si impedisce a grep di selezionare se stesso, visto che la stringa
[s]sh-agent con corrisponde all'espressione regolare [s]sh-agent. Non è
brillante? Se non vi siete ancora impadroniti di questa tecnica, giocate un
altro po' con grep e ne sarete presto ricompensati.
Conclusioni
Questo articolo conclude la mia panoramica su OpenSSH. Mi auguro che ne abbiate
imparato abbastanza per iniziare a usare OpenSSH per rendere sicuri i vostri
sistemi. Il prossimo mese, le rubriche di Common threads continueranno con la
serie "Advanced filesystem implementor's guide".
2.
Risorse
|