{"id":4989,"date":"2022-09-30T10:56:43","date_gmt":"2022-09-30T08:56:43","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=4989"},"modified":"2022-09-30T11:00:15","modified_gmt":"2022-09-30T09:00:15","slug":"realizzare-una-appliance-di-rete-personalizzata-su-aws-usando-i-gateway-load-balancer","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/realizzare-una-appliance-di-rete-personalizzata-su-aws-usando-i-gateway-load-balancer\/","title":{"rendered":"Realizzare una appliance di rete personalizzata su AWS usando i Gateway Load Balancer"},"content":{"rendered":"\n
“Quando guardi a lungo nell’abisso, l’abisso ti guarda dentro” (F. Nietzsche).<\/em><\/p>\n\n\n\n Nel nostro articolo precedente sulle configurazioni avanzate dei Load Balancer<\/a> avevamo volutamente tralasciato momentaneamente i Gateway Load Balancer per dedicare a questo aspetto un approfondimento specifico. <\/p>\n\n\n\n I Gateway Load Balancer possono permetterci di osservare e filtrare il traffico di rete in uscita usando appliance dedicate. Nei paragrafi successivi vedremo come fare, assumendo che sia gi\u00e0 stata progettata ed implementata una soluzione di networking centralizzato con un Transit Gateway<\/a>.<\/p>\n\n\n\n Per utilizzare un IDS o firewall custom possiamo configurare il routing della VPC per inotrare il traffico ad una interfaccia ENI di una istanza EC2. Questa soluzione \u00e8 semplice da implementare, ma non \u00e8 altamente affidabile, n\u00e9 scalabile: se l’istanza smette di funzionare, la regola impostata rimane legata alla ENI e non \u00e8 possibile fare in modo che cambi automaticamente. <\/p>\n\n\n\n I Gateway Load Balancer permettono di risolvere questo problema – e guadagnare quindi in affidabilit\u00e0 – instradando il traffico di Livello 3 distribuendolo a istanze EC2 in alta affidabilit\u00e0, a prescindere dal protocollo o porta utilizzati. Usando altri load balancer si resterebbe legati a listener con specifici protocolli o porte: non sarebbe possibile, ad esempio, inoltrare traffico ICMP.<\/p>\n\n\n\n Molti fornitori software, come Cisco, F5 e Fortinet mettono gi\u00e0 a disposizione appliance pronte all’uso. L’elenco completo \u00e8 disponibile qui<\/a>.<\/p>\n\n\n\n In questo articolo creeremo una semplice appliance personalizzata che faccia da IDS e router utilizzando Linux, iptables e Suricata<\/a>. In questo modo, avremo modo di capire come funziona la tecnologia dietro le quinte di queste soluzioni <\/p>\n\n\n\n Prima di passare alla parte pratica di implementazione descriviamo in breve questo tipo di load balancer.<\/p>\n\n\n\n Il Gateway Load Balancer (GWLB) pu\u00f2 fare routing di tutto il traffico IP (TCP, UDP, ICMP, GRE). Il protocollo GENEVE \u00e8 la componente tecnologica alla base del funzionamento di un GWLB.<\/p>\n\n\n\n GENEVE \u00e8 un nuovo protocollo di incapsulamento definito nella RFC 8926<\/a>, standard per tecnologie e vendor differenti. L’acronimo significa Generic Network Virtualization Encapsulation; permette di incapsulare il traffico in un tunnel, in modo che la rete fisica sottostante non sia a conoscenza del traffico trasportato. \u00c8 utilizzato frequentemente per estendere VLAN e VXLAN fra pi\u00f9 reti utilizzando internet.<\/p>\n\n\n\n Il nostro obiettivo \u00e8 ottenere un setup scalabile e fault tolerant. Il Gateway Load Balancer, come i “fratelli” Network ed Application, pu\u00f2 essere distribuito in pi\u00f9 Availability Zone. <\/p>\n\n\n\n Faremo anche in modo che il ciclo di vita delle nostre appliance sia gestito da un Autoscaling Group, cos\u00ec da aggiungere elasticit\u00e0 al nostro design. <\/p>\n\n\n\n Useremo i NAT Gateway per semplificare la gestione degli IP pubblici: alcuni partner o fornitori potrebbero limitare l’accesso a indirizzi IP pubblici predefiniti. Un NAT Gateway fa in modo che, se una istanza viene aggiunta dall’autoscaling group in una Availability Zone, l’ip pubblico di uscita rimanga sempre uguale usando un Elastic IP assegnato. <\/p>\n\n\n\n Di seguito lo schema dell’architettura che utilizzeremo:<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Mettiamo ora mano alla parte pratica, con command line e Console AWS ! <\/p>\n\n\n\n Prima di creare e configurare il nostro Network Load Balancer, avremo infatti bisogno di creare una AMI. Useremo Ubuntu 22.04, potremo aggiungere personalizzazioni pi\u00f9 avanti. <\/p>\n\n\n\n Una istanza EC2 pu\u00f2 essere utilizzata come target per un GWLB se supporta la creazione di un tunnel GENEVE. Quando il tunnel risulta funzionante, il Gateway Load Balancer inizier\u00e0 a distribuire il traffico.<\/p>\n\n\n\n Per prima cosa, dunque, bisogna fare in modo che il protocollo GENEVE sia supportato sulla nostra appliance custom. <\/p>\n\n\n\n \u00c8 possibile usare il comando standard Linux <\/p>\n\n\n\n per creare il tunnel, ma, per nostra fortuna, AWS mette a disposizione un tool che facilita il nostro compito. \u00c8 disponibile qui<\/a>. <\/p>\n\n\n\n Dando per scontata la creazione di una nuova istanza ed una AMI, partiamo con la compilazione del tool per la gestione del tunnel e l’installazione. <\/p>\n\n\n\n Installeremo anche Suricata<\/strong>, un Intrusion Detection System open-source. Useremo la configurazione di default<\/p>\n\n\n\n Al termine di queste operazioni il tunnel handler sar\u00e0 pronto, viene creato un file eseguibile con nome “gwlbtun”. Eseguendolo con il parametro “-h” viene visualizzata la pagina di help:<\/p>\n\n\n\n Oltre a stabilire la connessione con il Gateway Load Balancer e creare il tunnel GENEVE, gwlbtun mette a disposizione una porta utilizzabile per permettere al Target Group di effettuare gli health check, cos\u00ec da non dover implementare controlli ad hoc.<\/p>\n\n\n\n In aggiunta a questo, quando il tunnel viene creato o terminato, gwlbtun pu\u00f2 eseguire uno script. Useremo questa funzione per fare in modo che uno script bash possa abilitare il routing IP, usando iptables per aggiungere e rimuovare le regole di NAT.<\/p>\n\n\n\n Nota:<\/strong> per far funzionare la nostra istanza \u00e8 necessario disabilitare una feature di sicurezza chiamata “source\/destination check”. Questa impostazione fa in modo che il traffico non originato o diretto da o per l’istanza venga automaticamente bloccato. <\/p>\n\n\n\n L’istanza stessa deve essere in grado di farlo, per cui vedremo che dovremo creare ed assegnare un instance role con una autorizzazione specifica.<\/p>\n\n\n\n Lo script che segue p\u00f9o essere memorizzato nella directory<\/p>\n\n\n\n con il nome<\/p>\n\n\n\n A questo punto non ci resta che scrive una unit systemd per avviare gwlbtun. Useremo aws-gwlb.service come nome, mettendo il file nella directory<\/p>\n\n\n\n Con i comandi che seguono abilitiamo il servizio (possiamo non avviarlo perch\u00e9 l’istanza su cui stiamo lavorando verr\u00e0 usata come template)<\/p>\n\n\n\n Possiamo ora creare una AMI e iniziare a creare il Gateway Load Balancer.<\/p>\n\n\n\n Per prima cosa va creato un Target Group, cliccando sulla sezione “Target Group<\/strong>“<\/p>\n\n\n\n Il tipo di target sar\u00e0 “Instances”, il nome \u00e8 libero. Il protocollo deve essere “GENEVE”. Siccome nella unit systemd abbiamo specificato il flag “-p 80” useremo la porta 80 per l’health check<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Al prossimo step non selezioneremo nessuna istanza, la gestione sar\u00e0 compito dell’Autoscaling Group.<\/p>\n\n\n\n Terminata la configurazione del Target Group, passiamo alla creazione del load balancer. Facendo click su “Load Balancers<\/strong>” aggiungiamo un load balancer selezionando “Gateway Load Balancer”.<\/p>\n\n\n\n La configurazione di base \u00e8 condivisa con gli altri tipi di load balancer: occorre assegnare un nome, selezionare la VPC e le subnet. <\/p>\n\n\n\n Alla sezione “IP listener routing” invece troveremo il target group appena creato:<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Per poter referenziare il Gateway load balancer nelle routing table occorre definire un endpoint service. Basta fare click su “Endpoint Services” nella sezione VPC della console AWS e crearne uno nuovo. Il processo \u00e8 lo stesso usato per gli endpoint basati sui load balancer di tipo network (come descritto qui<\/a>).<\/p>\n\n\n\n Il tipo dovr\u00e0 essere “Gateway” e, una volta assegnato un nome, occorre selezionare il load balancer appena creato.<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Una volta creato il campo “service name” \u00e8 necessario per creare l’endpoint<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Ora, alla sezione “Endpoint” e facendo click su “Create Enpoint” occorre selezionare “Other endpoint services”, incollare il service name e fare click su “Verify Service”.<\/p>\n\n\n\n Una volta verificato il servizio possiamo selezionare la VPC e una subnet in cui impostare l’endpoint (in questo caso useremo una subnet raggiungibile dal Transit Gateway)<\/p>\n\n\n\n <\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n A questo punto occorre ripetere i passi per tutte le subnet, non dimenticate di accettare le connessioni! <\/p>\n\n\n\n La configurazione della parte rete \u00e8 ora terminata. A questo punto non ci rimane che creare un autoscaling group. Per brevit\u00e0 non descriveremo questi passaggi. <\/p>\n\n\n\n \u00c8 ora il momento di creare un ruolo per le nostre istanze e aggiungere la definizione al Launch Template. Il ruolo deve contenere la policy che permette alle istanze di disabilitare il source\/destination check con una policy simile a questa:<\/p>\n\n\n\n Terminata la configurazione dell’autoscaling possiamo finalmente vedere le istanze in esecuzione nel target group<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Effettuando l’accesso ad una istanza potremo vedere che: <\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n 2. Il servizio \u00e8 avviato e funzionante:<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n 3. Due interfacce di rete (gwi-* e gwo-*) sono state create da gwlbtun<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n 4. Le regole di firewall sono presenti<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n 5. Ultimo (ma non ultimo), Suricata sar\u00e0 pronto per catturare gli eventi di sicurezza<\/p>\n\n\n\n <\/p>\n\n\n <\/p>\n\n\n\n Per fare in modo che tutte le richieste HTTP uscenti siano tracciate, possiamo installare un proxy server in modalit\u00e0 transparent (come squid, ad esempio) sul template, centralizzando i log su CloudWatch Logs.<\/p>\n\n\n\n Suggerimento: nel file \/etc\/squid\/squid.conf occorre abiltiare il “transparent mode”, “SSL bumping” ed inserire le regole di NAT con iptables. <\/p>\n\n\n\n Facendo evolvere lo script di firewall si pu\u00f2 filtrare il traffico in uscita (o usare uno strumento come EasyWall<\/a>.<\/p>\n\n\n\n Utilizzando un Gateway Load Balancer \u00e8 possibile aumentare il controllo e la visibilit\u00e0 sul traffico di rete in uscita, mantenendo una soluzione affidabile, scalabile e personalizzabile.<\/p>\n\n\n\n Ora sappiamo come funzionano dietro le quinte le appliance di sicurezza di terze parti. Le implementazioni possono essere differenti, ma la tecnologia alla base \u00e8 in comune. <\/p>\n\n\n\n Vi siete imbattuti in scenari particolari o avete idee per cui un Gateway Load Balancer pu\u00f2 essere utile? Fatecelo sapere nei commenti!<\/p>\n\n\n\nCome funzionano i Gateway Load Balancer<\/h2>\n\n\n\n
La nostra architettura di esempio<\/h2>\n\n\n\n
<\/figure><\/div>\n\n\n
Installazione del software e del tunnel manager<\/h2>\n\n\n\n
ip<\/code><\/pre>\n\n\n\n
apt update\napt install -y build-essential \"Development Tools\"\napt install -y cmake g++ suricata\nsnap install aws-cli --classic\nsuricata-update #update rules for suricata\ncd \/opt\ngit clone https:\/\/github.com\/aws-samples\/aws-gateway-load-balancer-tunnel-handler\ncd aws-gateway-load-balancer-tunnel-handler\ncmake .\nmake<\/code><\/pre>\n\n\n\n
root@ip-10-101-5-238:\/opt\/aws-gateway-load-balancer-tunnel-handler# .\/gwlbtun -h\nAWS Gateway Load Balancer Tunnel Handler\nUsage: .\/gwlbtun [options]\nExample: .\/gwlbtun\n\n -h Print this help\n -c FILE Command to execute when a new tunnel has been built. See below for arguments passed.\n -r FILE Command to execute when a tunnel times out and is about to be destroyed. See below for arguments passed.\n -t TIME Minimum time in seconds between last packet seen and to consider the tunnel timed out. Set to 0 (the default) to never time out tunnels.\n Note the actual time between last packet and the destroy call may be longer than this time.\n -p PORT Listen to TCP port PORT and provide a health status report on it.\n -s Only return simple health check status (only the HTTP response code), instead of detailed statistics.\n -d Enable debugging output.\n -x Enable dumping the hex payload of packets being processed.\n\n---------------------------------------------------------------------------------------------------------\nTunnel command arguments:\nThe commands will be called with the following arguments:\n1: The string 'CREATE' or 'DESTROY', depending on which operation is occurring.\n2: The interface name of the ingress interface (gwi-<X>).\n3: The interface name of the egress interface (gwo-<X>). Packets can be sent out via in the ingress\n as well, but having two different interfaces makes routing and iptables easier.\n4: The GWLBE ENI ID in base 16 (e.g. '2b8ee1d4db0c51c4') associated with this tunnel.\n\nThe <X> in the interface name is replaced with the base 60 encoded ENI ID (to fit inside the 15 character\ndevice name limit).<\/code><\/pre>\n\n\n\n
\/opt\/aws-gateway-load-balancer-tunnel-handler<\/code><\/pre>\n\n\n\n
tunnel-handler.sh<\/code><\/pre>\n\n\n\n
#!\/bin\/bash\n\n# Note: This requires this instance to have Source\/Dest check disabled; we need to assign a role to the ec2 instance to enable and disable it\n\n\necho \"Running tunnel handler script... \"\necho Mode is $1, In Int is $2, Out Int is $3, ENI is $4\n\niptables -F\niptables -t nat -F\nINSTANCE_ID=$(curl 169.254.169.254\/latest\/meta-data\/instance-id\n\ncase $1 in\n \tCREATE)\n\t\t\techo \"Disabling source and destination check.\"\n\t\t\taws ec2 modify-instance-attribute --instance-id=$INSTANCE_ID --source-dest-check\n\n \techo \"Setting up NAT and IP FORWARD\"\n \tiptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE\n \tiptables -A FORWARD -i $2 -o $2 -j ACCEPT\n \techo 1 > \/proc\/sys\/net\/ipv4\/ip_forward\n \techo 0 > \/proc\/sys\/net\/ipv4\/conf\/all\/rp_filter\n \techo 0 > \/proc\/sys\/net\/ipv4\/conf\/$2\/rp_filter\n \t;;\n \tDESTROY)\n\t\t\techo \"Enabling source and destination check.\"\n\t\t\taws ec2 modify-instance-attribute --instance-id=$INSTANCE_ID --no-source-dest-check\n \techo \"Removing IP FORWARD\"\n \techo 0 > \/proc\/sys\/net\/ipv4\/ip_forward\n \techo 1 > \/proc\/sys\/net\/ipv4\/conf\/all\/rp_filter\n \techo 1 > \/proc\/sys\/net\/ipv4\/conf\/$2\/rp_filter\n \t;;\n \t*)\n \techo \"invalid action.\"\n \texit 1\n \t;;\nesac<\/code><\/pre>\n\n\n\n
\/lib\/systemd\/system\/<\/code><\/pre>\n\n\n\n
[Unit]\nDescription=AWS GWLB Tunnel Handler\nAfter=network.target\n\n[Service] \nExecStart=\/opt\/aws-gateway-load-balancer-tunnel-handler\/gwlbtun -c \/opt\/aws-gateway-load-balancer-tunnel-handler\/tunnel-handler.sh -r \/opt\/aws-gateway-load-balancer-tunnel-handler\/tunnel-handler.sh -p 80\nRestart=always\nRestartSec=5s\n\n[Install]\nWantedBy=multi-user.target\nAlias=aws-gwlb<\/code><\/pre>\n\n\n\n
systemctl daemon-reload\nsystemctl enable aws-gwlb<\/code><\/pre>\n\n\n\n
Configurazione del Load Balancer<\/h2>\n\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
{\n \"Sid\": \"Allow Source-Dest check modification\",\n \"Effect\": \"Allow\",\n \"Action\": \"ec2:ModifyInstanceAttribute\",\n \"Resource\": \"*\"\n}<\/code><\/pre>\n\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
<\/figure><\/div>\n\n\n
Prossimi passi<\/h2>\n\n\n\n
Conclusioni<\/h2>\n\n\n\n
\n\n\n\nAbout Proud2beCloud<\/h4>\n\n\n\n