VPS e sicurezza
“Sicurezza” non è una singola azione ma è un modo di pensare, di porti dinanzi alla realtà che ti circonda. La sicurezza è prima di tutto un abitudine. Bisogna lavorare ogni giorno sulla mentalità.
Era il lontano 2016 e mi presentavo come ospite e relatore ad un Linux day. Il mio talk era incentrato su come muovere i primi passi e mettere in sicurezza un VPS in quanto stava diventando molto semplice acquistare un istanza a buon mercato (con 2 o 3 € al mese).
È passato “qualche” anno ma le persone mi pongono ancora questa domanda e ho quindi deciso di riproporre questo tema con qualche attualizzazione.
Andiamo per punti:
- avere una shell più sicura;
- blindiamo il VPS;
- IDS e IPS;
Una shell sicura
Appena installato openssh (server) avremo la possibilità di telecontrollare della nostra istanza. In passato, il vendor ti inviava una email di benvenuto con la password scritta in chiaro, ma oggi i provider più attenti ti permettono di caricare già in fase di installazione la chiave tua pubblica.
Cosa è una chiave pubblica?
Uno dei metodi ritenuti più sicuri per accedere in ssh ai server è mediante l’utilizzo di una coppia di chiavi (certificati) con crittografia asimmetrica . In fase di creazione viene definita una coppia di chiavi chiamate pubblica e privata. La chiave privata deve restare nostra e va conservata gelosamente, mentre la chiave pubblica deve essere condivisa con gli altri.
Riassumendo: io ho una coppia di chiavi e fornisco al provider la mia chiave pubblica. La privata la userò per connettermi al server.
Come si genera una coppia di chiavi?
Nel caso in cui non avessimo già una coppia di certificati è possibile generarli in formato RSA e lunghezza 4 KB.
$ ssh-keygen -t RSA -b 4096
sui sistemi Unix-like viene proposto come nome id_rsa e id_rsa.pub e verranno salvati nella cartella nascosta .ssh nella nostra home.
Fatto questo basterà copiare il contenuto di id_rsa.pub in /root/.ssh/authorized_keys per l’utente root oppure /home/marvin/.ssh/authorized_keys per l’utente marvin.
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYVWGspW9NMvKPM5XgQOcMvEyHt57yu7aXVLIpZxXqfHtRDrrYxXREKxKfUM4mu3N7V6TkpikzuwGD5IOC9mVzxzrqx8c3ORfXlX+0VIgWr4l5Q3u9vU2QOcWDsO1B7ozlZ96JhafGct6V9Igl90dMmXdO6i0uqP1ksUHLVzadCoxvcix4RZN6vU2BIyAS0rvJtUsuE45IZh+izIIphi0k1fp5XLq+dDC9ZDdHukWfT8rCsqk7hRJC2qUTFoXKpHeqG/OYYjiA8iRV6G+WPP5U3aFn2h8mYpLvsa6d4UrLUdKQIpTMOKr1K75sPUJ7Zccy32boxwEBlExGo826remb marvin
Configuriamo OpenSSH server
All’interno del file /etc/ssh/sshd_config andremo ad impostare una serie di obblighi e limitazioni.
...
Protocol 2
...
PermitEmptyPasswords no
...
DebianBanner no
...
Ho preso come esempio un VPS Debian ma il concetto vale per tutti i sistemi Linux.
Protocol 2 obblica il server a richiedere al client di utilizzare la versione 2;
PermitEmptyPasswords no non permette la login con password “vuote”;
DebianBanner no rimuove dal banner di presentazione tutta una serie di informazioni che potrebbero essere succose per un potenziale attaccante.
Prima del tuning
$ sudo nmap -sV -p 22 10.18.61.60
Starting Nmap 7.93 ( https://nmap.org ) at 2022-11-18 12:22 CET
Nmap scan report for 10.18.61.60
Host is up (0.0045s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
MAC Address: 08:00:27:1B:83:68 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.55 seconds
dopo il tuning
$ sudo nmap -sV -p 22 10.18.61.60
Starting Nmap 7.93 ( https://nmap.org ) at 2022-11-18 12:26 CET
Nmap scan report for 10.18.61.60
Host is up (0.0049s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 (protocol 2.0)
MAC Address: 08:00:27:1B:83:68 (Oracle VirtualBox virtual NIC)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.58 seconds
Extra
Una volta certi di poterci autenticare con la chiave RSA è consigliato limitare la possibilità di fare login con la password.
Sempre all’interno del file /etc/ssh/sshd_config decommentiamo la riga PermitRootLogin prohibit-password.
NB: in alcune distribuzioni potresti trovare PermitRootLogin without-password.
Configuriamo Iptables
Iptables è il firewall da linea di comando configurato in modo predefinito su molte distribuzioni Linux. Anche se ci sono moltissimi wrapper per agevolarne l’utilizzo, credo che conoscere il suo funzionamento sia imprescindibile; per cui oggi vedremo come funziona e come configurarlo.
XXXXXXXXXXXXXXXXXX
XXX Network XXX
XXXXXXXXXXXXXXXXXX
+
|
v
+-------------+ +------------------+
|table: filter| <---+ | table: nat |
|chain: INPUT | | | chain: PREROUTING|
+-----+-------+ | +--------+---------+
| | |
v | v
[local process] | **************** +--------------+
| +---------+ Routing decision +------> |table: filter |
v **************** |chain: FORWARD|
**************** +------+-------+
Routing decision |
**************** |
| |
v **************** |
+-------------+ +------> Routing decision <---------------+
|table: nat | | ****************
|chain: OUTPUT| | +
+-----+-------+ | |
| | v
v | +-------------------+
+--------------+ | | table: nat |
|table: filter | +----+ | chain: POSTROUTING|
|chain: OUTPUT | +--------+----------+
+--------------+ |
v
XXXXXXXXXXXXXXXXXX
XXX Network XXX
XXXXXXXXXXXXXXXXXX
Parliamo di un firewall implementato a livello kernel che ci permette di analizzare e filtrare il traffico. Questo software mette in pratica delle regole ben definite e sequenziali che vengono dette catene. Questo significa che le regole vengono valutate dalla prima all’ultima ma appena c’è un match, questa viene applicata e non verrà valutata nessun’altra regola.
Nella pratica questo si traduce che se, per caso, come prima regola io inserisco un DROP ALL non avrò nessuna speranza di vedere accettato un pacchetto.
Esistono cinque tipi di catene anche se le più importanti sono due: INPUT e OUTPUT.
- INPUT: valuta i pacchetti in entrata;
- OTPUT: valuta i pacchetti in uscita.
Non dimentichiamo le catene di: FORWARD, valuta i pacchetti che sono diretti ad un altro host della rete (fondamentale nei router); PREROUTING, valuta i pacchetti in entrata con caratteristiche ben precise; POSTROUTING, valuta i pacchetti in uscita dal sistema ma solamente dopo che è stato valutato e definito l’instradamento.
Ogni catena fa parte a sua volta di una tabella; le catene INPUT, OUTPUT e FORWARD fanno parte della tabella filter, mentre le catene PREROUTING E POSTROUTING fanno parte della tabella nat. Ultima, ma non meno importante è la tabella mangle che viene usata in situazioni specifiche.
Se non specificato iptables mostrerà la tabelli filters
# iptables -L
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere
se invece volessimo vedere la tabella nat
# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
per avere in output una catena precisa
# iptables -L INPUT
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere
REJECT all -- anywhere 127.0.0.0/8 reject-with icmp-port-unreachable
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
per fare pulizia della tabella filter
# iptables -F
# iptables -X
Ora parliamo delle connessioni che possono essere: NEW, nuova connessione; ESTABLISHED, connessione esistente; RELATED, una nuova connessione associata ad una connessione già esistente (es: FTP che stabilisce almeno due connessioni; una per scambiare le informazioni, una di controllo e almeno una per il trasferimento.
# iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
In questo esempio diciamo a iptables per accettare le nuove connessioni in entrata sulla porta 22. -A: (append) se sono presenti altre regole questa regola inseriscila alla fine;
INPUT: la catena a cui la regola fa riferimento;-p: il protocollo (TCP, UDP, …);
–dport: la porta di destinazione;
-m state –state NEW: connessioni nuove;
-j: (jump) cosa fare quando si rileva un pacchetto che rispecchia le caratteristiche precedenti (nel nostro caso ACCEPT).
Come prassi, dopo aver definito tutte le aperture va inserita una regola di DROP o REJECT che sta a significare che “tutto ciò che non è espressamente consentito è vietato”. In alternativa è possibile definire una policy di default che si applicherà come ultima regola nella rispettiva catena:
iptables -P INPUT DROP
Preferisco questa seconda modalità; in quanto, nel caso in cui io debba aggiungere una regola in fondo alla catena, non dovrò intervenire su quella di drop.
Iptables è molto completo e complesso, per cui se questa piccola infarinatura ti ha incuriosito ti invito ad approfondire.
Automatizziamo
Sulle distribuzioni Debian based possiamo creare uno script che verrà invocato prima di abilitare le interfacce di rete
/etc/network/if-pre-up.d/iptables
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.rules
/sbin/ip6tables-restore < /etc/iptablesv6.rules
molto semplicemente vengono caricate le configurazioni per Iptables sia per l’IPv4 che per l’IPv6
/etc/iptables.rules
*filter
-F INPUT
-F OUTPUT
-F FORWARD
-F
-X
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
-P FORWARD DROP
-P INPUT DROP
COMMIT
/etc/iptablesv6.rules
*filter
-F INPUT
-F OUTPUT
-F FORWARD
-F
-X
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d ::1/128 -j REJECT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
-P FORWARD DROP
-P INPUT DROP
COMMIT
Nell’esempio permetteremo tutto il traffico in uscita limitanto il traffino in entrata per la sola porta 22. Nel caso in cui dovessimo esporre altri servizi basterà aggiungere nuove regole di “ACCEPT”.
Teniamo tutto sotto controllo
Come sistema di IDP (Intrusion Prevention System) e IDS (Intrusion Detection System) utilizzeremo Crowdsec . Ho già parlato molto di Crowdsec in passato e ho descritto diversi scenari per cui, se non lo avete già fatto, vi invito a leggere la guida .
Quello che dobbiamo tener conto è che installando il bouncer di iptables quest’ultimo si integrerà e posizionerà una sua regola all’interno della tabella di INPUT.
# iptables -L INPUT
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP all -- anywhere anywhere match-set crowdsec-blacklists src
....
Le opinioni in quanto tali sono opinabili e nulla ti vieta di approfondire l’argomento.
Risorse: