BeyondProd: un nuovo approccio alla sicurezza cloud-native

Google ha pubblicato vari white paper che illustrano i progetti sviluppati internamente per garantire una maggiore sicurezza. BeyondProd si basa volutamente sui concetti di BeyondCorp: proprio come non funziona più per gli utenti finali, il modello di sicurezza perimetrale non è più valido neanche per i microservizi. Adattando il white paper BeyondCorp originale: "I presupposti fondamentali di questo modello non sono più validi: il perimetro non è più solo il luogo fisico [del data center] dell'azienda e tutto ciò che si trova al suo interno non è più un luogo sicuro per ospitare personal computer e applicazioni aziendali [microservizi]."

Questo white paper fornisce informazioni dettagliate sulle modalità con cui diverse parti dell'infrastruttura di Google collaborano per proteggere i carichi di lavoro, in un'architettura ora nota come "cloud-native". Per una panoramica sulla sicurezza di Google, consulta il white paper sulla progettazione dell'infrastruttura di sicurezza.

I contenuti del presente documento sono aggiornati a dicembre 2019. Questo white paper rappresenta lo status quo al momento in cui è stato redatto. Criteri e sistemi di sicurezza di Google Cloud possono variare in futuro, grazie al costante miglioramento della protezione per gli utenti.

Glossario

Nel presente documento vengono utilizzate le seguenti definizioni:

  • Un microservizio separa le singole attività necessarie all'esecuzione dell'applicazione in servizi separati, ciascuno dei quali può essere sviluppato e gestito in modo indipendente, con API, implementazione, scalabilità e gestione delle quote proprie. In un'architettura più moderna, un'applicazione, ad esempio un sito web, può essere eseguita come raccolta di microservizi invece che come servizio monolitico singolo. I microservizi sono indipendenti, modulari, dinamici e temporanei. Possono essere distribuiti su diversi host, cluster o anche cloud.

  • Un carico di lavoro è un'attività univoca completata da un'applicazione. In un'architettura di microservizi, il carico di lavoro può comprendere uno o più microservizi.

  • Un job è una singola istanza di un microservizio che esegue una parte di un'applicazione.

  • Il microservizio utilizza un'identità del servizio per l'autenticazione ad altri servizi in esecuzione nell'infrastruttura.

  • Una mesh di servizi è un livello dell'infrastruttura per la comunicazione service-to-service in grado di controllare il traffico, applicare criteri e monitorare a livello centralizzato le chiamate ai servizi. Quando si utilizza un'architettura di microservizi, questa rimuove il carico di implementazione di questi controlli dai singoli servizi e consente una gestione centralizzata e semplificata di numerosi microservizi.

Riepilogo a livello di CIO

  • L'infrastruttura di Google esegue il deployment dei carichi di lavoro come microservizi singoli in container e gestisce tali carichi di lavoro utilizzando Borg, il nostro sistema di orchestrazione dei container. Si tratta di un'ispirazione e di un modello per quella che oggi è ampiamente conosciuta come architettura "cloud-native".

  • L'infrastruttura di Google è stata progettata appositamente pensando alla sicurezza, che non è stata quindi frutto di una valutazione a posteriori. La nostra infrastruttura presuppone che tra i servizi non sia presente una relazione trust.

  • Google protegge i microservizi con un'iniziativa chiamata BeyondProd. Questa protezione include il modo in cui il codice viene modificato e la modalità di accesso ai dati utente nei microservizi. BeyondProd applica concetti quali: endpoint di servizi autenticati reciprocamente, sicurezza dei trasferimenti, terminazione sul perimetro con bilanciamento del carico globale e protezione da denial of service, provenienza del codice end-to-end e limitazione tramite sandbox del runtime.

  • Il passaggio da un modello di sicurezza tradizionale ad un modello di sicurezza cloud-native ha richiesto modifiche a due aree principali, ovvero l'infrastruttura e il processo di sviluppo. La creazione di componenti condivisi in una struttura condivisa che include e collega tutti i microservizi, nota come mesh di servizi, rende più semplice l'implementazione delle modifiche e consente di ottenere una sicurezza coerente nei vari servizi.

Motivazione

Google ha eseguito il passaggio ai container e alla relativa orchestrazione per ottenere un maggiore utilizzo delle risorse, creare applicazioni ad alta disponibilità e semplificare il lavoro degli sviluppatori Google. C'è un altra motivazione per il passaggio a un'infrastruttura containerizzata: allineare i controlli sulla sicurezza con l'architettura. Era diventato evidente che il modello di sicurezza basato sul perimetro non era sufficientemente sicuro. Se un utente malintenzionato avesse violato il perimetro, avrebbe avuto libertà di movimento all'interno della rete. Avevamo capito che erano necessari controlli della sicurezza più rigorosi nell'intera infrastruttura, ma desideravamo anche che gli sviluppatori Google fossero in grado di scrivere ed eseguire il deployment di applicazioni sicure senza dover implementare funzionalità di sicurezza.

Il passaggio da applicazioni monolitiche a microservizi distribuiti implementati da container utilizzando un sistema di orchestrazione offre vantaggi operativi tangibili: una gestione e una scalabilità più semplici. Questa architettura cloud-native ha richiesto un modello di sicurezza differente con strumenti diversi per proteggere i deployment allineati con i vantaggi in termini di gestione e scalabilità offerti dai microservizi.

