Come realizzare un semplice VPN server su AWS
Realizzare un semplice VPN server su AWS può aiutarci a risolvere qualche piccolo problemino quotidiano: mi è servito, ad esempio, quando ho preso la rivoluzionaria decisione di rinunciare alla linea ADSL casalinga a favore di una ben più performante connettività LTE 4G. Vivendo in una zona non ancora raggiunta (e chissà quando accadrà) dalla fibra, ho deciso infatti di approfittare di una delle offerte “a tanti GB inclusi” superando di gran lunga le prestazioni della tradizionale linea di terra e risparmiando anche un pò ogni mese.
Si è però subito posto un problema: come noto, le connettività mobile sono quasi sempre di tipo NAT, cioè l’indirizzo IP pubblico che si usa per la propria presenza su internet è in realtà condiviso tra più dispositivi e utenti. Di per se questo tipo di connettività non rappresenta un limite per accedere ai servizi web ma, al contrario, rende di fatto impossibile contattare il proprio dispositivo 4G da un altro dispositivo posto sulla rete pubblica. Come posso quindi connettermi, ovunque mi trovi, ai dispositivi della mia LAN casalinga se NATtati dietro una connettività mobile?
Per questo motivo e per altri che vedremo, può far comodo realizzare una semplice rete VPN.
Il cuore di una rete VPN è un server centrale, raggiungibile da tutti i client che saranno autenticati dallo stesso. Una volta connessi, i dispositivi posti sulla VPN potranno dialogare in modo sicuro tra loro superando le limitazioni dei livelli di rete sottostanti, come se si trovassero su un’unica – virtuale e privata – LAN.
Vediamo come realizzare un semplice VPN server su AWS utilizzando OpenVPN. Il diagramma che seguiremo è il seguente.
Realizziamo il server
Avremo bisogno di un’istanza EC2: per rapidità useremo un’istanza On-Demand. Ci occuperemo di installare e configurare tutto il software necessario; successivamente andremo a realizzare una nostra AMI in modo da poter ricreare rapidamente il nostro server.
Avviamo la nostra EC2 basata su Ubuntu Server 18.04 LTS: sarà molto semplice configurare il nostro server OpenVPN senza aver la necessità di utilizzare AMI del MarketPlace.
Un’istanza t2.micro Free-Tier è assolutamente adatta alla scopo; configuriamo un Security Group in modo che sia raggiungibile tramite protocollo SSH (TCP/22).
Una volta avviata, colleghiamoci alla console utilizzando l’utente ubuntu.
ssh -i <keypairfile.pem> ubuntu@<ec2_public_ip>
Aggiorniamo subito la nostra istanza e recuperiamo lo script per l’installazione di OpenVPN
sudo apt-get update
sudo apt-get upgrade -y
wget https://git.io/vpn -O openvpn-install.sh
chmod +x openvpn-install.sh
A questo punto, prima di lanciare lo script di installazione è necessario fare una premessa: per contattare il nostro server OpenVPN sarà necessario fornire ai client l’indirizzo di quest’ultimo. L’indirizzo può essere un IP statico oppure un record DNS. La soluzione più semplice consiste nell’assegnare un Elastic IP alla nostra istanza. Tuttavia ho preferito rendere la nostra configurazione indipendente anche dall’indirizzo IP assegnato al server, utilizzando Route 53 di AWS per definire un hostname pubblico a cui i nostri client faranno riferimento.
Eseguiamo lo script
sudo ./openvpn-install.sh
La prima cosa che ci verrà chiesta è proprio l’indirizzo del server OpenVPN, che lo script tenta di determinare automaticamente. Se abbiamo assegnato un Elastic IP alla nostra istanza AWS, confermiamo l’indirizzo. Se invece intendiamo utilizzare un host in una zona Route 53, specifichiamo il nome host completo. Nel mio caso: vpn.aws.gotocloud.it.
Confermiamo poi tutte le successive richieste con i valori di default proposti e avviamo l’installazione.
Al termine dell’installazione un messaggio ci ricorda che il file di configurazione ed il certificato da utilizzare su uno dei nostri client (laptop, smartphone, raspberry, etc..) è disponibile in /home/ubuntu/client.ovpn
Assicuriamoci di avviare OpenVPN.
sudo systemctl enable openvpn
sudo systemctl start openvpn
Alcune informazioni: lo script crea la rete 10.8.0.0/24 ed assegna l’indirizzo ip 10.8.0.1 al nostro OpenVPN server. Gli indirizzi IP successivi saranno assegnato ai client. Utilizzeremo questi indirizzo per comunicare tramite la nostra VPN.
OpenVPN utilizza di default la porta UDP 1194. Dobbiamo quindi provvedere a modificare il SecurityGroup dell’instanza EC2 su cui stiamo lavorando aggiungendo questa policy.
Se abbiamo deciso di utilizzare un indirizzo Elastic IP, la configurazione del server è terminata. Se invece abbiamo deciso di utilizzare un nome host di una zona DNS ospitata su Route 53, dovremo fare in modo di aggiornare il relativo record A con l’indirizzo pubblico che AWS ha assegnato alla nostra istanza, ripetendo l’operazione ad ogni avvio. Per farlo utilizziamo uno script Python.
Integrazione con Route53
Per prima cosa installiamo le dipendenze tra cui Boto3 che è il SDK di AWS per Python. Dato che lo script verrà eseguito come root all’avvio, anche le relative dipendenze vanno installate nell’ambiente di root.
sudo apt install python-pip -y
sudo su
pip install boto3
pip install requests
exit
Andiamo ora a creare lo script “update_route53_zone.py” con il contenuto seguente.
import requests
import json
import boto3
META_DATA_URL = 'http://169.254.169.254/latest/meta-data/public-ipv4'
# Route53 host to be updated
zone_name = "aws.gotocloud.it."
A_record_name = "vpn"
# Get Public IP Address
r = requests.get(META_DATA_URL)
public_ip = r.text
# Prepare data for Route53 changes
data = {}
data['Comment'] = "Update openvpn DNS record"
data['Changes'] = []
data['Changes'].append({
'Action': 'UPSERT',
'ResourceRecordSet': {
"Name": A_record_name + '.' + zone_name,
"Type": "A",
"TTL": 60,
"ResourceRecords": [
{
"Value": public_ip
}
]
}
})
# Get hosted zone list to find zone_id
client = boto3.client('route53')
zones = client.list_hosted_zones()
zone_id = ''
for z in zones['HostedZones']:
if (z['Name'] == zone_name):
zone_id = z['Id']
break
# Change Route53 record
if zone_id != '':
response = client.change_resource_record_sets(HostedZoneId=zone_id, ChangeBatch=data)
print(response)
Lo script ottiene l’indirizzo IP pubblico dell’instanza e aggiorna la zona DNS specificata inserendo o aggiornando l’host indicato come “A_record_name”. Per poter procedere all’aggiornamento della zona DNS, è necessario assegnare all’instanza un ruolo dotato delle policy corrette.
Le API di Route 53 utilizzate sono “ChangeResourceRecordSets” e “ListHostedZones”.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "route53:ListHostedZones",
"Resource": "*"
}
]
}
Una volta assegnato il ruolo, eseguendo lo script come root, verrà creato o aggiornato il record DNS. Per fare in modo che questo avvenga ad ogni avvio, utilizziamo CRON.
sudo cp update_route53_zone.py /bin
sudo crontab -e
Andremo ad inserire:
@reboot python /bin/update_route53_zone.py &
I client
Per aggiungere utenti/dispositivi alla nostra VPN utilizziamo ancora lo script openvpn-install.sh. E’ consigliato creare un utente per ogni dispositivo che andremo a connettere alla nostra VPN specificandone un nome che ci permetta di riconoscerlo facilmente. Inoltre lo script può essere utilizzato per revocare un utente.
E’ il momento del client: nella sezione Community Download di OpenVPN troviamo i client per varie versioni di Windows. Su MacOS consiglio di utilizzare TunnelBlick. IOS e Android prevedono l’app ufficiale di OpenVPN nei rispettivi store. Per configurare Raspbian (Raspberry PI) ho invece seguito le indicazioni presenti in questo thread.
Indipendentemente dal client utilizzato, ci servirà il file OVPN generato dallo script di configurazione che dovremo quindi trasferire dal server (SFTP). Normalmente la configurazione del client è abbastanza semplice: con MacOS e TunnelBlick, ad esempio, basterà un doppio click sul file OVPN perchè questo venga importato. Rimarrà solo da avviare la VPN.
Se tutto è andato per il meglio, dal nostro dispositivo in VPN sarà possibile pingare con successo l’indirizzo del nostro VPN server 10.8.0.1.
Raspberry PI
Per il setup casalingo ho invece scelto di installare il client OpenVPN su un dispositivo Raspberry PI da utilizzare poi anche come bridge verso gli altri dispositivi presenti sulla mia LAN. L’installazione su Raspbian è semplice.
sudo apt-get install openvpn
Andremo a copiare il file OVPN nella directory corretta, cambiando l’estensione in .CONF. Procederemo poi ad avviare il servizio.
sudo cp pi4.ovpn /etc/openvpn/pi4.conf
sudo systemctl enable openvpn
sudo systemctl start openvpn
Il nostro PI è ora connesso alla VPN e lo potremo verificare pingando, anche in queso caso, l’indirizzo IP 10.8.0.1 del server. Al contrario, se il PI ospita per esempio un repository per i nostri file (Owncloud) , questo sarà raggiungibile da tutti i dispositivi connessi in VPN (ad esempio il nostro smartphone) tramite l’indirizzo IP assegnato al PI sulla rete 10.8.0.0/24
Se vogliamo inoltre che il PI si comporti da VPN gateway, pubblicando servizi ospitati sul nostra LAN da altri dispositivi (ad esempio, il modulo IP della nostra centrale antifurto) potremo utilizzare un reverse proxy come NGINX. Un esempio di configurazione è il seguente: tutte le richieste pervenute al nostro PI sulla porta 10000 vengono girate sull’indirizzo LAN della nostra centrale antifurto, rendendola di fatto accessibile tramite VPN da qualunque dispositivo connesso alla stessa.
stream {
upstream paradox {
server 192.168.22.3:10000;
}
server {
listen 10000;
proxy_pass paradox;
}
}
Navigazione tramite VPN
Se avete mantenuto la configurazione di default così come indicato in questo articolo, vi sarete resi conto che, una volta avviata la VPN dal nostro client (ad esempio dal nostro laptop), questa viene utilizzata anche per la navigazione WEB: in pratica il nostro server EC2 si comporta come nostro personale gateway per andare sulla rete pubblica. Di conseguenza ci presenteremo su internet con l’indirizzo IP del server OpenVPN nella Region AWS dove è stato creato. Possiamo scegliere in che parte del mondo far finta di essere, che è poi una delle prerogative dei tanti servizi VPN a pagamento!
Se invece vogliamo evitare questo comportamento e, al contrario, intendiamo utilizzare la nostra VPN solo per raggiungere i dispositivi connessi alla stessa, dovremo modificare la configurazione di OpenVPN.
Modifichiamo il file /etc/openvpn/server/server.conf e andiamo a commentare queste direttive.
#push "redirect-gateway def1 bypass-dhcp"
#push "dhcp-option DNS 1.1.1.1"
#push "dhcp-option DNS 1.0.0.1"
Ora riavviamo il server VPN e il nostro traffico web non sarà più incanalato tramite VPN.
Conclusioni
Esistono moltissime alternative alla soluzione proposta per risolvere i problemi di raggiungibilità dietro NAT, connessione sicura di più dispositivi over internet e navigazione originata da una region diversa dalla nostra. La soluzione OpenVPN/AWS ha però diversi vantaggi. Vediamoli.
- OpenVPN è gratuito ed esistono client per tutti i principali sistemi operativi
- Utilizzando AWS è possibile creare rapidamente il proprio OpenVPN server in diverse Region del mondo (US/EU/etc..) utilizzando CloudFormation.
- La configurazione è semplice e personalizzabile secondo le proprie esigenze
- Costa meno di altri servizi VPN: pagheremo la sola istanza EC2 (on-demand, reserved o addirittura Spot se possiamo tollerare interruzioni del servizio)
- Non ha limiti di utilizzo
- E’ personale e autogestito: nessun log o dato fornito a un provider VPN
- E’ possibile utilizzarlo come bastion host per collegarsi alle nostre risorse AWS
Ci siamo divertiti? Alla prossima!