HOWTO: configurare ssh-agent su CentOS 7 e riutilizzarlo tra le sessioni

CentOS-logo
CentOS-logo

Configurare ssh-agent su CentOS permette di utilizzare Git tramite SSH senza dover inserire la passphrase della chiave privata a ogni pull o push, mantenendo comunque la protezione offerta dalla passphrase.

Ovviamente tutto ciò che vi dico qui si può applicare allo stesso modo a Ubuntu o a un’altra distribuzione, ma oggi questo è il server con cui mi trovo a lavorare.

ssh
ssh-agenti su CentOS

La soluzione non è eliminare la passphrase dalla chiave, ma utilizzare correttamente ssh-agent in modo da non dover riudigitare la password ogni volta che ci si riconnette alla consolle con una nuova sessione.

In questo HOWTO vediamo come configurarlo su CentOS Linux 7, facendo in modo che lo stesso agent venga riutilizzato anche nelle successive sessioni SSH.

Il problema

Supponiamo di avere una chiave Ed25519:

~/.ssh/id_ed25519

e di accedere a un repository Git attraverso SSH.

Un normale:

git pull

può quindi produrre:

Enter passphrase for key '/home/marcob/.ssh/id_ed25519':

La chiave è correttamente protetta, ma non vogliamo digitare continuamente la passphrase.

Due parole su Ed25519

Ed25519 è un algoritmo di firma digitale basato sulla crittografia a curve ellittiche, comunemente utilizzato da SSH per l’autenticazione: la chiave privata rimane sul client, mentre la corrispondente chiave pubblica viene registrata sul server. La passphrase non fa parte dell’algoritmo Ed25519: serve invece a proteggere il file contenente la chiave privata, che ssh-agent può sbloccare una volta e mantenere disponibile in memoria.

A cosa serve ssh-agent su CentOS

ssh-agent è un processo che mantiene in memoria le chiavi private sbloccate.

La sequenza classica è:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Il primo comando avvia l’agent; il secondo gli consegna la chiave, chiedendoci la passphrase.

Possiamo verificare le chiavi presenti nell’agent con:

ssh-add -l

Da quel momento i programmi che utilizzano SSH, compreso Git, possono chiedere all’agent di effettuare le operazioni crittografiche senza doverci chiedere nuovamente la passphrase.

C’è però un problema: aprendo una nuova sessione SSH, la nuova shell deve sapere come trovare l’agent già esistente.

Il socket di ssh-agent

Quando viene avviato, ssh-agent espone principalmente due variabili:

SSH_AUTH_SOCK
SSH_AGENT_PID

Possiamo visualizzarle con:

echo "$SSH_AUTH_SOCK"
echo "$SSH_AGENT_PID"

Per esempio:

/tmp/ssh-GOMN9SlaqeyH/agent.16645
16647

SSH_AUTH_SOCK indica un Unix domain socket attraverso il quale i processi comunicano con ssh-agent (se volete qui trovate un articolo che parla del funzionamento dei socket)

Questo è un dettaglio importante: la chiave privata non viene copiata nelle applicazioni che ne hanno bisogno. È l’agent a eseguire per loro le operazioni crittografiche richieste.

Se però apriamo una nuova shell, dobbiamo fare in modo che essa recuperi SSH_AUTH_SOCK e SSH_AGENT_PID.

Rendere persistenti le informazioni sull’agent

Creiamo nel nostro .bashrc un piccolo meccanismo che salva queste informazioni:

SSH_ENV="$HOME/.ssh/agent-environment"
SSH_KEY="$HOME/.ssh/id_ed25519"

start_agent() {
    echo "Avvio ssh-agent..."
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
    chmod 600 "$SSH_ENV"
    . "$SSH_ENV" > /dev/null
}

Il file:

~/.ssh/agent-environment

conterrà qualcosa del genere:

SSH_AUTH_SOCK=/tmp/ssh-GOMN9SlaqeyH/agent.16645; export SSH_AUTH_SOCK;
SSH_AGENT_PID=16647; export SSH_AGENT_PID;

Possiamo quindi recuperare un agent già esistente oppure crearne uno nuovo:

if [ -f "$SSH_ENV" ]; then
    . "$SSH_ENV" > /dev/null

    # Verifica che l'agent salvato sia ancora in esecuzione
    if ! kill -0 "$SSH_AGENT_PID" 2>/dev/null; then
        start_agent
    fi
else
    start_agent
fi

kill -0 non termina il processo: viene utilizzato semplicemente per verificare se il PID indicato esiste ed è accessibile.

Caricare automaticamente la chiave

Avviare ssh-agent non significa automaticamente caricare una chiave.

Se:

ssh-add -l

risponde:

The agent has no identities.

l’agent è perfettamente funzionante, ma non contiene ancora alcuna identità.

Aggiungiamo quindi:

if ! ssh-add -l >/dev/null 2>&1; then
    ssh-add "$SSH_KEY"
fi

Se l’agent non contiene identità, viene eseguito automaticamente ssh-add.

Il .bashrc completo

Il blocco completo diventa quindi:

# Riutilizzo di ssh-agent tra sessioni
SSH_ENV="$HOME/.ssh/agent-environment"
SSH_KEY="$HOME/.ssh/id_ed25519"

start_agent() {
    echo "Avvio ssh-agent..."
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
    chmod 600 "$SSH_ENV"
    . "$SSH_ENV" > /dev/null
}

# Recupera l'agent esistente oppure ne avvia uno nuovo
if [ -f "$SSH_ENV" ]; then
    . "$SSH_ENV" > /dev/null

    if ! kill -0 "$SSH_AGENT_PID" 2>/dev/null; then
        start_agent
    fi
else
    start_agent
fi

# Carica la chiave se l'agent non contiene identità
if ! ssh-add -l >/dev/null 2>&1; then
    ssh-add "$SSH_KEY"
fi

Cosa succede adesso

Al primo login dopo l’avvio dell’agent verrà richiesta la passphrase:

Enter passphrase for /home/marcob/.ssh/id_ed25519:

Una volta inserita, la chiave rimane caricata nell’agent.

Possiamo verificarlo con:

ssh-add -l

Ora:

git pull
git push

non richiederanno più la passphrase.

Se chiudiamo la sessione SSH e successivamente ci ricolleghiamo al server, il .bashrc recupererà il PID e il socket dell’agent precedente. Se questo è ancora attivo e contiene la chiave, non verrà chiesto nulla.

In sostanza:

nuovo login
     |
     v
esiste agent-environment?
     |
  +--+--+
  |     |
 sì     no
  |     |
  v     v
agent   avvia
vivo?   agent
  |
+-+--+
|    |
sì   no
|    |
|   avvia
|   agent
|
v
chiave caricata?
  |
+-+--+
|    |
sì   no
|    |
|   ssh-add
|    |
+----+
  |
  v
Git via SSH

La passphrase viene quindi normalmente richiesta una volta sola, mentre l’agent può essere riutilizzato da più sessioni.

Dopo un reboot della macchina, naturalmente, il vecchio processo ssh-agent non esisterà più: il nostro .bashrc se ne accorgerà, ne avvierà uno nuovo e ssh-add richiederà nuovamente la passphrase.

Ed è esattamente quello che vogliamo.

Verifica e diagnostica

In caso di dubbi possiamo controllare l’intera situazione con:

echo "PID=$SSH_AGENT_PID"
echo "SOCK=$SSH_AUTH_SOCK"

ps -ef | grep '[s]sh-agent'

ssh-add -l

cat ~/.ssh/agent-environment

È utile soprattutto distinguere due situazioni molto diverse:

ssh-agent non esiste

da:

ssh-agent esiste, ma non contiene identità

Nel secondo caso non dobbiamo avviare un altro agent: basta un semplice:

ssh-add ~/.ssh/id_ed25519

Con questa configurazione manteniamo quindi la protezione offerta dalla passphrase senza trasformare ogni git pull in un piccolo esercizio di memoria.

Riferimenti

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.