Questo documento descrive l'implementazione della sicurezza cloud-native in Google, qui denominata BeyondProd: cosa comporta il passaggio a cloud-native per la sicurezza, i principi per la sicurezza cloud-native, i sistemi creati per soddisfare questi requisiti e alcune indicazioni su come gestire questo tipo di cambiamento.

Sicurezza cloud-native in Google

Microservizi containerizzati

Fin dall'inizio, Google ha scelto consapevolmente di creare la capacità dei suoi data center utilizzando server base economici invece di investire in hardware ad alta disponibilità più costosi. La filosofia alla base della nostra affidabilità era, e continua ad essere, che un eventuale problema in una singola parte del sistema non deve compromettere la disponibilità dei servizi visibili agli utenti. Per ottenere questa affidabilità sono state necessarie istanze ridondanti di servizi cosicché singoli guasti non comportassero interruzioni del servizio. Uno dei risultati di questa filosofia è stato lo sviluppo di container, microservizi e orchestrazione dei container per gestire con scalabilità il deployment di questi sistemi distribuiti e altamente ridondanti.

In un'infrastruttura containerizzata, il deployment di ogni carico di lavoro viene eseguito come proprio set di container immutabili, mobili e programmabili. Per gestire internamente questi container, abbiamo sviluppato un apposito sistema di orchestrazione denominato Borg1, utilizzato ancora oggi per il deployment di diversi miliardi di container a settimana.

I container semplificano la creazione di pacchetti di carichi di lavoro e la riprogrammazione tra le diverse macchine. I microservizi rendono più semplice lo sviluppo e il debug di parti diverse di un'applicazione. Utilizzati in combinazione, microservizi e container consentono la suddivisione dei carichi di lavoro in unità più piccole e più facili da gestire per la manutenzione e l'individuazione. Il passaggio a un'infrastruttura containerizzata con un'architettura di microservizi è noto come passaggio a "cloud-native". I servizi vengono eseguiti all'interno di container il cui deployment viene eseguito da Borg. Questa architettura scala i carichi di lavoro secondo necessità: in caso di elevata richiesta per un particolare carico di lavoro, più macchine potrebbero eseguire copie dello stesso servizio così da gestire la scala richiesta del carico di lavoro.

Google si distingue per aver preso in considerazione la sicurezza come parte di ogni evoluzione della nostra architettura. Il più recente concetto di sicurezza cloud-native è comparabile con le funzioni di sicurezza utilizzate da Google nel corso degli anni per proteggere l'infrastruttura. Il nostro obiettivo per questa architettura di microservizi e il processo di sviluppo è risolvere i problemi relativi alla sicurezza il prima possibile in fase di sviluppo e deployment, quando affrontarli è meno costoso, e farlo in maniera standardizzata e coerente. Il risultato finale consente agli sviluppatori di dedicare meno tempo alla sicurezza, ottenendo al contempo risultati più sicuri.

Migrazione a un'architettura cloud-native

Le moderne architetture di sicurezza hanno superato il modello di sicurezza tradizionale basato sul perimetro, in cui un muro protegge il perimetro e gli utenti e i servizi al suo interno sono considerati completamente affidabili. BeyondCorp è stata la risposta al cambiamento nelle modalità operative degli utenti delle aziende moderne. Oggi gli utenti sono mobili e lavorano comunemente all'esterno del tradizionale perimetro di sicurezza aziendale, ad esempio da una caffetteria, da un aereo o da qualsiasi altro luogo. Con BeyondCorp, viene meno l'idea di una rete aziendale privilegiata e di un accesso autorizzato basato esclusivamente su attributi e credenziali dell'utente e del dispositivo, indipendentemente dalla località in cui si trova la rete dell'utente.

La sicurezza cloud-native affronta gli stessi problemi per i servizi come per gli utenti: nel mondo cloud-native non possiamo affidarci esclusivamente a un firewall per proteggere la rete di produzione, così come non è possibile farlo per proteggere la rete aziendale. Così come non tutti gli utenti si trovano nello stesso luogo fisico o utilizzano lo stesso dispositivo, non tutti gli sviluppatori eseguono il deployment del codice nello stesso ambiente. Con BeyondProd, i microservizi possono essere eseguiti non solo all'interno di un data center protetto da firewall, ma in cloud pubblici o privati o in servizi ospitati da terze parti, ed è necessario garantire la loro sicurezza ovunque.

Proprio come gli utenti si spostano, utilizzano dispositivi differenti e si collegano da località diverse, anche i microservizi sono mobili e il deployment viene eseguito in ambienti diversi, su host eterogenei. Laddove BeyondCorp afferma che "la fiducia degli utenti dovrebbe dipendere da caratteristiche quali lo stato dei dispositivi sensibile al contesto e non dalla possibilità di collegarsi alla rete aziendale", per BeyondProd la "fiducia dei servizi dovrebbe dipendere da caratteristiche quali la provenienza del codice e l'identità dei servizi e non dalla posizione nella rete di produzione, come l'identità del nome host o l'IP".

Cloud-native e sviluppo di applicazioni

Un modello di sicurezza più tradizionale, incentrato sulla sicurezza del perimetro, non è in grado di proteggere da solo un'architettura cloud-native. Ad esempio, viene eseguito il deployment di un'applicazione monolitica che utilizza un'architettura a tre livelli nel data center di un'azienda privata, con una capacità sufficiente a gestire picchi di carico in caso di eventi critici. Applicazioni con requisiti di rete o hardware specifici vengono appositamente implementate su macchine specifiche, che di solito mantengono indirizzi IP fissi. Le implementazioni sono poco frequenti, voluminose e difficili da coordinare poiché le modifiche che ne conseguono si riflettono contemporaneamente su molte parti dell'applicazione. Ciò comporta applicazioni dalla durata estremamente lunga con aggiornamenti e applicazioni delle patch di sicurezza meno frequenti.

In un modello cloud-native, i container disaccoppiano i programmi binari necessari per l'applicazione dal sistema operativo host sottostante e rendono le applicazioni più portabili. I container sono pensati per essere utilizzati senza variazioni, ovvero non cambiano una volta eseguito il deployment, pertanto build e deployment si ripetono di frequente. I job vengono scalati per gestire il carico, viene eseguito il deployment di nuovi job all'aumentare del carico e i job esistenti vengono eliminati quando il carico diminuisce. Con i container riavviati, eliminati o riprogrammati spesso, il riutilizzo e la condivisione di hardware e networking è più frequente. Grazie a un processo di creazione e distribuzione comune standardizzato, il processo di sviluppo è più coerente e uniforme tra i team, anche se i team gestiscono lo sviluppo dei propri microservizi in maniera indipendente. Pertanto le considerazioni relative alla sicurezza (ad esempio le revisioni della sicurezza, la scansione del codice e la gestione delle vulnerabilità) possono essere applicate fin dalle prime fasi del ciclo di deployment.

Implicazioni per la sicurezza

Abbiamo parlato a lungo di come il modello di un interno non attendibile, con gli utenti in BeyondCorp, può essere applicato anche ai microservizi in BeyondProd. Ma in cosa consiste tale cambiamento? La Tabella 1 mette a confronto gli aspetti della sicurezza dell'infrastruttura tradizionale e i relativi contrappunti in un'architettura cloud-native. La tabella mostra inoltre i requisiti necessari per passare dall'una all'altra. Il resto di questa sezione fornisce maggiori dettagli su ciascuna riga della tabella.

Sicurezza nelle infrastrutture tradizionali Sicurezza cloud-native Requisiti impliciti per la sicurezza cloud-native
Sicurezza basata sul perimetro (ovvero, firewall), con comunicazioni interne considerate attendibili. Sicurezza Zero Trust con comunicazione Service-to-Service verificata e nessuna attendibilità implicita per i servizi nell'ambiente. Protezione della rete a livello perimetrale (rimane applicabile) e nessuna attendibilità reciproca intrinseca tra i servizi.
Hardware e IP fissi per alcune applicazioni. Uso, riutilizzo e condivisioni di risorse maggiori, inclusi IP e hardware. Codice di provenienza nota in esecuzione su macchine attendibili.
Identità basata su indirizzo IP. Identità basata sui servizi.
I servizi vengono eseguiti in una posizione nota e prevista. I servizi possono essere eseguiti ovunque nell'ambiente, inclusi deployment ibridi in cloud pubblici e data center privati.
Requisiti specifici della sicurezza integrati in ogni applicazione e applicati separatamente. Requisiti di sicurezza condivisi, integrati negli stack dei servizi secondo un criterio di applicazione centralizzata. Punti di passaggio obbligato per un'applicazione dei criteri coerente tra i diversi servizi.
Restrizioni limitate alla creazione e revisione dei servizi. Requisiti di sicurezza applicati coerentemente a tutti i servizi.
Supervisione limitata dei componenti di sicurezza. Visualizzazione centralizzata dei criteri di sicurezza e ottemperanza a tali criteri.
Implementazioni specializzate e non frequenti. Processo di creazione e implementazione standardizzato con modifiche più frequenti ai singoli microservizi. Implementazione semplice, automatica e standardizzata delle modifiche.
In genere, i carichi di lavoro vengono distribuiti come VM o su host fisici e utilizzano macchine fisiche o hypervisor per fornire l'isolamento. I carichi di lavoro con pacchetti e i relativi processi vengono eseguiti in un sistema operativo condiviso, il che richiede un meccanismo per isolare i carichi di lavoro. Isolamento tra carichi di lavoro che condividono lo stesso sistema operativo.

Tabella 1: Requisiti impliciti per la sicurezza nel passaggio all'architettura cloud-native

Dalla sicurezza perimetrale alla sicurezza "zero trust"

In un modello di sicurezza tradizionale, le applicazioni di un'organizzazione potrebbero dipendere da un firewall esterno che protegge il data center privato dal traffico in entrata. In un ambiente cloud-native, anche se il perimetro della rete deve ancora essere protetto, come nel modello BeyondCorp, un modello di sicurezza basato su perimetro non è più sufficiente. Non si tratta di un nuovo problema per la sicurezza, quanto piuttosto di un riconoscimento della realtà: se un firewall non è in grado di proteggere completamente la rete aziendale, non sarà in grado di proteggere neanche la rete di produzione. Con un modello di sicurezza "zero-trust" non è più possibile considerare implicitamente affidabile il traffico interno e sono necessari altri controlli di sicurezza, come l'autenticazione e la crittografia. Allo stesso tempo, il passaggio ai microservizi offre l'opportunità di ripensare il modello di sicurezza tradizionale. Eliminando la dipendenza da un singolo perimetro di rete (ad esempio un firewall), è possibile segmentare ulteriormente la rete in base al servizio. Un ulteriore passo avanti è implementare la segmentazione a livello dei microservizi senza attendibilità intrinseca tra i servizi. Con i microservizi, il traffico può avere diversi livelli di attendibilità con controlli differenti, andando oltre il semplice confronto tra traffico interno ed esterno.

Da hardware e IP fissi a risorse condivise più grandi

In un modello di sicurezza tradizionale, il deployment delle applicazioni di un'organizzazione avveniva su macchine specifiche, i cui indirizzi IP venivano cambiati raramente. Ciò significava che gli strumenti di sicurezza potevano contare su una mappa dell'architettura relativamente statica, che collegava le applicazioni in modo prevedibile; i criteri di sicurezza in strumenti come i firewall potevano utilizzare gli indirizzi IP per l'identificazione.

Nel mondo cloud-native, con host condivisi e job che cambiano frequentemente, l'uso di un firewall per controllare l'accesso tra i diversi microservizi non è efficace. Non si può contare sul fatto che un indirizzo IP specifico sia collegato ad un particolare servizio. Pertanto, invece che su un indirizzo IP o un nome host, l'identità è basata su un servizio.

Dall'implementazione della sicurezza specifica per le applicazioni ai requisiti di sicurezza condivisi integrati negli stack dei servizi

In un modello di sicurezza tradizionale, ogni singola applicazione era responsabile della conformità ai propri requisiti di sicurezza, indipendentemente dagli altri servizi. Tali requisiti includevano la gestione dell'identità, la terminazione SSL/TLS e la gestione degli accessi ai dati. Tutto questo spesso causava implementazioni discordanti o problemi di sicurezza irrisolti che, dovendo essere corretti in diversi punti, complicavano l'applicazione delle correzioni.

Nel mondo cloud-native, i componenti vengono riutilizzati tra servizi con maggiore frequenza e sono presenti punti di passaggio obbligati che consentono un'applicazione coerente dei criteri nei diversi servizi. Possono essere applicati criteri differenti utilizzando servizi di sicurezza diversi. Invece di richiedere che ogni applicazione implementi i servizi di sicurezza critici separatamente, si possono suddividere i diversi criteri in microservizi separati (ad esempio, un criterio per garantire l'accesso autorizzato ai dati utente e un altro per garantire l'uso di suite di crittografia TLS aggiornate).

Da processi di implementazione specializzati e distanziati nel tempo a processi standardizzati con implementazioni più frequenti

