Introduzione
Nel precedente articolo abbiamo esplorato come implementare una configurazione SSH modulare su un server Ubuntu/Debian, differenziando i metodi di accesso a seconda della provenienza (interna o esterna), degli utenti e dei metodi di autenticazione. Abbiamo separato la configurazione in file multipli all’interno di sshd_config.d
, applicando politiche restrittive per l’accesso esterno e più permissive per la rete interna, oltre a integrare fail2ban, firewall (ufw
) e il port forwarding su porte alte per aumentare la sicurezza.
In questo nuovo articolo ci concentreremo sull’automazione dello stesso identico approccio utilizzando Ansible, uno strumento di automazione dell’infrastruttura che ci consentirà di distribuire e mantenere le medesime configurazioni su uno o più server in modo semplice, ripetibile e privo di errori umani. L’uso di Ansible garantisce che le configurazioni siano idempotenti: eseguendo il playbook più volte otterremo sempre lo stato desiderato, senza dover ritoccare manualmente i file o rischiare inconsistenze.
Obiettivi principali:
- Automatizzare l’installazione e configurazione di OpenSSH Server.
- Gestire i file di configurazione (
/etc/ssh/sshd_config
e/etc/ssh/sshd_config.d/*.conf
) tramite Ansible. - Impostare regole
ufw
e configurazione base difail2ban
. - Creare utenti, impostare chiavi SSH, differenziare l’accesso da interno ed esterno.
- Integrare il port forwarding su porte alte e mostrare come questa configurazione rimane coerente nel tempo.
- Mantenere la stessa logica del precedente articolo, ma ora tramite playbook Ansible.
Prerequisiti
- Un controller Ansible (ad esempio un sistema locale con Ansible installato).
- Connessione SSH al server o ai server bersaglio, con utente avente privilegi
sudo
. - Chiavi SSH già installate sul controller per l’utente amministrativo che si collegherà al target in modo da non dover usare password negli
ansible-playbook
. - Un server Ubuntu/Debian di destinazione, aggiornato e con un utente sudo.
- Configurazione base di rete, con la possibilità di configurare il router per il port forwarding (come descritto nel precedente articolo).
- Conoscenza base di Ansible e della struttura di un playbook.
Struttura del Progetto Ansible
Possiamo organizzare il nostro progetto con la seguente struttura:
1 | ansible/ |
- inventory.ini: Contiene l’inventario dei nostri host da configurare.
- playbook.yaml: Il file principale che esegue il ruolo
ssh_config
. - roles/ssh_config/tasks/: Directory con i task divisi per argomento (firewall, fail2ban, sshd, utenti).
- roles/ssh_config/templates/: I template Jinja2 per generare i file di configurazione.
- roles/ssh_config/files/: Eventuali file statici come chiavi autorizzate.
È possibile variare la struttura secondo le preferenze, ma questa offre una buona separazione delle responsabilità.
L’inventario di Ansible
Nel file inventory.ini
definiremo i server target:
1 | [ssh_servers] |
Sostituite aa.bb.cc.dd
con l’IP pubblico del server o un dominio che lo punti. L’ansible_user
è l’utente sudo locale creato in precedenza.
Variabili e Considerazioni
Nelle variabili potremmo definire:
- La rete interna da cui consentire l’accesso con password (es.
192.168.0.0/24
). - L’IP esterno o range ammesso all’accesso via chiave.
- L’utente dedicato all’accesso esterno (es.
deploy
).
Queste variabili possono essere definite nel playbook.yaml
o in group_vars/ssh_servers.yaml
per maggiore scalabilità.
Esempio di variabili (in playbook.yaml
o in un file vars.yaml
):
1 | vars: |
Il Playbook Principale: playbook.yaml
Questo playbook eseguirà il ruolo ssh_config
sui server del gruppo ssh_servers
:
1 | - name: Configure SSH access via Ansible |
Il Ruolo ssh_config
Il ruolo si occupa di:
Installare e configurare OpenSSH Server:
- Assicurarsi che
openssh-server
sia installato. - Copiare i template di configurazione in
/etc/ssh/
. - Riavviare il demone
sshd
.
- Assicurarsi che
Configurare il firewall (ufw):
- Consentire la porta 22 (interna) se serve.
- Limitare l’accesso esterno eventualmente per IP specifici.
- Gestire le regole per consentire l’accesso dalla LAN e limitare l’esterno.
Configurare fail2ban:
- Installare fail2ban se non presente.
- Copiare una configurazione base.
- Riavviare fail2ban.
Creare utenti, chiavi SSH, assegnare i privilegi:
- Creare l’utente
deploy
se non esiste. - Aggiungere la chiave pubblica a
~deploy/.ssh/authorized_keys
. - Configurare l’utente amministrativo.
- Creare l’utente
Template per
sshd_config
esshd_config.d/*.conf
:sshd_config.j2
contenente configurazioni generali.00-internal.conf.j2
e01-external.conf.j2
per differenziare gli accessi.
Esempio di tasks/main.yaml
Questo file includerà gli altri file di task:
1 | - name: Include firewall tasks |
Esempio di tasks/firewall.yaml
Questa sezione:
- Installa
ufw
se non presente. - Resetta le regole, permette SSH interno, limita l’esterno all’IP definito.
1 | - name: Ensure ufw is installed |
Qui apriamo la 22 solo per la rete interna e per l’IP specificato per l’esterno. Questo riflette la logica precedente: da esterno accede solo un IP e uno specifico utente.
Esempio di tasks/fail2ban.yaml
1 | - name: Ensure fail2ban is installed |
Abbiamo ignorato il range interno in modo che la LAN non venga bannata da tentativi multipli.
Esempio di tasks/users.yaml
Creiamo l’utente deploy
e aggiungiamo la sua chiave pubblica. Presumiamo di avere una chiave pubblica pronta nel ruolo (in files/authorized_keys_deploy
):
1 | - name: Ensure deploy user exists |
Esempio di tasks/sshd.yaml
Qui gestiamo i file di configurazione sshd_config
e sshd_config.d
tramite template:
1 | - name: Ensure openssh-server is installed |
Template sshd_config.j2
Questo ricalca le configurazioni base:
1 | UsePAM yes |
Template 00-internal.conf.j2
Consente da rete interna l’uso della password e delle chiavi a qualsiasi utente:
1 | Match address {{ internal_network }} |
Template 01-external.conf.j2
Limitato all’utente deploy
e all’indirizzo IP esterno, solo chiave:
1 | Match User {{ external_user }}, Address {{ external_ip }} |
Port Forwarding su Porta Alta (22222)
Dal lato del router, come nel precedente articolo, si configura il port forwarding della porta 22222 esterna verso la 22 interna del server. Ansible non può effettuare questa operazione sul router a meno di avere plugin specifici o un router programmabile (ad esempio via API). Presumiamo che questa modifica sia stata già fatta a mano sul router:
- Dall’esterno:
ssh -p 22222 deploy@mio_dominio
Il router inoltra la 22222 → 22 del server interno.
Nella configurazione di Ansible non cambia nulla per sshd
, rimane in ascolto sulla 22 interna. Il firewall filtra gli accessi, l’utente deploy
può accedere solo dall’external_ip
consentito con chiave, mentre la rete interna può accedere con password.
Se si volesse essere più coerenti, potremmo cambiare la Port
in sshd_config.j2
. Tuttavia, il port forwarding è una soluzione migliore, perché non richiede modifiche al demone SSH e non influisce sugli accessi LAN.
Eseguire il Playbook
Dopo aver preparato tutto:
1 | ansible-playbook -i inventory.ini playbook.yaml |
Se il vostro accesso SSH al server è pronto, Ansible applicherà in pochi secondi tutte le configurazioni descritte. Eseguite nuovamente il playbook in futuro per mantenere lo stato o dopo modifiche alle variabili: le configurazioni saranno sempre coerenti.
Possibili Estensioni e Conclusione
In questo articolo abbiamo replicato l’approccio modulare e differenziato dell’accesso SSH mediante la creazione e applicazione automatizzata di playbook Ansible. Questo consente di scalare la stessa configurazione su più server e di mantenere un controllo centralizzato, coerente e privo di rischi di errore umano.
Per ulteriori estensioni future, si potrebbero creare ruoli separati per la gestione delle chiavi SSH, per l’applicazione di policy di sicurezza più complesse, per l’integrazione con sistemi di gestione delle identità o per la distribuzione automatica di fail2ban con configurazioni personalizzate.
Inoltre, si potrebbe integrare Ansible con strumenti CI/CD per testare automaticamente le configurazioni prima del deployment in produzione.
Con l’introduzione di Ansible, abbiamo compiuto un passo significativo verso l’automazione dell’infrastruttura e la riduzione del carico di lavoro amministrativo, garantendo un accesso SSH sicuro, modulare e facile da mantenere.