Gentoo Logo

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

Indice:

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

Fig. 1

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

Fig. 2

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

Fig. 3

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:

  1. 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.
  2. 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.
  3. 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



Stampa

Aggiornato il 26 aprile 2010

Oggetto: Nel terzo articolo della serie, Daniel Robbins mostra come trarre vantaggio dall'inoltro dell'agent OpenSSH per migliorare la sicurezza. Inoltre presenta i recenti miglioramenti allo script shell keychain.

Daniel Robbins
Autore

Sergio Vaccaro
Traduzione

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.