Mi capita spesso di dover accedere a risorse AWS come S3, SQS e così via da un'applicazione in esecuzione su Google Cloud. Affidarsi alle access key e alle secret key è giustamente considerata una cattiva pratica, così ho finito per sviluppare Janus: un piccolo strumento che le consente di assumere un ruolo AWS da un'istanza Google Cloud Compute Engine tramite un service account.
Perché Janus? Perché nella mitologia romana Janus, divinità bifronte, era il dio delle porte e dei passaggi. Mi è sembrata un'analogia azzeccata ;-)
Ma cos'è esattamente Janus? È un piccolo frammento di codice che può eseguire sulla sua istanza Google Cloud Compute Engine e che le permette di assumere un ruolo AWS senza dover ricorrere alle chiavi AWS IAM.
Perché usare Janus?
Immaginiamo che debba scaricare un file privato da un bucket S3 a partire da un'istanza o da un container in esecuzione su Google Cloud. Per farlo, dovrà autenticarsi con AWS IAM.
Le opzioni di autenticazione sono diverse. La più sicura consiste nell'associare un ruolo IAM a un'istanza, ma è praticabile soltanto con un'istanza EC2 (cioè un'istanza in esecuzione sull'infrastruttura AWS).
Quando invece lavora con un'istanza Google Cloud, le opzioni si riducono e nella maggior parte dei casi si finisce per generare delle access key AWS IAM (spesso con permessi più ampi del necessario) e salvarle hardcoded.
Un approccio del genere espone a un rischio di sicurezza tutt'altro che trascurabile: la chiave può essere compromessa. E una volta trapelata, chiunque ne entri in possesso può accedere al suo account AWS, con conseguenze che vanno dalla fuga di dati al danno economico per la sua azienda. Lo scenario più "innocente" in cui mi sia imbattuto è quello di hacker che usano l'account per creare server di mining di criptovalute, oppure cifrano dati e backup chiedendo poi un riscatto per restituirli.
Come si stabilisce, quindi, una connessione sicura da GCP alle risorse AWS senza chiavi hardcoded?
Janus aiuta a superare questi limiti consentendole di assumere un ruolo tramite una Google Web Identity su una macchina GCP Compute Engine, in modo del tutto analogo all'uso di un ruolo IAM con EC2. Janus richiede ad AWS una chiave temporanea con una validità di 1 ora.
Come implementare Janus
A titolo di esempio, mostrerò come concedere l'accesso a un bucket AWS S3 con permessi di sola lettura, che utilizzerò poi nella mia istanza Google Compute.
- Acceda alla sua console GCP e selezioni IAM e amministratore dal menu di navigazione a sinistra. Poi clicchi su Service Accounts e quindi su +CREA ACCOUNT DI SERVIZIO.

2. Assegni un nome al service account e inserisca una descrizione dettagliata (è sempre buona pratica). Poi clicchi sul pulsante CREA.

3. A questo punto, clicchi sulla casella Ruolo e selezioni Service Account → Service Account Token Creator.
Più rapidamente, può digitare Token nella casella di ricerca Digita per filtrare.

4. Verrà visualizzata la pagina "Concedi agli utenti l'accesso a questo service account (facoltativo)". Quando appare, clicchi su FINE.
5. Clicchi sul nome del service account e copi l'ID univoco.

6. Acceda al suo account AWS e si porti su IAM console → Roles → Create role.

7. Come trusted entity, selezioni Web identity → Google nel campo Identity provider e incolli l'ID univoco del service account GCP nella casella Audience. Poi clicchi sul pulsante Next: Permissions.

8. Conceda i permessi al ruolo. Per questa dimostrazione utilizzeremo AmazonS3ReadOnlyAccess. Clicchi sul pulsante Next: Tags.

9. La fase dei tag è facoltativa e in questa dimostrazione la salteremo. Clicchi sul pulsante Next: Review.
10. Assegni un nome e una descrizione dettagliata al ruolo, quindi clicchi su Create role.

11. Il ruolo è pronto all'uso. Clicchi e copi il Role ARN: le servirà più avanti.

12. Torni alla GCP Console e crei una nuova istanza VM Compute Engine.

Scorra fino alla sezione Identity and API access e sostituisca il service account predefinito di Compute Engine con il service account creato al passaggio 5.

13. Ora che AWS e GCP sono entrambi configurati e possiamo assumere un ruolo AWS da GCP, ci serve uno strumento o del codice che esegua la procedura tramite la web identity di Google. È proprio da qui che è nata l'idea di Janus.
Janus restituisce credenziali AWS temporanee a tutti gli AWS SDK supportati e alla AWS CLI.
Per implementare Janus, esegua questi comandi come utente root:
$ apt install python3-pip awscli
$ pip3 install boto3 requests
$ wget https://raw.githubusercontent.com/doitintl/janus/master/janus.py -O /usr/local/bin/janus
$ chmod 555 /usr/local/bin/janus
Per eseguire Janus con il ruolo IAM desiderato, dovrà indicare l'AWS Role ARN copiato al passaggio 10.
/usr/local/bin/janus arn:aws:iam::123456789012:role/s3-ro-from-gcp
14. Crei un nuovo profilo nel file /.aws/credentials ( è una scorciatoia per la home directory dell'utente). L'SDK di AWS si serve dei profili (definiti in questo file) per completare l'autenticazione con AWS.
Se il file non esiste, lo crei:
$ mkdir ~/.aws
$ touch ~/.aws/credentials
Apra il file credentials e aggiunga un profilo (il nome predefinito è default). È possibile gestire più profili e, ai fini di questa dimostrazione, ne creeremo uno predefinito:
credential_process = /usr/local/bin/janus arn:aws:iam::123456789012:role/s3-ro-from-gcp
15. Tutto qui! Può iniziare a usare AWS dalla sua istanza GCP.
A titolo di esempio, copierò un file chiamato hello.txt dal mio bucket AWS S3 utilizzando la AWS CLI:

Come funziona?
/.aws/credentials su Mac, Linux e Unix. ( è una scorciatoia per la home directory)
oppure
C:\Users\USERNAME\.aws\credentials su Windows.
- A quel punto esegue Janus con il nome del ruolo AWS che vogliamo assumere.
- Janus richiede un token JWT al metadata server di Compute Engine, quindi assume un ruolo su AWS utilizzando il nome del ruolo indicato.
- AWS verifica che il token JWT corrisponda alla configurazione del ruolo AWS e restituisce una access key temporanea:

- Con questa access key temporanea possiamo accedere ad AWS e chiamare le API per conto del ruolo AWS.
Come posso sapere quale istanza accede al mio account AWS?
A fini di tracciamento, Janus assume il ruolo con un nome di sessione personalizzato, nel formato project-id.instance-name.
Se nel suo account ha attivato AWS CloudTrail, può filtrare tutti gli eventi AssumeRoleWithWebIdentity e tracciare ogni chiamata API effettuata.

L'evento conterrà:
- L'IP di origine della macchina GCP.
- username: l'ID univoco del service account GCP.
- Il "session name" del ruolo di sessione, ovvero project id e nome dell'istanza.
- Il nome del ruolo assunto.
Grazie all'access key possiamo poi risalire a tutte le azioni eseguite dall'istanza.