Architettura di un'Infrastruttura Cloud Multi-Tenant: Design e Scelte Tecniche

Documentazione dell'architettura di rete che ho progettato per un'infrastruttura multi-tenant su Hetzner Cloud, con focus sulle decisioni di design e le motivazioni tecniche dietro ogni scelta.

Contesto e Obiettivi del Progetto

Ho progettato e implementato un'infrastruttura cloud per gestire multiple istanze di applicazioni e-commerce (principalmente Magento) in un modello multi-tenant. I requisiti principali erano:

  • Isolamento tra tenant: Ogni cliente deve essere isolato dagli altri, con diversi livelli di isolamento in base al tier (shared, business, enterprise)
  • Scalabilità: L'architettura deve supportare la crescita da pochi clienti a centinaia senza re-design
  • Security by design: Principio least privilege e defense in depth applicati a tutti i livelli
  • Cost efficiency: Budget limitato, necessità di ottimizzare i costi senza compromettere la sicurezza
  • Operational complexity gestibile: Team piccolo, serve un'architettura mantenibile

Ho scelto Hetzner Cloud come provider per il rapporto costo/prestazioni e la localizzazione europea dei datacenter (GDPR compliance). L'intera infrastruttura è gestita come codice con Terraform e Ansible.

1. Decisioni Architetturali Fondamentali

1.1 VPC-Based Architecture vs Flat Network

La prima decisione fondamentale è stata l'adozione di un'architettura basata su Virtual Private Cloud (VPC) invece di una rete flat con tutti i server esposti pubblicamente. Ho valutato tre approcci:

Approccio Vantaggi Svantaggi Valutazione
Flat Network (IP pubblici) Setup semplice, nessun overhead di rete Superficie di attacco massima, difficile gestire firewall su N server ❌ Scartato per motivi di security
VPN Always-On Massima sicurezza, nessun server pubblico Ogni admin deve configurare VPN, single point of failure ⚠️ Troppo complesso per team distribuiti
VPC + Bastion Host Bilanciamento security/usabilità, pattern consolidato Setup iniziale più complesso Scelto

Ho optato per l'architettura VPC + Bastion perché offre il miglior compromesso tra sicurezza e operatività. Il bastion host diventa il single point of entry, semplificando audit e monitoring. Ho comunque integrato WireGuard VPN sul bastion per accesso diretto alla rete privata quando necessario.

1.2 NAT Gateway: Managed vs Self-Hosted

Le VM private hanno bisogno di accesso internet per updates, download di immagini Docker, e chiamate API esterne. Ho valutato due opzioni per il NAT Gateway:

Aspetto AWS NAT Gateway (Managed) Self-Hosted (iptables su VM)
Costo mensile ~$32 + $0.045/GB transfer ~€4.50 (costo VM CPX11)
Manutenzione Zero Security updates, monitoring
Controllo Limitato Completo (custom iptables rules, logging)
Lock-in Vendor-specific Portabile tra provider

Decisione: Ho implementato un NAT gateway self-hosted sul bastion host. Le motivazioni principali sono:

  • Risparmio del 85%: €4.50/mese vs $32/mese + transfer
  • Controllo completo: Posso implementare regole custom, logging dettagliato, traffic shaping
  • Portabilità: La soluzione funziona su qualsiasi provider cloud con lo stesso codice
  • Overhead accettabile: Per un team tecnico, manutenere iptables rules non è un problema

Il trade-off è l'overhead operativo (security patches, monitoring) e il single point of failure. Quest'ultimo è mitigabile con un secondo bastion in HA, configurabile quando necessario.

1.3 Segmentazione Multi-Tenant

Per supportare diversi tier di clienti (shared, business, enterprise) ho progettato una segmentazione di rete a 4 livelli. La chiave è bilanciare isolamento e costi: non tutti i clienti hanno bisogno (o possono permettersi) un'infrastruttura dedicata.

Modello di Segmentazione Implementato

Tier Network Isolamento Use Case
Management 10.0.0.0/16 Rete privata per infrastruttura core (Bastion, Rancher, Vault, ArgoCD) Gestione centralizzata
Shared 10.10.0.0/16 Namespace Kubernetes + Network Policies Clienti tier standard (budget limitato)
Business 10.20.0.0/16
(subnet /24 per cliente)
Nodi Kubernetes dedicati per cliente Clienti business (performance garantite)
Enterprise 10.100+.0.0/16
(rete /16 dedicata)
Rete completamente isolata, bastion dedicato Clienti enterprise (compliance, auditing)

Questa struttura mi permette di offrire tre livelli di isolamento con costi crescenti. Il cliente shared paga poco ma condivide risorse, il business ha nodi dedicati, l'enterprise ha un'intera infrastruttura separata.

2. Design della Network Topology

2.1 Management Network (10.0.0.0/16)

La rete di management ospita l'infrastruttura core. Ho progettato l'allocazione IP con spazio per crescita futura:

Network: 10.0.0.0/16 (65,534 host disponibili)

Subnet allocation:
- 10.0.0.0/24    Infrastructure core (254 host)
  ├─ 10.0.0.1    Gateway (reserved)
  ├─ 10.0.0.2    Bastion host (NAT + Jump + VPN)
  ├─ 10.0.0.3    Reserved (future HA bastion)
  ├─ 10.0.0.4    Rancher management cluster
  ├─ 10.0.0.5    Vault server (secrets management)
  ├─ 10.0.0.6    ArgoCD (GitOps)
  └─ 10.0.0.7-10 Reserved per futuri servizi

- 10.0.1.0/24    Rancher worker nodes
- 10.0.2.0/24    Monitoring stack (Prometheus, Grafana, Loki)
- 10.0.3.0/24    CI/CD infrastructure
- 10.0.10.0/24+  Reserved per expansion (spazio per ~240 subnet)
            

Rationale della scelta /16: Anche se oggi uso solo poche decine di IP, ho scelto un /16 per evitare re-numbering in futuro. Il costo di IP privati su Hetzner è zero, quindi posso permettermi di essere generoso con l'allocazione.

2.2 Customer Networks

Shared Network (10.10.0.0/16)

Per i clienti tier standard ho implementato isolamento a livello Kubernetes:

  • Un namespace dedicato per cliente
  • Kubernetes Network Policies per isolare il traffico pod-to-pod
  • Resource quotas per evitare noisy neighbor
  • Separate service accounts e RBAC

Questo approccio è economico (molti clienti sugli stessi nodi) ma richiede Kubernetes ben configurato. L'isolamento è forte ma non totale: tutti i pod girano sullo stesso kernel.

Business Network (10.20.0.0/16)

I clienti business ottengono una subnet /24 dedicata (254 host) e nodi Kubernetes dedicati:

10.20.0.0/24     Reserved (base subnet)
10.20.1.0/24     Cliente Business A (fino a 254 host)
10.20.2.0/24     Cliente Business B
10.20.3.0/24     Cliente Business C
...
10.20.255.0/24   Cliente Business 255

Capacità: 256 clienti business
            

Ogni cliente ha i propri worker nodes, quindi performance garantite e miglior isolamento. Il costo è proporzionalmente più alto (VM dedicate).

Enterprise Networks (10.100+.0.0/16)

I clienti enterprise ottengono una rete /16 completamente separata:

10.100.0.0/16    Enterprise Cliente A (65,534 host)
10.101.0.0/16    Enterprise Cliente B
10.102.0.0/16    Enterprise Cliente C
...

Capacità: 56 clienti enterprise (10.100-10.155)
            

Ogni rete enterprise ha il proprio bastion host, il proprio NAT gateway, e non condivide nulla con gli altri. Questo è necessario per compliance (es. PCI-DSS) o per clienti con requisiti specifici di auditing e sicurezza.

2.3 Routing Table Design

Ho configurato le routing table per garantire che il traffico segua sempre i percorsi previsti. Il design si basa su due principi:

  1. Traffico intra-VPC rimane locale: Non deve mai uscire e rientrare
  2. Traffico internet passa sempre per NAT gateway: Controllo centralizzato

Routing Table: Management Network

Destination         Next Hop              Priority    Note
10.0.0.0/16        Local                 1           Intra-VPC (higher priority)
0.0.0.0/0          10.0.0.2 (Bastion)    2           Default route via NAT
            

