Tag

Un po' più di sicurezza!

24 Dicembre 2019 - ultima modifica: 2019-12-26 16:09:27

Al fine di cominciare a mettere in sicurezza l'ambiente, ho lavorato su alcuni aspetti:

  • SSH, autenticazione con chiave pubblica
  • Certificati SSL per HTTP
  • Firewall
  • Accesso autenticato a node-red

SSH

SSH è un protocollo di comunicazione utilizzato tipicamente per implementare un login remoto su una piattaforma attraverso protocolli criptografici. È molto testato ed è uno standard di gestione degli ambienti UNIX.

Normalmente il processo di autenticazione si basa su quello del sistema UNIX su cui gira, ovvero un semplice nome utente + password. Una utile estensione a questo sistema è quello di autenticazione con chiave pubblica che, detto in breve, permette di effettuare l'autenticazione di un utente attraverso uno scambio di certificati, ovvero blocchi di dati che vengono riconosciuti come validi in base a un algoritmo criptografico che assicura l'impossibilità di falsificarli. Questo sistema permette sia di effettuare il login sul sistema remoto in maniera automatica, sia di ottenere un livello di sicurezza superiore a quello di utente+password.

Lavorando da computer Windows può essere complicato far funzionare una chiave prodotta in ambito Linux con Putty e Pageant, due dei software SSH più usati in ambiento Windows. Nel mio caso, la chiave privata non veniva riconosciuta, e la soluzione consisteva nel produrre le chiavi sul server Linux specificando l'algoritmo di message authentication code in questo modo:

$ ssh-keygen -m PEM -t rsa -b 4096 -C "your_email@example.com"

SSL

Il protocollo di comunicazione HTTP è un protocollo non sicuro, che passa i dati in chiaro attraverso la rete (password e dati sensibili inclusi), per questo è sempre più consigliato usare HTTPS. Per fare questo occorrono dei certificati crittografici SSL, firmati (ovvero convalidati criptograficamente) da delle autorità riconosciute, i quali però sono tipicamente a pagamento e forniti solo in seguito a delle verifiche manuali da parte di queste entità. Let's Encrypt, al contrario, è un'organizzazione che permette la creazione di certificati in maniera automatica e gratuita.

L'utilizzo è estremamente semplice e ben documentato nella guida, e basta davvero seguire i passi della stessa per ottenere il risultato voluto. Il mezzo utilizzato è quello di certbot, un programma che si occupa della comunicazione con i server di Let's Encrypt, permettendo a essi di verificare che l'utente ha il controllo dei siti web che dichiara e gestendo inoltre il rinnovo automatico degli stessi certificati SSL, automatizzando un'operazione tipicamente delicata.

Nel suo utilizzo di default, i certificati prodotti sono puntuali per tutti i sottodomini (e.g.: www.foxthesystem.space, blog.foxthesystem.space, ecc...) e non wildcard, ovvero che coprono qualunque sottodominio (tipicamente scritto come *.foxthesystem.space). Questa configurazione è quella tipicamente più comoda e corretta per la maggior parte dei server, dove i sottodomini vengono aggiunti o tolti come operazioni non di routine.

L'aggiunta o modifica degli stessi è per altro estremamente semplice da riga di comando:

$ certbot certonly -d foxthesystem.space -d blog.foxthesystem.space \
-d node-red.foxthesystem.space

Nell'esempio qui sopra dovevo aggiungere il sottodominio node-red, e l'ho fatto in questo modo. Ogni volta andranno indicati tutti i sottodomini che si desiderano nel certificato finale: sarà poi certbot a fornire un report di quali domini stanno venendo aggiunti o tolti e come.

Le modifiche alla configurazione di nginx, il server web che utilizzo per fornire tutti i contenuti, sono state minime, e possono essere lasciate gestire da Certbot, il quale non fa altro che impostare i file di certificato corretti e forzare la redirezione da HTTP ad HTTPS:

listen 443 ssl;