In un modello di sicurezza tradizionale, i servizi condivisi erano limitati. La maggiore distribuzione del codice, abbinata allo sviluppo locale, rendeva difficile valutare l'impatto di una modifica che interessava più parti di un'applicazione; di conseguenza, le implementazioni erano poco frequenti e difficili da coordinare. Per apportare una modifica, gli sviluppatori potevano aver bisogno di aggiornare direttamente ciascun componente (ad esempio, eseguendo l'accesso tramite SSH a una macchina virtuale per aggiornare una configurazione). Nel complesso, questo comportava una durata delle applicazioni estremamente lunga. Dal punto di vista della sicurezza, data la maggiore distribuzione, era più difficile revisionare il codice, e ancora più arduo garantire che la correzione di una vulnerabilità venisse applicata ovunque. Il passaggio a cloud-native, dove le implementazioni sono frequenti e standardizzate, consente di introdurre la sicurezza fin dalle prime fasi2 del ciclo di vita di sviluppo del software. Questo consente un'applicazione più coerente e semplice delle misure ordinarie di sicurezza, compresa la regolare applicazione delle patch.

Da carichi di lavoro isolati utilizzando macchine fisiche o hypervisor a pacchetti di carichi di lavoro in esecuzione sulla stessa macchina che richiedono un isolamento più robusto

In un modello di sicurezza tradizionale, i carichi di lavoro erano programmati sulle proprie istanze, senza risorse condivise. L'applicazione veniva delimitata efficacemente dalla propria macchina e dal confine della rete, e l'isolamento dei carichi di lavoro veniva applicato esclusivamente facendo affidamento sulla separazione degli host fisici, sugli hypervisor e sui firewall tradizionali.

Nel mondo cloud-native, i carichi di lavoro sono containerizzati e inclusi in pacchetti, con risorse e host condivisi. Quindi è necessario un isolamento più efficace tra i carichi di lavoro. I carichi di lavoro possono essere separati in microservizi, isolati l'uno dall'altro mediante controlli della rete e tecnologie di limitazione tramite sandbox.

Principi di sicurezza

Nello sviluppo di un'architettura cloud-native, volevamo contemporaneamente potenziare la nostra sicurezza, pertanto abbiamo sviluppato e ottimizzato i seguenti principi di sicurezza:

  • Protezione della rete a livello perimetrale, in modo da isolare i carichi di lavoro da attacchi di rete e da traffico Internet non autorizzato. Sebbene un approccio basato su "muro" non sia un concetto nuovo dell'architettura cloud-native, rimane comunque una best practice per la sicurezza. Nel mondo cloud-native, l'approccio perimetrale viene utilizzato per proteggere il massimo numero di infrastrutture possibile da traffico non autorizzato e attacchi potenziali da Internet, ad esempio, attacchi Denial of Service basati sui volumi.

  • Nessuna attendibilità reciproca intrinseca tra i servizi, per cui un servizio può essere utilizzato solo da chiamanti noti, attendibili, e dotati di specifiche autorizzazioni. In questo modo un utente malintenzionato non può utilizzare codice non attendibile per accedere a un servizio. Se un servizio viene compromesso, all'utente malintenzionato viene impedito di eseguire operazioni che gli consentano di espandere il proprio campo di azione. Questa assenza di attendibilità reciproca consente di limitare la portata dell'attacco.

  • Codice in esecuzione su macchine attendibili di provenienza nota, in modo da imporre alle identità dei servizi l'uso esclusivo di configurazioni e codice autorizzati e l'esecuzione solo in ambienti verificati e autorizzati.

  • Punti di passaggio obbligato per un'applicazione dei criteri coerente tra i diversi servizi. Ad esempio, un punto di passaggio obbligato per verificare le richieste di accesso a dati utente, in modo che l'accesso di un servizio avvenga solo a seguito di una richiesta convalidata di un utente finale autorizzato e che un accesso come amministratore necessiti di una motivazione di business.

  • Implementazione semplice, automatica e standardizzata delle modifiche, che consente una facile revisione dell'impatto sulla sicurezza dei cambiamenti nell'infrastruttura, e l'implementazione delle patch di sicurezza con un impatto minimo sulla produzione.

  • Isolamento dei carichi di lavoro con sistema operativo condiviso, in modo che la compromissione di un servizio non possa influire sulla sicurezza di un altro carico di lavoro in esecuzione sullo stesso host. Ciò consente di limitare la portata dell'attacco di una potenziale compromissione.

Nell'intera infrastruttura, il nostro obiettivo è avere una sicurezza automatizzata che non dipenda dai singoli. La sicurezza deve essere scalata così come vengono scalati i servizi. I servizi devono essere sicuri per impostazione predefinita e la mancata sicurezza deve essere un'eccezione: gli interventi umani devono essere un'eccezione e non la regola e, quando avvengono, devono essere verificabili. Possiamo quindi autenticare un servizio in base al codice e alla configurazione di deployment del servizio stesso, invece che alle persone che ne hanno eseguito il deployment.

Nel complesso, l'implementazione di questi principi di sicurezza significa che è possibile effettuare il deployment dei container e dei microservizi in esecuzione al loro interno, che possono comunicare tra loro ed essere eseguiti uno accanto all'altro senza compromettere le proprietà dell'architettura cloud-native (ovvero gestione semplice dei carichi di lavoro, scalabilità autonoma, e pacchettizzazione efficace). Tutto questo può essere ottenuto senza gravare sui singoli sviluppatori di microservizi con i dettagli di sicurezza e implementazione dell'infrastruttura sottostante.

Servizi di sicurezza interna di Google

Per proteggere l'infrastruttura cloud-native di Google, abbiamo progettato e sviluppato diversi servizi e strumenti interni. I servizi di sicurezza elencati di seguito operano insieme per rispondere ai principi di sicurezza definiti nella sezione Principi di sicurezza:

  • Google Front End (GFE): termina la connessione dall'utente finale e offre un punto centrale per applicare le best practice TLS. Anche se la nostra attenzione non è più incentrata sulla sicurezza perimetrale, GFE è ancora una parte importante della nostra strategia di protezione dei servizi interni dagli attacchi denial of service. GFE è il primo punto di accesso per una connessione utente a Google; una volta dentro la nostra infrastruttura, GFE è responsabile anche del bilanciamento del carico e del reindirizzamento del traffico tra le aree geografiche secondo necessità. Nella nostra infrastruttura, GFE è il proxy perimetrale che indirizza il traffico al microservizio corretto.

  • Application Layer Transport Security (ALTS): Utilizzato per autenticazione, integrità e crittografia RPC. ALTS è un sistema di autenticazione reciproca e crittografia di trasporto per i servizi nell'infrastruttura di Google. Le identità sono generalmente associate a servizi invece che a un nome server o host specifico. Questo favorisce la replica perfetta dei microservizi, il bilanciamento del carico e la ripianificazione tra host.

  • Autorizzazione binaria per Borg e Integrità host vengono utilizzati per la verifica rispettivamente dell'integrità del microservizio e della macchina:

    • Autorizzazione binaria per Borg (BAB): un controllo dell'applicazione forzata in fase di deployment che garantisce la conformità del codice ai requisiti interni per la sicurezza prima del deployment effettivo. I controlli BAB includono l'esame delle modifiche da parte di un secondo tecnico, l'invio del codice al nostro repository del codice sorgente e la compilazione verificabile di programmi binari sull'infrastruttura dedicata. Nella nostra infrastruttura, BAB limita il deployment di microservizi non autorizzati.
    • Integrità host (HINT): verifica l'integrità del software del sistema host tramite una procedura di avvio protetto ed è accompagnata da un hardware microcontroller di sicurezza, dove supportato. I controlli HINT includono la verifica delle firme digitali su BIOS, BMC, bootloader e kernel del sistema operativo.
  • Criteri di accesso ai servizi e i Ticket di contesto utente finale vengono utilizzati per limitare l'accesso ai dati:

    • Criteri di accesso ai servizi: limitano la modalità di accesso ai dati tra i servizi. Quando una RPC viene inviata da un servizio a un altro, i criteri di accesso ai servizi definiscono i requisiti di autenticazione, autorizzazione e controllo richiesti per accedere ai dati del servizio ricevente. Si limita così la modalità di accesso ai dati, viene garantito il livello minimo di accesso necessario e viene specificata la modalità di controllo dell'accesso. Nell'infrastruttura Google, le norme di accesso ai servizi limitano l'accesso di un microservizio ai dati di un altro microservizio e consentono analisi globali dei controlli di accesso.
    • Ticket contesto utente finale (EUC): questi ticket vengono generati da un servizio di autenticazione dell'utente finale e forniscono ai servizi un'identità utente, separata dall'identità servizio. Sono credenziali con integrità protetta, emesse a livello centrale e inoltrabili che attestano l'identità dell'utente finale che ha richiesto il servizio. In questo modo viene ridotta la necessità di relazioni trust tra i servizi, poiché l'identità peer tramite ALTS è spesso insufficiente a concedere l'accesso e le decisioni sull'autorizzazione si basano solitamente anche sull'identità dell'utente finale.
  • Strumenti Borg per deployment blu/verde 3: questi strumenti sono responsabili della migrazione dei carichi di lavoro in esecuzione durante le attività di manutenzione. Viene eseguito il deployment di un nuovo job Borg oltre a quello esistente, e un bilanciatore del carico sposta gradualmente il traffico dall'uno all'altro. Ciò consente di aggiornare un microservizio senza tempi di inattività e senza che l'utente se ne accorga. Questi strumenti vengono utilizzati per applicare upgrade dei servizi in occasione dell'aggiunta di nuove funzionalità, oltre che per applicare aggiornamenti critici della sicurezza senza tempi di inattività (ad esempio, Heartbleed e Spectre/Meltdown). Per modifiche che interessano l'infrastruttura Google Cloud, utilizziamo la migrazione live per garantire che non ci siano conseguenze sui carichi di lavoro delle VM.

  • gVisor, per l'isolamento dei carichi di lavoro. gVisor utilizza un kernel dello spazio utente per intercettare e gestire chiamate di sistema, riducendo l'interazione con l'host e la superficie esposta a potenziali attacchi. Questo kernel offre la maggior parte delle funzionalità necessarie per eseguire un'applicazione e limita la superficie del kernel dell'host accessibile all'applicazione. Nell'infrastruttura di Google, gVisor è uno dei vari, importanti strumenti utilizzati per isolare l'uno dall'altro i carichi di lavoro interni e dei clienti Google Cloud in esecuzione sullo stesso host.

La Tabella 2 associa ciascun principio descritto nella sezione Principi di sicurezza allo strumento corrispondente utilizzato per implementare tale principio.

Principio di sicurezza Servizio/strumento di sicurezza interna di Google
Protezione della rete a livello perimetrale Google Front End (GFE), per la gestione della terminazione TLS e dei criteri per il traffico in entrata.
Nessuna attendibilità reciproca intrinseca tra i servizi Application Layer Transport Security (ALTS), per autenticazione RPC, integrità, crittografia e identità dei servizi
Codice in esecuzione su macchine attendibili di provenienza nota Autorizzazione binaria per Borg (BAB), per la verifica della provenienza del codice

Integrità host (HINT), per la verifica dell'integrità della macchina

Punti di passaggio obbligato per un'applicazione dei criteri coerente tra i diversi servizi Criteri di accesso ai servizi, per limitare la modalità di accesso ai dati tra i servizi

Ticket contesto utente finale (EUC), per attestare l'identità del richiedente originale

Implementazione semplice, automatica e standardizzata delle modifiche Strumenti Borg, per deployment blu/verde
Isolamento tra carichi di lavoro che condividono lo stesso sistema operativo gVisor, per isolamento dei carichi di lavoro

Tabella 2: Principi e strumenti di sicurezza per l'implementazione della sicurezza cloud-native in Google

Riepilogo

Questa sezione illustra come i componenti fin qui descritti operano insieme per rispondere alle richieste degli utenti nel mondo cloud-native. Si avvale di due esempi: nel primo, viene tracciata una tipica richiesta di dati dell'utente dalla creazione alla consegna a destinazione; nel secondo, viene tracciata una modifica al codice dallo sviluppo alla produzione. Non tutte le tecnologie elencate qui vengono utilizzate in tutte le parti dell'infrastruttura di Google, in base ai servizi e ai carichi di lavoro.

Accesso ai dati utente

Come mostrato nella Figura 1, quando GFE riceve una richiesta dall'utente (fase 1), termina la connessione TLS e inoltra la richiesta al frontend del servizio appropriato tramite ALTS4 (fase 2). Il frontend dell'applicazione autentica la richiesta dell'utente utilizzando un servizio di autenticazione dell'utente finale (EUA) centrale e, in caso di esito positivo, riceve un breve ticket di contesto utente finale (EUC) crittografato a breve durata (fase 3).

diagramma

Figura 1: Controlli di sicurezza dell'architettura cloud-native di Google - Accesso ai dati utente

Quindi il frontend dell'applicazione invia una RPC tramite ALTS a un servizio di backend di archiviazione, inoltrando il ticket EUC nella richiesta al backend (fase 4). Il servizio di backend utilizza il criterio di accesso ai servizi per assicurarsi che:

  1. l'identità ALTS del servizio frontend sia autorizzata a inviare richieste al servizio di backend e presenti un ticket EUC;
  2. l'identità del frontend sia protetta dalla nostra Autorizzazione binaria per Borg (BAB);
  3. il ticket EUC sia valido.

Il servizio di backend quindi controlla che l'utente nel ticket EUC sia autorizzato ad accedere ai dati richiesti. Se uno di questi controlli ha esito negativo, la richiesta viene rifiutata. In molti casi, esiste una catena di chiamate backend e ogni servizio intermedio esegue un controllo dei criteri di accesso ai servizi sulle RPC in entrata e il ticket EUC viene inoltrato sulle RPC in uscita. Se questi controlli vengono superati, i dati vengono restituiti al frontend applicazione autorizzato e forniti all'utente autorizzato.

Ciascuna macchina dispone di credenziali ALTS fornite tramite il sistema HINT, che possono essere decriptate solo se HINT ha verificato che l'avvio della macchina è avvenuto correttamente. La maggior parte dei servizi Google viene eseguita come microservizi su Borg e ognuno di questi microservizi ha la propria identità ALTS. Borgmaster 5 concede queste credenziali dei microservizi ALTS ai carichi di lavoro in base all'identità del microservizio, come descritto nella Figura 1. Le credenziali ALTS di livello macchina costituiscono il canale sicuro per il provisioning delle credenziali dei microservizi, in modo che solo le macchine che hanno superano correttamente l'avvio verificato di HINT possano ospitare effettivamente i carichi di lavoro dei microservizi.

Esecuzione di una modifica al codice

Come mostrato nella Figura 2, quando un Googler apporta una modifica a un microservizio protetto da un BAB abbastanza solido, questa deve essere inviata al nostro repository di codice sorgente centrale, che applica forzatamente una revisione del codice. Una volta approvata, la modifica viene inviata al sistema di compilazione centrale affidabile, che produce un pacchetto con un certificato firmato sotto forma di file manifest della build verificabile (fase 1). Al momento del deployment, BAB verifica che il processo sia stato seguito convalidando il certificato firmato dalla pipeline di compilazione (fase 2).

diagramma

Figura 2: Controlli di sicurezza dell'architettura cloud-native di Google. Esecuzione di una modifica al codice

Tutti gli aggiornamenti dei carichi di lavoro vengono gestiti tramite deployment blu/verde, che si tratti di un'implementazione di routine o di una patch di sicurezza di emergenza (fase 3). GFE esegue il bilanciamento del carico del traffico al nuovo deployment per garantire la continuità delle operazioni.

Tutti i carichi di lavoro devono essere isolati. Se il carico di lavoro ha un'affidabilità inferiore, ad esempio, ha un'architettura multi-tenant o il codice sorgente ha origine all'esterno di Google, il deployment può essere eseguito in un ambiente protetto da gVisor o utilizzare altri livelli di isolamento. Questo isolamento garantisce che se un'istanza dell'applicazione viene compromessa, nessun'altra istanza viene interessata.

Applicazione di BeyondProd

Ai massimi termini

Con cloud-native e applicando la sicurezza corretta all'infrastruttura, Google può offrire proprietà di sicurezza potenti per carichi di lavoro interni ed esterni (Google Cloud).

Creando componenti condivisi, il carico sui singoli sviluppatori per ottenere la conformità ai requisiti di sicurezza standard è minimo. Teoricamente, la funzionalità di sicurezza dovrebbe richiedere un'integrazione minima o nulla in ogni singola applicazione e agire piuttosto da rete che avvolge e connette tutti i microservizi. Questa è comunemente definita mesh di servizi. Ciò significa, inoltre, che la sicurezza può essere gestita e implementata separatamente rispetto alle normali attività di sviluppo o deployment.

Realizzare il passaggio a cloud-native

Il passaggio di Google a cloud-native ha richiesto modifiche in due aree principali: nella nostra infrastruttura e nel nostro processo di sviluppo. Abbiamo gestito queste modifiche simultaneamente, ma possono essere separate e affrontate in modo indipendente.

Modifica della nostra infrastruttura

Abbiamo iniziato dalla costruzione di solide fondamenta di identificazione, autenticazione e autorizzazione dei servizi. Una solida base di identità di servizio attendibili ci ha consentito di implementare funzioni di sicurezza di massimo livello dipendenti dalla convalida di tali identità, quali i criteri di accesso al servizio e i ticket EUC. Per semplificare il passaggio per i servizi nuovi ed esistenti, per prima cosa abbiamo fornito ALTS sotto forma di una libreria con un singolo daemon helper. Questo daemon veniva eseguito sull'host richiamato da ogni servizio e, nel corso del tempo, si è evoluto in una libreria che utilizza le credenziali di servizio. La libreria ALTS è stata perfettamente integrata nella libreria RPC principale e ciò ne ha semplificato l'ampia adozione senza imporre carichi significativi ai singoli team di sviluppo. Il lancio del sistema ALTS è stato un prerequisito per l'implementazione dei criteri di accesso ai servizi e dei ticket EUC.

Modifica dei nostri processi di sviluppo

Per Google, è stato fondamentale stabilire un robusto processo di compilazione e revisione del codice. Questo ci ha consentito di garantire sia l'integrità dei servizi in esecuzione, sia la significatività delle identità utilizzate da ALTS. Abbiamo stabilito un processo di compilazione centrale dove abbiamo potuto iniziare ad applicare requisiti quali la revisione del codice da parte di due persone e la verifica automatica nelle fasi di compilazione e deployment. (Consulta il white paper Autorizzazione binaria per Borg per maggiori dettagli sul deployment.)

Una volta organizzate le basi, abbiamo iniziato a occuparci dei requisiti per eseguire codice esterno non attendibile nei nostri ambienti. Per raggiungere questo obiettivo, abbiamo avviato la limitazione tramite sandbox, prima con ptrace, quindi utilizzando gVisor. Analogamente, i deployment blu/verde hanno offerto importanti vantaggi in termini di sicurezza (ad esempio nell'applicazione di patch) e di affidabilità.

Abbiamo presto scoperto che era più semplice se un servizio cominciava registrando le violazioni delle norme invece di bloccarle. Il vantaggio di questo approccio è stato duplice. Innanzitutto, offriva ai proprietari dei servizi un'opportunità di testare le modifiche e valutare l'eventuale impatto sul loro servizio del passaggio a un ambiente cloud-native. In secondo luogo, ci permetteva di correggere eventuali bug e di identificare le funzionalità aggiuntive necessarie per i team dei servizi. Ad esempio, durante l'onboarding di un servizio in BAB, i proprietari del servizio abilitano la modalità di solo controllo. Tale modalità consente di identificare codice o carichi di lavoro che non soddisfano i requisiti. Una volta risolti i problemi segnalati dalla modalità di solo controllo, i proprietari dei servizi passano alla modalità di applicazione forzata. In gVisor, questa operazione è stata eseguita partendo dalla limitazione tramite sandbox dei carichi di lavoro, anche in presenza di problemi di compatibilità con le funzioni sandbox, procedendo quindi a risolvere sistematicamente questi problemi per migliorare la capacità di limitazione.

Vantaggi della modifica

Così come BeyondCorp ci ha aiutato a superare il modello di sicurezza basato su perimetro, anche BeyondProd rappresenta un balzo in avanti nel nostro approccio alla sicurezza nella produzione. L'approccio BeyondProd descrive un'architettura di sicurezza cloud-native che non presuppone alcuna attendibilità tra i servizi, isola i carichi di lavoro, verifica che venga eseguito il deployment delle sole applicazioni compilate centralmente, automatizza la gestione delle vulnerabilità e applica rigorosi controlli di accesso a dati critici. L'architettura BeyondProd ha portato Google a innovare diversi nuovi sistemi per soddisfare tali requisiti.

Troppo spesso, la sicurezza è l'ultimo elemento considerato, quando si è già deciso di passare ad una nuova architettura. Grazie al coinvolgimento del team di sicurezza già nelle fasi iniziali e all'attenzione ai vantaggi del nuovo modello di sicurezza come la gestione più semplice delle patch e controlli di accesso più rigorosi, l'architettura cloud-native può offrire vantaggi significativi ai team di sicurezza e di sviluppo delle applicazioni. L'applicazione dei principi di sicurezza delineati in questo documento all'infrastruttura cloud-native, permette di rendere più solidi il deployment dei carichi di lavoro, la sicurezza nelle comunicazioni dei carichi di lavoro e la loro influenza sugli altri carichi di lavoro.

Note

1 Borg è il sistema di gestione dei cluster di Google per la pianificazione e l'esecuzione dei carichi di lavoro su larga scala. Borg è stato il primo sistema di gestione dei container unificato di Google, e l'ispirazione per Kubernetes.

2 Il termine inglese "shifting left" si riferisce all'anticipazione di fasi nel ciclo di vita di sviluppo del software come creazione del codice, compilazione, test, convalida e deployment. I diagrammi dei cicli di vita vengono spesso tracciati da sinistra a destra, quindi "left" indica una fase precedente.

3 Un deployment blu/verde è un modo di implementare una modifica a un carico di lavoro senza influenzare il traffico in entrata, per cui gli utenti finali non rilevano alcun tempo di inattività nell'accesso all'applicazione.

4 Per comprendere meglio il routing del traffico all'interno dell'infrastruttura di Google da GFE al servizio, vedere la sezione Modalità di routing del traffico nel white paper Crittografia in transito in Google Cloud.

5 Borgmaster è il controller centralizzato di Borg. Gestisce la programmazione dei job e comunica lo stato dei job in esecuzione.