La priorità è fondamentale: la route più specifica (10.0.0.0/16) ha priorità su quella default (0.0.0.0/0). Questo garantisce che una VM che vuole parlare con un'altra VM nella stessa VPC non passi mai dal bastion.

Configurazione Bastion Host

Il bastion è configurato come dual-homed host (due interfacce di rete):

eth0 (Public interface):
  - IP pubblico Hetzner
  - Default gateway verso internet
  - Esposto a internet (solo SSH + WireGuard)

eth1 (Private interface):
  - IP: 10.0.0.2
  - Connesso alla management VPC
  - Non raggiungibile da internet

Kernel configuration:
  net.ipv4.ip_forward = 1

iptables configuration:
  # NAT per traffico da VPC verso internet
  iptables -t nat -A POSTROUTING -s 10.0.0.0/16 -o eth0 -j MASQUERADE

  # Permetti forwarding da VPC verso internet
  iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
  iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT

  # Blocca connessioni non richieste da internet verso VPC
  iptables -A FORWARD -i eth0 -o eth1 -j DROP
            

Funzionamento del NAT: Quando una VM privata (es. 10.0.0.4) vuole raggiungere internet (es. 8.8.8.8), il pacchetto arriva al bastion che applica SNAT (Source NAT), sostituendo l'IP sorgente con il proprio IP pubblico. Mantiene una connection tracking table per sapere a chi rispedire le risposte. È completamente trasparente per le VM.

2.4 Firewall Rules

Ho implementato firewall rules basate sul principio least privilege: tutto è bloccato di default, permetto solo il traffico strettamente necessario. Hetzner offre firewall a livello cloud (prima della VM), che ho combinato con iptables locale su ogni host per defense in depth.

Bastion Host Firewall

INBOUND (Hetzner Cloud Firewall):
  ✅ TCP 22 (SSH) from MY_OFFICE_IP/32
  ✅ UDP 51820 (WireGuard) from 0.0.0.0/0
  ❌ Everything else: DROP

OUTBOUND:
  ✅ Allow all (necessario per NAT function)

FORWARD:
  ✅ From 10.0.0.0/16 to 0.0.0.0/0 (NAT traffic)
  ✅ ESTABLISHED,RELATED connections
  ❌ From internet to 10.0.0.0/16: DROP
            

Nota sulla porta SSH: Ho ristretto SSH al solo IP del mio ufficio quando possibile. Per accesso da remoto uso WireGuard VPN, che offre autenticazione forte tramite chiavi crittografiche.

Private VMs Firewall (Rancher, Vault, etc.)

INBOUND:
  ✅ TCP 22 (SSH) from 10.0.0.2/32 (bastion only)
  ✅ TCP 6443 (K8s API) from 10.0.0.0/16 (management network)
  ✅ TCP 443 (HTTPS) from 10.0.0.2/32 (via reverse proxy)
  ✅ All from 10.0.0.0/16 (intra-VPC communication)
  ❌ Everything else: DROP

OUTBOUND:
  ✅ To 10.0.0.0/16 (intra-VPC)
  ✅ To 0.0.0.0/0 (internet via NAT)
            

Nessuna VM privata accetta connessioni dirette da internet. L'unico modo per raggiungerle è:

  1. SSH jump tramite bastion: ssh -J bastion private-vm
  2. WireGuard VPN connesso alla rete privata
  3. Reverse proxy sul bastion per servizi web (Nginx Proxy Manager)

3. Security Considerations

3.1 Threat Model

Ho analizzato i principali attack vector per questa architettura e implementato mitigazioni specifiche:

Threat Likelihood Impact Mitigation
SSH brute-force sul bastion Alto Medio fail2ban, key-only auth, IP whitelisting, rate limiting
Compromissione del bastion Basso Critico Hardening, monitoring, session recording, 2FA per sudo
Lateral movement post-breach Medio Alto Network segmentation, K8s Network Policies, RBAC stretto
DDoS sul bastion Medio Alto Rate limiting, connection limits, Cloudflare per web services
Data exfiltration Basso Critico Egress filtering, anomaly detection, audit logging

3.2 Defense in Depth

Ho implementato sicurezza su 5 layer. La compromissione di un singolo layer non deve compromettere l'intero sistema:

Layer 1: Network

  • VPC isolation tra management e customer networks
  • Cloud firewall (Hetzner) + iptables locale su ogni host
  • NAT gateway per controllo traffico outbound
  • Single point of entry (bastion) facilmente monitorabile

Layer 2: Host

  • SSH hardening: disable password auth, custom port, key-only
  • Automatic security updates (unattended-upgrades su Ubuntu)
  • fail2ban per intrusion prevention
  • Minimal attack surface: disable servizi non necessari

Layer 3: Application (Kubernetes)

  • Network Policies per controllo traffico pod-to-pod
  • Pod Security Standards (no privileged containers per workload)
  • RBAC granulare per service accounts
  • Image scanning (Trivy) in CI/CD pipeline

Layer 4: Data

  • Encryption at rest (LUKS per volumi critici)
  • Encryption in transit (TLS 1.3 everywhere)
  • Secrets management con HashiCorp Vault (no secrets in code)
  • Database encryption per dati PII

Layer 5: Operations

  • Centralized logging (Loki + Promtail)
  • Monitoring e alerting (Prometheus + Grafana + Alertmanager)
  • Backup automatici giornalieri con retention policy
  • Incident response plan documentato e testato

3.3 Limitazioni Note

È importante essere onesti sui limiti di questa architettura. Non protegge da:

  • Compromissione completa del bastion: Se un attaccante ottiene root sul bastion, ha potenzialmente accesso a tutta la rete privata. Mitigation parziale: monitoring rigoroso, session recording, 2FA per operazioni critiche.
  • Insider threats: Un amministratore con accesso legittimo può causare danni. Richiede separation of duties e audit logging.
  • Application-layer attacks: SQL injection, XSS, etc. non sono mitigati dall'architettura di rete. Richiede secure coding e WAF.
  • Supply chain attacks: Compromissione di dependency o immagini Docker. Richiede image scanning, SBOM, e verifica delle signature.

4. Cost Analysis

4.1 Infrastructure Base Cost

Ho fatto un'analisi comparativa dei costi per validare la scelta di Hetzner vs alternative più costose:

Componente Hetzner AWS Equivalente Risparmio
Bastion (CPX11: 2vCPU, 2GB RAM) €4.51/mese t3.small: ~$15/mese ~70%
NAT Gateway €0 (self-hosted) ~$32/mese + $0.045/GB ~100%
Rancher (CPX21: 3vCPU, 4GB) €8.21/mese t3.medium: ~$30/mese ~73%
Vault (CPX11) €4.51/mese t3.small: ~$15/mese ~70%
ArgoCD (CPX11) €4.51/mese t3.small: ~$15/mese ~70%
Traffic (20TB incluso) €0 ~$50/mese (1TB stimato) 100%
TOTALE €21.74/mese ~$157/mese 85%

Risparmio annuale: €1,440 (~$1,600) solo per l'infrastruttura base. Su scala con N customer worker nodes, il risparmio diventa ancora più significativo.

4.2 Scaling Economics

Per ogni cliente business con nodi dedicati:

  • CPX31 (4 vCPU, 8GB RAM): €14.28/mese → raccomandato per Magento
  • CPX41 (8 vCPU, 16GB RAM): €26.64/mese → per carichi pesanti

Esempio: 10 clienti business con 1x CPX31 = 10 × €14.28 = €142.80/mese aggiuntivi. Su AWS lo stesso setup costerebbe ~$500-600/mese.

Conclusioni

L'architettura che ho progettato mi ha permesso di costruire un'infrastruttura multi-tenant sicura, scalabile e cost-effective su Hetzner Cloud. Le decisioni chiave sono state:

  • Segmentazione di rete a 4 livelli per diversi tier di isolamento
  • NAT gateway self-hosted per ridurre i costi dell'85%
  • Bastion host come single point of entry con WireGuard VPN integrato
  • Defense in depth su 5 layer
  • Infrastructure as Code per riproducibilità

Nel prossimo articolo mostrerò l'implementazione pratica con Terraform: come trasformare questo design in codice riproducibile, con generazione automatica di inventory Ansible e configurazione completa del bastion host tramite cloud-init.

Risorse

Prossimo articolo: Infrastructure as Code con Terraform su Hetzner Cloud