# RSA certificate
ssl_certificate /etc/letsencrypt/live/foxthesystem.space/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/foxthesystem.space/privkey.pem;

include /etc/letsencrypt/options-ssl-nginx.conf;

# Redirect non-https traffic to https
if ($scheme != "https") {
    return 301 https://$host$request_uri;
}

Firewall

Su Debian Buster il sistema per la gestione di pacchetti e connessioni consigliato è nftables. Sempre nella guida di Debian Buster si consiglia di appoggiarsi su un programma di più alto livello, firewalld, al fine di configurare in modo semplice lo stesso. Considerando che, con una configurazione pressoché nulla, il listato di regole via nftables risulta lungo 452 linee mentre la configurazione di firewalld è lunga 6, in effetti mi sento di consigliarlo anche io.

Al momento, l'unica modifica è stata di sovrascrivere la zona public in /etc/firewalld/zones cambiando i servizi raggiungibili e aggiungendo http e https:

<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on
networks to not harm your computer. Only selected incoming connections
are accepted.</description> <service name="ssh"/> <service name="dhcpv6-client"/> <service name="http"/> <service name="https"/> </zone>

L'unico problema avuto è stato di dover effettuare un reboot della macchina una volta cambiato il sistema di backend di firewalld da iptables a nftables. Probabilmente utilizzando la giusta combinazione di riavvio dei servizi e cambi di configurazione avrei raggiunto lo stesso risultato senza un riavvio, ma per il sistema che gestisco non mi dava problemi al momento.

Node-RED

Uno dei motivi per cui ho installato questo sistema è di poter riprendermi il controllo della gestione domotica della casa, e la scelta dello strumento è caduta su Node-RED. Al fine di potervi accedere da remoto senza problemi ho seguito una configurazione piuttosto standard.

L'unico server che risponde alle richieste HTTP provenienti da internet è nginx. Questo, come configurato sopra, redirige tutte le richieste HTTP su HTTPS: in questo modo sono sicuro che non passi traffico in chiaro.

Su nginx ho configurato un virtual host, ovvero ho fornito una configurazione per rispondere alle richieste che non dipende dal server fisico che risponde (sempre e solo il mio Raspberry Pi), bensì dall'host utilizzato. Ad esempio, sia https://blog.foxthesystem.space che https://node-red.foxthesystem.space vengono serviti dalla stessa macchina, ma poiché il nome dell'host è diverso, nginx risponde con siti diversi.

Infine, ho configurato l'host node-red.foxthesystem.space attraverso il modulo http_proxy, il cui scopo è rigirare le richieste http, eventualmente modificandole, a un altro server web. Questo genere di comportamento per un server web, che "rigira" le richieste a un server nascosto dietro, viene chiamato reverse proxy. In tal modo tutte le richieste vengono filtrate e bonificate da nginx, un server robusto e di produzione su cui posso eventualmente aggiungere ulteriori filtri, controlli e limiti, e non dal server di Node-RED direttamente.

Lo schema finale è il seguente:

Tutto questo ha portato a una configurazione del genere su nginx:

server {
        listen 80;
        listen [::]:80;

        server_name node-red.foxthesystem.space;

        listen 443 ssl;

        # RSA certificate
        ssl_certificate /etc/letsencrypt/live/foxthesystem.space/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/foxthesystem.space/privkey.pem;

        include /etc/letsencrypt/options-ssl-nginx.conf;

        # Redirect non-https traffic to https
        if ($scheme != "https") {
                return 301 https://$host$request_uri;
        }

        location / {
                proxy_pass http://127.0.0.1:1880/;
        }

        location /comms {
                proxy_pass http://127.0.0.1:1880;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_read_timeout 86400;
        }
}

Le parti interessanti di configurazione sono le due location:

  • La prima attiva il reverse proxy attraverso la direttiva proxy_pass al server Node-RED
  • La seconda serve perché Node-RED utilizza un WebSocket sicuro al fine di mantenere sincronizzata la situazione tra interfaccia client web e stato del server, e queste righe sono necessarie per il suo funzionamento