
Non è raro che un'applicazione in esecuzione su Google Kubernetes Engine ( GKE) debba accedere alle API di Amazon Web Services ( AWS). Le esigenze possono essere le più diverse: eseguire una query analitica su Amazon Redshift, leggere dati da un bucket Amazon S3, convertire testo in voce con Amazon Polly o sfruttare un altro servizio AWS. Uno scenario multi-cloud ormai all'ordine del giorno, visto che le aziende lavorano sempre più spesso con più cloud provider.

L'accesso cross-cloud porta con sé una nuova sfida: come gestire le credenziali necessarie a raggiungere, da un cloud provider, i servizi in esecuzione su un altro. L'approccio più ingenuo, ovvero distribuire e archiviare i secret del cloud provider, non è certo il più sicuro: distribuire credenziali a lungo termine a ogni servizio che deve accedere ad AWS è oneroso da gestire e rappresenta un potenziale rischio per la sicurezza.
Le soluzioni attuali
Ogni cloud propone la propria soluzione specifica per affrontare il problema e, finché si lavora con un singolo provider, è più che sufficiente.
Google Cloud ha introdotto Workload Identity, il metodo consigliato per consentire alle applicazioni GKE di autenticarsi e utilizzare gli altri servizi Google Cloud. Workload Identity collega tra loro i Kubernetes service account e i Cloud IAM service account: è così possibile sfruttare i concetti nativi di Kubernetes per definire con quali identità eseguire i workloads e consentire loro di accedere automaticamente agli altri servizi Google Cloud, senza dover gestire né secret di Kubernetes né chiavi dei service account IAM. Per approfondire si rimanda al post del blog DoiT Kubernetes GKE Workload Identity.
Amazon Web Services offre una funzionalità analoga con IAM Roles for Service Accounts. Sui cluster Amazon EKS è possibile associare un ruolo IAM a un Kubernetes service account, che a sua volta concederà le autorizzazioni AWS ai container di qualsiasi pod che lo utilizzi. In questo modo non serve più assegnare autorizzazioni estese al ruolo IAM del worker node per permettere ai pod di chiamare le API AWS.
E se invece il workload applicativo gira su un cluster GKE e si vuole accedere ai servizi AWS senza scendere a compromessi sulla sicurezza?
Definizione del caso d'uso
Ipotizziamo di avere già un account AWS e un cluster GKE, e che l'azienda abbia deciso di eseguire un'applicazione a microservizi sul cluster GKE pur volendo continuare a utilizzare risorse nell'account AWS (servizi Amazon S3 e SNS) per integrarsi con altri sistemi presenti su AWS.
Ad esempio, l'orchestration job (distribuito come Kubernetes Job) viene eseguito all'interno di un cluster GKE e deve caricare un file di dati in un bucket S3 e inviare un messaggio a un topic Amazon SNS. La riga di comando equivalente potrebbe essere:
https://gist.github.com/90682305b879a96d273284df5d20fdcb
Un esempio piuttosto semplice. Affinché questi comandi vadano a buon fine, l'orchestration job deve disporre delle credenziali AWS, e queste devono essere abilitate a effettuare le relative chiamate API.
L'approccio ingenuo (e poco sicuro): credenziali IAM a lungo termine
Esportare l'Access Key e la Secret Key AWS di un IAM User e iniettare le credenziali AWS nell'orchestration job, sotto forma di file di credenziali o variabili d'ambiente. Probabilmente non in modo diretto, ma tramite la risorsa Kubernetes Secrets, protetta da una policy di autorizzazione RBAC.
Il problema è che queste credenziali non scadono mai. Vanno trasferite in qualche modo dall'ambiente AWS a quello GCP e, nella maggior parte dei casi, si tende a conservarle da qualche parte per poter ricreare l'orchestration job in seguito, all'occorrenza.
Quando si usano credenziali AWS a lungo termine, i modi in cui un account AWS può essere compromesso sono molti: commit involontari delle credenziali su un repository GitHub, salvataggio in una Wiki, riutilizzo delle stesse credenziali tra servizi e applicazioni differenti, accessi senza restrizioni e altro ancora.
Per quanto sia possibile progettare una soluzione adeguata di gestione delle credenziali emesse per gli IAM User, il modo migliore per evitare il problema è non crearle affatto.
L'approccio proposto
L'idea di fondo è assegnare un ruolo IAM AWS a un Pod GKE, in modo analogo a quanto fanno le funzionalità cloud-native Workload Identity ed EKS IAM Roles for Service Accounts.
Per nostra fortuna, AWS permette di creare un ruolo IAM per provider di identità basati sulla federazione OpenID Connect (OIDC) anziché per IAM User. Dall'altra parte, Google implementa un provider OIDC e lo integra strettamente con GKE tramite la funzionalità Workload Identity, fornendo un token OIDC valido al pod GKE eseguito sotto un Kubernetes Service Account collegato a un Google Cloud Service Account. Sono tutti tasselli che tornano utili per realizzare un accesso sicuro da GKE ad AWS.
Da access token OIDC a ID token
Manca però un tassello per completare il puzzle. Con Workflow Identity configurato correttamente, il Pod GKE riceve un access token OIDC che consente di accedere ai servizi Google Cloud. Per ottenere credenziali AWS temporanee dall'AWS Security Token Service ( STS), serve invece un ID token OIDC valido.
L'AWS SDK (e lo strumento aws-cli) richiederà automaticamente le credenziali AWS temporanee al servizio STS, a patto che le seguenti variabili d'ambiente siano impostate correttamente:
AWS_WEB_IDENTITY_TOKEN_FILE- il percorso del file contenente il web identity token (OIDC ID token)AWS_ROLE_ARN- l'ARN del ruolo che i container del Pod devono assumereAWS_ROLE_SESSION_NAME- il nome assegnato a questa sessione di assume-role
Il meccanismo può sembrare un po' complesso, ma di seguito troverete una guida passo passo e un progetto open source di supporto, dointl/gtoken, pensato per semplificare la configurazione.
Il Mutating Admission webhook Kubernetes gtoken-webhook
gtoken-webhook è un mutating admission webhook di Kubernetes che modifica qualsiasi Pod K8s eseguito sotto un Kubernetes Service Account opportunamente annotato (vedere i dettagli più avanti).
Flusso di mutazione di gtoken-webhook
gtoken-webhook inietta un gtoken``initContainer nel Pod di destinazione e un container gtoken aggiuntivo come sidekick (per rinnovare l'ID token OIDC poco prima della scadenza), monta il volume del token e inietta tre variabili d'ambiente specifiche per AWS. Il container gtoken genera un ID token OIDC GCP valido e lo scrive sul volume del token, oltre a iniettare le variabili d'ambiente AWS richieste.
L'AWS SDK effettuerà poi automaticamente le corrispondenti chiamate AssumeRoleWithWebIdentity ad AWS STS, occupandosi del caching in memoria e del rinnovo delle credenziali quando necessario.

Guida al flusso di configurazione
Distribuire gtoken-webhook
- Per distribuire il server
gtoken-webhookoccorre creare un servizio webhook e un deployment nel cluster Kubernetes. È un'operazione piuttosto lineare, fatta eccezione per un dettaglio: la configurazione TLS del server. Esaminando il file deployment.yaml si nota che il certificato e la corrispondente chiave privata vengono letti dagli argomenti della riga di comando e che il loro percorso proviene da un volume mount che punta a un Kubernetes secret:
https://gist.github.com/b9fc2fe5acb556e3b48ee89af7db368e
L'aspetto più importante da non dimenticare è impostare il certificato CA corrispondente più avanti, nella configurazione del webhook, in modo che l'apiserver sappia che deve essere accettato. Per ora riutilizzeremo lo script originariamente scritto dal team di Istio per generare una richiesta di firma del certificato. Invieremo poi la richiesta all'API di Kubernetes, recupereremo il certificato e creeremo a partire dal risultato il secret necessario.
Per prima cosa, eseguire lo script webhook-create-signed-cert.sh e verificare che il secret contenente il certificato e la chiave sia stato creato:
https://gist.github.com/91d2bd8cddf116aa0fa8cefbf3208bcd
Una volta creato il secret, possiamo definire deployment e servizio. Si tratta di risorse Kubernetes deployment e service standard. Fino a questo punto abbiamo prodotto solo un server HTTP che accetta richieste tramite il servizio sulla porta 443:
https://gist.github.com/34ec56018700a610f37a09a9e846eecb
Configurare il Mutating Admission webhook
Ora che il server webhook è in esecuzione può accettare richieste dall'apiserver. Prima, però, dobbiamo creare in Kubernetes alcune risorse di configurazione. Iniziamo dal validating webhook, per poi configurare il mutating webhook in un secondo momento. Aprendo la configurazione del webhook si nota che contiene un placeholder per CA_BUNDLE:
https://gist.github.com/d40101fba8203cc77d62629dde4d6a8f
Esiste un piccolo script che sostituisce nel file di configurazione il placeholder CA_BUNDLE con questa CA. Eseguire questo comando prima di creare la configurazione del validating webhook:
https://gist.github.com/5e0ed44892fc7a21709ac18b381cb5f6
Creare una configurazione di mutating webhook:
https://gist.github.com/a545e146e1168f3b88d04b3f4c490d68
Configurare RBAC per gtoken-webhook
Creare un Kubernetes Service Account da utilizzare con gtoken-webhook:
https://gist.github.com/aabfa30bc005ae8654b360814523c9bc
Definire le autorizzazioni RBAC per il service account del webhook:
https://gist.github.com/5ceb8a6e1ca19d9be3b8ed2d810ddc96
Variabili del flusso
Alcune delle variabili che seguono devono essere fornite dall'utente, altre vengono generate automaticamente e riutilizzate nei passaggi successivi.
PROJECT_ID- ID del progetto GCP (fornito dall'utente)CLUSTER_NAME- nome del cluster GKE (fornito dall'utente)GSA_NAME- nome del Google Cloud Service Account (fornito dall'utente)GSA_ID- ID univoco del Google Cloud Service Account (generato da Google)KSA_NAME- nome del Kubernetes Service Account (fornito dall'utente)KSA_NAMESPACE- namespace Kubernetes (fornito dall'utente)AWS_ROLE_NAME- nome del ruolo IAM AWS (fornito dall'utente)AWS_POLICY_NAME- una policy IAM AWS da assegnare al ruolo IAM (fornita dall'utente)AWS_ROLE_ARN- identificatore ARN del ruolo IAM AWS (generato da AWS)
Google Cloud: abilitare GKE Workload Identity
Creare un nuovo cluster GKE con Workload Identity abilitato:
https://gist.github.com/0fcbc309cd1967edac19bac6795d651d
oppure aggiornare un cluster esistente:
https://gist.github.com/05a94c349aa4a274fc27daae45448be9
Google Cloud: creare un Google Cloud Service Account
Creare un Google Cloud Service Account:
https://gist.github.com/1790d9e61c63123937d01c8146c1a67d
Aggiornare il Google Service Account GSA_NAME con i seguenti ruoli:
roles/iam.workloadIdentityUser- per impersonare i service account dai workloads GKEroles/iam.serviceAccountTokenCreator- per impersonare i service account allo scopo di creare access token OAuth2, firmare blob o firmare token JWT
https://gist.github.com/19570300e584798d4d5c3a234ceb0d62
AWS: creare un ruolo IAM AWS con federazione OIDC Google
Preparare un documento di trust policy del ruolo per il provider OIDC Google:
https://gist.github.com/d80514354a5270bd0f6766dee7947902
Creare il ruolo IAM AWS con Google Web Identity:
https://gist.github.com/94f0ce213cf60cfce6ac987af51afbed
Assegnare al ruolo AWS le policy desiderate:
https://gist.github.com/afd9ecc776cfba5148137769aaf71a5f
Recuperare l'ARN del ruolo AWS da utilizzare nell'annotazione del K8s SA:
https://gist.github.com/d407769dffd4eb276ed22f7b723cbdc4
GKE: creare un Kubernetes Service Account
Creare il namespace K8s:
https://gist.github.com/59dafb6baf1223552eb7d9fc3c3cbea3
Creare il K8s Service Account:
https://gist.github.com/58fd6924e79270857f39d924a0f12011
Annotare il K8s Service Account con GKE Workload Identity (email del Service Account GCP):
https://gist.github.com/0be380a02ff228a38b2a9e5400b75dcb
Annotare il K8s Service Account con l'ARN del ruolo AWS:
https://gist.github.com/01620978944525a8df6c3d76ddc1fb67
Eseguire la demo
Eseguire un nuovo Pod K8s con il Service Account K8s ``${KSA_NAME}```:
https://gist.github.com/9c5a30f5dd3825d5c3bc6adc14b76fbc
Riferimenti esterni
- GitHub: Accesso sicuro ai servizi AWS dal cluster GKE con doitintl/gtoken
- Documentazione AWS: Creating a Role for Web Identity or OpenID Connect Federation
- Blog: Kubernetes GKE Workload Identity link
- Blog AWS: Introducing fine-grained IAM roles for service accounts link
- GitHub: AWS Auth tramite Web Identity Federation da Google Cloud, progetto GitHub shrikant0013/gcp-aws-webidentityfederation
- Blog: Using GCP Service Accounts to access AWS IAM Roles, articolo del blog di Colin Panisset
In sintesi
Spero che questo articolo le sia utile. Resto a disposizione per commenti e domande.
Vuole leggere altri articoli? Dia un'occhiata al nostro blog oppure segua Alexei su Twitter.