Introduzione
La distribuzione di componenti discreti nella configurazione della tua applicazione su nodi diversi è un modo comune per ridurre il carico e iniziare a scalare in modo orizzontale. Un esempio tipico è la configurazione di un database su un server separato dalla tua applicazione. Sebbene ci siano diversi vantaggi con questa configurazione, la connessione tramite una rete comporta una nuova serie di preoccupazioni per la sicurezza.
In questa guida, dimostreremo come configurare un firewall su ciascuno dei tuoi server in una configurazione distribuita. Configureremo la nostra politica per consentire il traffico previsto tra i nostri componenti, mentre verrà negato il resto del traffico.
Puoi anche configurare i Cloud Firewalls di DigitalOcean, che operano come uno strato aggiuntivo esterno ai tuoi server nell’infrastruttura di DigitalOcean. In questo modo, non è necessario configurare un firewall direttamente sui tuoi server.
Per la dimostrazione in questa guida, useremo due server Ubuntu 22.04. Uno avrà un’applicazione web servita con Nginx e l’altro ospiterà il database MySQL per l’applicazione. Anche se utilizzeremo questa configurazione come esempio, dovresti essere in grado di estrapolare le tecniche coinvolte per adattarle ai requisiti del tuo server.
Prerequisiti
Per iniziare, dovrai avere due server Ubuntu 22.04 freschi. Aggiungi un account utente regolare con privilegi sudo
su ciascuno. Per farlo, segui la nostra guida alla configurazione iniziale del server Ubuntu 22.04.
La configurazione dell’applicazione che sicureremo si basa su questa guida. Se desideri seguire quell’esempio, configura i tuoi server di applicazioni e database come indicato in quel tutorial. Altrimenti, puoi usare questo articolo come riferimento generale.
Passaggio 1 — Configurazione di un firewall
Inizierai implementando una configurazione di base del firewall per ciascuno dei tuoi server. La politica che implementeremo adotta un approccio orientato alla sicurezza. Bloccheremo praticamente tutto tranne il traffico SSH e poi faremo dei fori nel firewall per la nostra applicazione specifica.
Questa guida segue la sintassi di iptables
. iptables
è installato automaticamente su Ubuntu 22.04 utilizzando un backend nftables
, quindi non dovresti dover installare pacchetti aggiuntivi.
Utilizzando nano
o il tuo editor di testo preferito, apri il file /etc/iptables/rules.v4
:
- sudo nano /etc/iptables/rules.v4
Incolla la configurazione dalla guida al template del firewall:
*filter
# Consenti tutti i pacchetti in uscita, ma scarta quelli in ingresso e di forwarding per impostazione predefinita
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Catene personalizzate per protocollo
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]
# Traffico UDP accettabile
# Traffico TCP accettabile
-A TCP -p tcp --dport 22 -j ACCEPT
# Traffico ICMP accettabile
# Politica di accettazione boilerplate
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT
# Scarta i pacchetti non validi
-A INPUT -m conntrack --ctstate INVALID -j DROP
# Inoltra il traffico alle catene specifiche del protocollo
## Consenti solo nuove connessioni (quelle stabilite e correlate dovrebbero già essere gestite)
## Per il TCP, consenti solo nuovi pacchetti SYN poiché è l'unico valido
## metodo per stabilire una nuova connessione TCP
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP
# Rifiuta tutto ciò che è giunto a questo punto
## Cerca di essere specifico del protocollo con un messaggio di rifiuto
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# Conferma le modifiche
COMMIT
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
Salva e chiudi il file. Se stai usando nano
, premi Ctrl+X
per uscire, quindi quando richiesto, premi Y
e poi Invio.
Se stai implementando ciò in un ambiente live, non ricaricare ancora le regole del firewall. Caricare il set di regole descritte qui scarterà immediatamente la connessione tra la tua applicazione e il server del database. Dovrai regolare le regole per riflettere le nostre esigenze operative prima di ricaricare.
Passo 2 — Scopri le porte utilizzate dai tuoi servizi
Per consentire la comunicazione tra i componenti, è necessario conoscere le porte di rete utilizzate. È possibile trovare le porte di rete corrette esaminando i file di configurazione, ma un metodo indipendente dall’applicazione per trovare le porte corrette è semplicemente controllare quali servizi stanno ascoltando le connessioni su ciascuna delle nostre macchine.
Puoi utilizzare lo strumento netstat
per scoprirlo. Poiché la tua applicazione comunica solo su IPv4, aggiungeremo l’argomento -4
, ma puoi rimuoverlo se stai usando anche IPv6. Gli altri argomenti di cui hai bisogno per trovare i tuoi servizi in esecuzione sono -p
, -l
, -u
, -n
e -t
, che puoi fornire come -plunt
.
Questi argomenti possono essere suddivisi come segue:
p
: Show the PID and name of the program to which each socket belongs.l
: Show only listening sockets.u
: Show UDP traffic.n
: Show numeric output instead of service names.t
: Show TCP traffic.
- sudo netstat -4plunt
Nel tuo server web, l’output potrebbe apparire così:
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1058/sshd
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 4187/nginx
La prima colonna evidenziata mostra l’indirizzo IP e la porta su cui il servizio evidenziato alla fine della riga sta ascoltando. L’indirizzo speciale 0.0.0.0
significa che il servizio in questione sta ascoltando su tutti gli indirizzi disponibili.
Nel tuo server di database, l’output potrebbe apparire così:
- sudo netstat -4plunt
OutputActive Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1097/sshd
tcp 0 0 192.0.2.30:3306 0.0.0.0:* LISTEN 3112/mysqld
Puoi leggere queste colonne nello stesso modo. In questo esempio, l’indirizzo 192.0.2.30
rappresenta l’indirizzo IP privato del server di database. Nel tutorial preliminare, hai limitato MySQL all’interfaccia privata per motivi di sicurezza.
Prendi nota dei valori che trovi in questa fase. Questi sono i dettagli di rete di cui avrai bisogno per regolare la configurazione del firewall.
Nel tuo server web, devi assicurarti che siano accessibili le seguenti porte:
- Porta 80 su tutti gli indirizzi
- Porta 22 su tutti gli indirizzi (già considerati nelle regole del firewall)
Il tuo server di database dovrebbe garantire che le seguenti porte siano accessibili:
- Porta 3306 sull’indirizzo
192.0.2.30
(o sull’interfaccia associata ad esso) - Porta 22 su tutti gli indirizzi (già considerati nelle regole del firewall)
Passaggio 3 — Regola il Firewall del Server Web
Ora che hai le informazioni sulla porta di cui hai bisogno, regolerai l’insieme di regole del firewall del tuo server web. Apri il file delle regole nel tuo editor con i privilegi di sudo
:
- sudo nano /etc/iptables/rules.v4
Sul server web, è necessario aggiungere la porta 80 alla tua lista di traffico accettabile. Poiché il server è in ascolto su tutti gli indirizzi disponibili — i server web generalmente si aspettano di essere accessibili da qualsiasi luogo — non limiterai la regola per interfaccia o indirizzo di destinazione.
I visitatori del tuo sito web utilizzeranno il protocollo TCP per connettersi. Il tuo framework ha già una catena personalizzata chiamata TCP
per le eccezioni delle applicazioni TCP. Puoi aggiungere la porta 80 a quella catena, subito sotto l’eccezione per la tua porta SSH:
*filter
. . .
# Traffico TCP accettabile
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 80 -j ACCEPT
. . .
Il tuo server web avvierà la connessione con il server del database. Il traffico in uscita non è limitato nel firewall e il traffico in ingresso associato alle connessioni stabilite è consentito, quindi non è necessario aprire altre porte su questo server per consentire questa connessione.
Salva e chiudi il file quando hai finito. Il tuo server web ha ora una policy del firewall che consentirà tutto il traffico legittimo bloccando tutto il resto.
Testa il tuo file di regole per gli errori di sintassi:
- sudo iptables-restore -t < /etc/iptables/rules.v4
Se non vengono visualizzati errori di sintassi, ricarica il firewall per implementare il nuovo set di regole:
- sudo service iptables-persistent reload
Passaggio 4 — Regola il Firewall del Server del Database
Sul tuo server del database, devi consentire l’accesso alla porta 3306
sull’indirizzo IP privato del tuo server. In questo caso, quell’indirizzo era 192.0.2.30
. Puoi limitare l’accesso destinato a questo indirizzo specificamente, o puoi limitare l’accesso corrispondente all’interfaccia a cui è assegnato quell’indirizzo.
Per trovare l’interfaccia di rete associata a quell’indirizzo, esegui ip -4 addr show scope global
:
- ip -4 addr show scope global
Output2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 203.0.113.5/24 brd 104.236.113.255 scope global eth0
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.0.2.30/24 brd 192.0.2.255 scope global eth1
valid_lft forever preferred_lft forever
Le aree evidenziate mostrano che l’interfaccia eth1
è associata a quell’indirizzo.
In seguito, regolerai le regole del firewall sul server del database. Apri il file delle regole con i privilegi sudo
sul tuo server del database.
- sudo nano /etc/iptables/rules.v4
Ancora una volta, aggiornerai la nostra catena TCP
per creare un’eccezione per la connessione tra il tuo server web e quello del database.
Per limitare l’accesso in base all’indirizzo effettivo in questione, aggiungeresti la regola in questo modo:
*filter
. . .
# Traffico TCP accettabile
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -d 192.0.2.30 -j ACCEPT
. . .
Se preferisci consentire l’eccezione in base all’interfaccia che ospita quell’indirizzo, puoi aggiungere una regola simile a questa invece:
*filter
. . .
# Traffico TCP accettabile
-A TCP -p tcp --dport 22 -j ACCEPT
-A TCP -p tcp --dport 3306 -i eth1 -j ACCEPT
. . .
Salva e chiudi il file quando hai finito.
Controlla gli errori di sintassi con questo comando:
- sudo iptables-restore -t < /etc/iptables/rules.v4
Quando sei pronto, ricarica le regole del firewall:
- sudo service iptables-persistent reload
Ora entrambi i tuoi server dovrebbero essere protetti senza limitare il flusso necessario dei dati tra di essi.
Conclusione
Implementare un firewall adeguato dovrebbe sempre far parte del tuo piano di distribuzione durante la configurazione di un’applicazione. Anche se abbiamo dimostrato questa configurazione usando due server con Nginx e MySQL, le tecniche illustrate sopra sono applicabili indipendentemente dalle tue scelte tecnologiche specifiche.
Per saperne di più sui firewall e su iptables
in particolare, dai un’occhiata alle seguenti guide: