Openhab con NGINX (reverseproxy)

Configurazione Proxy NGINX per Openhab

L'obiettivo è configurare una connessione sicura (HTTPS) su Openhab per permettere l'esposizione diretta su internet del vostro Openhab senza usare il servizio Cloud.

Condizione essenziale è avere un IP pubblico fisso o una configurazione DDNS e permettere il portforwarding della porta 443 (o quella che volete configurare) sul vostro router verso l'host di openhab.

Inoltre servirà avere dei certificati digitali che possono essere autoprodotti (selfsigned) oppure possono essere generati con il servizio di Letsencrypt che genererà certificati gratuiti della durata di 3 mesi ma con una CA riconosciuta dai principali browser.

Configurazione DNS

Per usare un DDNS ho trovato comodo DuckDNS, creare un account e registrare il nome host corrispondente al vostro host.

Alla pagina DuckDNS installa il client adatto al tuo host.

Generazione dei certificati (sceglierne uno)

Certificati SELFSIGNED

Installare OpenSSL:

sudo apt-get install openssl

Una volta completato, è necessario creare una directory in cui posizionare i nostri certificati:

sudo mkdir -p /etc/ssl/certs

Generiamo con OpenSSL una chiave RSA lunga 2048 bit e un certificato valido per un anno (ci metterà diverso tempo):

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/openhab.key -out /etc/ssl/certs/openhab.crt

Ti verranno chieste alcune informazioni che dovrai compilare per il certificato, quando ti verrà richiesto un nome comune , puoi inserire il tuo indirizzo IP: Nome comune (es. FQDN del server o TUO nome) []: xx.xx. xx.xx

Rinominare il certificato in .pem

sudo mv /etc/ssl/certs/openhab.crt /etc/ssl/certs/openhab.pem

Certificati Letsencrypt (https://letsencrypt.org/)

Usando il servizio DDNS di DuckDns abbiamo bisogno di creare i certificati digitali con il metodo trovato da jmorahan, quindi creiamo questo due script nella HOME di openhabian sostituendo il valore del tuo TOKEN preso da DuckDns:

auth.sh

#!/bin/bash 
DUCKDNS_TOKEN="your_token_here" 
[[ "$(curl -s "https://www.duckdns.org/update?domains=${CERTBOT_DOMAIN%.duckdns.org}&token=${DUCKDNS_TOKEN}&txt=${CERTBOT_VALIDATION}")" = "OK" ]]

cleanup.sh

#!/bin/bash 
DUCKDNS_TOKEN="your_token_here" 
[[ "$(curl -s "https://www.duckdns.org/update?domains=${CERTBOT_DOMAIN%.duckdns.org}&token=${DUCKDNS_TOKEN}&txt=${CERTBOT_VALIDATION}&clear=true")" = "OK" ]]

Concediamo le autorizzazioni di esecuzioni:

chmod +x auth.sh cleanup.sh

Procediamo con il processo di verifica lanciando:

sudo certbot certonly --manual --preferred-challenges dns --manual-auth-hook ./auth.sh --manual-cleanup-hook ./cleanup.sh

I certificati avranno scadenza 90 giorni, potranno essere rinnovati con il comando:

certbot renew --pre-hook "service nginx stop" --post-hook "service nginx start"

Installazione e configurazione NGINX

Usiamo lo strumento di configurazione di Openhab:

sudo openhabian-config

Scegliere una USERNAME e una PASSWORD e concludere la procedura con l'installazione automatica dei pacchetti necessari.

Il file di configurazione che ne consegue dovrebbe essere una cosa del genere sotto il path /etc/nginx/sites-enabled/openhab (caso SELFSIGNED):

    #################################
    # openHABian NGINX Confiuration #
    #################################

    ## Redirection
    server {
       listen                          80;
       server_name                     IPPUBBLICO;
       return 301                      https://$server_name$request_uri;
    }

    ## Reverse Proxy to openHAB
    server {
    #    listen                          80;
       listen                          443 ssl;
       server_name                     IPPUBBLICO;
       add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

        # Cross-Origin Resource Sharing.
        # add_header 'Access-Control-Allow-Origin' 'http://localhost:8080/rest';
        add_header 'Access-Control-Allow_Credentials' 'true';
        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

    ## Secure Certificate Locations
       ssl_certificate                 /etc/ssl/certs/openhab.pem;
       ssl_certificate_key             /etc/ssl/private/openhab.key;

        location / {
            proxy_pass                              http://localhost:8080/;
    #        proxy_buffering                         off;  # openHAB supports non-buffering specifically for SSEs now
            proxy_set_header Host                   $http_host;
            proxy_set_header X-Real-IP              $remote_addr;
            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto      $scheme;

    # Password Protection
           auth_basic                              "Username and Password Required";
           auth_basic_user_file                    /etc/nginx/.htpasswd;
        }

    }

Il file di configurazione che ne consegue dovrebbe essere una cosa del genere sotto il path /etc/nginx/sites-enabled/openhab (caso letsencrypt):

#################################
# openHABian NGINX Confiuration #
#################################

## Redirection
 server {
#   listen                          80;
   server_name                     mydomain.duckdns.org;
   return 301                      https://$server_name$request_uri;
 }

## Reverse Proxy to openHAB
server {
#    listen                          80;
   listen                          443 ssl;
    server_name                     mydomain.duckdns.org;
   add_header                      Strict-Transport-Security "max-age=31536000; includeSubDomains";

    # Cross-Origin Resource Sharing.
    add_header 'Access-Control-Allow-Origin' '*' always; # make sure that also a 400 response works
    add_header 'Access-Control-Allow_Credentials' 'true' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range' always;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH' always;

## Secure Certificate Locations
   ssl_certificate                 /etc/letsencrypt/live/mydomain.duckdns.org/fullchain.pem;
   ssl_certificate_key             /etc/letsencrypt/live/mydomain.duckdns.org/privkey.pem;

    location / {
        proxy_pass                              http://localhost:8080/;
#        proxy_buffering                         off;           # openHAB supports non-buffering specifically for SSEs now
        proxy_set_header Host                   $http_host;
        proxy_set_header X-Real-IP              $remote_addr;
        proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto      $scheme;
        proxy_read_timeout 3600;                                # to avoid "offline" error msgs in Basic UI

## Password Protection
       auth_basic                              "Username and Password Required";
       auth_basic_user_file                    /etc/nginx/.htpasswd;
    }

## Let's Encrypt webroot location
   location /.well-known/acme-challenge/ {
       root                                    /var/www/mydomain.duckdns.org;
   }
}

# vim: filetype=conf

Eventuali problemi

Se la chiave ssl_dhparam non è presente generarla:

sudo openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem

Verifica impostazioni di sicurezza

Puoi testare le tue impostazioni di sicurezza su SSLLABS (assicurati di selezionare "Non mostrare i risultati nelle schede" se non desideri che il tuo dominio venga visualizzato)

Se vogliamo raggiungere migliori livelli di sicurezza dobbiamo generare delle chiavi più sicure:

sudo mkdir -p /etc/nginx/ssl
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096

Inseriamo nella parte degli SSL certificate queste direttive (assicurati che siano sopra le location)

ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers       on;
ssl_dhparam                     /etc/nginx/ssl/dhparam.pem;
ssl_ciphers                     ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH;
ssl_session_timeout             1d;
ssl_session_cache               shared:SSL:10m;
keepalive_timeout               70;

Gestione NGINX

Per aggiungere un nuovo utente:

sudo apt-get install apache2-utils

sudo htpasswd -c /etc/nginx/.htpasswd {username}

Per rimuovere un utente:

sudo htpasswd -D /etc/nginx/.htpasswd {username}

Per rilanciare il NGINX:

sudo systemctl restart nginx.service    

Link utili

Massimiliano Casini (Coordinatore tecnico Service Assurance presso www.kenamobile.it)
Sono cresciuto nel modo IT di aziende ad alto contenuto tecnologico e da sempre ho acquisto esperienze nel settore della domotica e sicurezza residenziale. Negli ultimi dieci anni ho acquisito competenze specifiche nel campo delle TLC in particolare sulla rete GSM e sui protocolli ad essa collegati.