Condividi tramite


Autenticazione di app ospitate in Azure in risorse di Azure con Azure SDK per Go

Quando si ospita un'app in Azure usando servizi come Azure App Service, Azure Virtual Machines o Azure Container Instances, l'approccio consigliato per autenticare un'app alle risorse di Azure è con identità gestita.

Un'identità gestita fornisce un'identità per l'app in modo che possa connettersi ad altre risorse di Azure senza la necessità di usare una chiave privata o un altro segreto dell'applicazione. Internamente, Azure conosce l'identità dell'app e le risorse a cui è consentito connettersi. Azure usa queste informazioni per ottenere automaticamente i token di Microsoft Entra per consentire all'app di connettersi ad altre risorse di Azure, senza dover gestire i segreti dell'applicazione.

Nota

Le app in esecuzione su Azure Kubernetes Service (AKS) possono utilizzare un'identità di carico di lavoro per autenticarsi con le risorse di Azure. Nel servizio AKS, un'identità di carico di lavoro rappresenta una relazione di fiducia tra un'identità gestita e un account di servizio Kubernetes. Se un'applicazione distribuita in AKS è configurata con un account di servizio Kubernetes in una relazione di questo tipo, DefaultAzureCredential autentica l'app ad Azure utilizzando l'identità gestita. L'autenticazione tramite un'identità del carico di lavoro è descritta in Usare l'ID del carico di lavoro Microsoft Entra con il servizio Azure Kubernetes. Per informazioni su come configurare l'identità del carico di lavoro, vedere Distribuire e configurare l'identità del carico di lavoro in un cluster del servizio Azure Kubernetes.

Tipi di identità gestiti

Esistono due tipi di identità gestite:

  • identità gestite assegnate dal sistema: questo tipo di identità gestita viene fornito e associato direttamente a una risorsa di Azure. Quando si abilita l'identità gestita in una risorsa di Azure, si ottiene un'identità gestita assegnata dal sistema per tale risorsa. Un'identità gestita assegnata dal sistema è associata al ciclo di vita della risorsa di Azure a cui è associata. Quando la risorsa viene eliminata, Azure elimina l'identità per te automaticamente. Poiché è necessario abilitare l'identità gestita per la risorsa di Azure che ospita il codice, questo approccio è il tipo più semplice di identità gestita da usare.
  • identità gestite assegnate dall'utente: è anche possibile creare un'identità gestita come risorsa di Azure autonoma. Questo approccio viene usato più di frequente quando la soluzione ha più carichi di lavoro eseguiti su più risorse di Azure che devono tutti condividere la stessa identità e le stesse autorizzazioni. Ad esempio, se la soluzione include componenti eseguiti in più istanze del servizio app e delle macchine virtuali che devono accedere allo stesso set di risorse di Azure, un'identità gestita assegnata dall'utente usata tra queste risorse ha senso.

Questo articolo illustra i passaggi per abilitare e usare un'identità gestita assegnata dal sistema per un'app. Se è necessario usare un'identità gestita assegnata dall'utente, vedere l'articolo Gestire le identità gestite assegnate dall'utente per informazioni su come creare un'identità gestita assegnata dall'utente.

1 - Abilitare l'identità gestita nella risorsa di Azure che ospita l'app

Il primo passaggio consiste nell'abilitare l'identità gestita nella risorsa di Azure che ospita l'app. Ad esempio, se stai ospitando un'applicazione Gin usando Azure Container Apps, è necessario abilitare l'identità gestita per l'applicazione in contenitore. Se si usa una macchina virtuale per ospitare l'app, si abiliterà la macchina virtuale all'uso dell'identità gestita.

È possibile abilitare l'identità gestita da usare per una risorsa di Azure usando il portale di Azure o l'interfaccia della riga di comando di Azure.

I comandi dell'interfaccia della riga di comando di Azure possono essere eseguiti nel di Azure Cloud Shell o in una workstation con l'interfaccia della riga di comando di Azure installata.

I comandi dell'interfaccia della riga di comando di Azure usati per abilitare l'identità gestita per una risorsa di Azure sono nel formato az <command-group> identity --resource-group <resource-group-name> --name <resource-name>. Di seguito sono riportati comandi specifici per i servizi di Azure più diffusi.

az containerapp identity assign \
    --resource-group <resource-group-name> \
    --name <container-app-name> \
    --system-assigned

L'output sarà simile al seguente.

{
  "principalId": "aaaaaaaa-bbbb-cccc-1111-222222222222",
  "tenantId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
  "type": "SystemAssigned",
  "userAssignedIdentities": null
}

Il valore principalId è l'ID univoco dell'identità gestita. Mantenere una copia di questo output perché questi valori saranno necessari nel passaggio successivo.

2 - Assegnare ruoli all'identità gestita

Successivamente, è necessario determinare i ruoli (autorizzazioni) necessari per l'app e assegnare l'identità gestita a tali ruoli in Azure. Un'identità gestita può essere assegnata a ruoli a livello di risorsa, gruppo di risorse o ambito di sottoscrizione. Questo esempio illustra come assegnare ruoli nell'ambito del gruppo di risorse perché la maggior parte delle applicazioni raggruppa tutte le risorse di Azure in un singolo gruppo di risorse.

Ad un'identità gestita viene assegnato un ruolo in Azure utilizzando il comando az role assignment create. Per l'assegnatario, usare il codice principalId copiato nel passaggio 1.

az role assignment create --assignee {managedIdentityprincipalId} \
    --scope /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName} \
    --role "{roleName}" 

Per ottenere i nomi di ruolo a cui è possibile assegnare un principale del servizio, usare il comando az role definition list.

az role definition list \
    --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \
    --output table

Ad esempio, per consentire all'identità gestita con l'ID aaaaaaaa-bbbb-cccc-1111-222222222222 l'accesso in lettura, scrittura ed eliminazione ai contenitori e ai dati BLOB di Archiviazione di Azure in tutti gli account di archiviazione nel gruppo di risorse your-resource-group-name della sottoscrizione con ID aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e, si assegnerebbe l'entità servizio dell'applicazione al ruolo di Storage Blob Data Contributor usando il comando seguente.

az role assignment create --assignee aaaaaaaa-bbbb-cccc-1111-222222222222 \
    --scope /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/your-resource-group-name \
    --role "Storage Blob Data Contributor"

Per informazioni sull'assegnazione di autorizzazioni a livello di risorsa o sottoscrizione tramite l'interfaccia della riga di comando di Azure, vedere l'articolo Assegnare ruoli di Azure usando l'interfaccia della riga di comando di Azure.

3 - Implementare DefaultAzureCredential nell'applicazione

Quando il codice è in esecuzione in Azure e l'identità gestita è stata abilitata nella risorsa di Azure che ospita l'app, il DefaultAzureCredential determina le credenziali da usare nell'ordine seguente:

  1. Controllare l'ambiente per un'entità servizio come definito dalle variabili di ambiente AZURE_CLIENT_ID, AZURE_TENANT_IDe AZURE_CLIENT_SECRET o AZURE_CLIENT_CERTIFICATE_PATH e (facoltativamente) AZURE_CLIENT_CERTIFICATE_PASSWORD.
  2. Verificare la variabile di ambiente AZURE_CLIENT_ID per ottenere l'ID client di un'identità gestita assegnata a un utente.
  3. Usare l'identità gestita assegnata dal sistema per la risorsa di Azure, se abilitata.

In questo articolo viene usata l'identità gestita assegnata dal sistema per un'app Azure Container, quindi non è necessario configurare un'identità gestita nell'ambiente o passarla come parametro. I passaggi seguenti illustrano come usare DefaultAzureCredential.

Aggiungere prima di tutto il pacchetto azidentity all'applicazione.

go get github.com/Azure/azure-sdk-for-go/sdk/azidentity

Successivamente, per qualsiasi codice Go che crea un'istanza di un client Azure SDK nell'app, è necessario:

  1. Importare il pacchetto azidentity.
  2. Creare un'istanza del tipo DefaultAzureCredential.
  3. Passare l'istanza di tipo DefaultAzureCredential al costruttore del client Azure SDK.

Un esempio di questi passaggi è mostrato nel segmento di codice seguente con un client Blob di Azure Storage.

import (
	"context"

	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
)

const (
	account       = "https://<replace_with_your_storage_account_name>.blob.core.windows.net/"
	containerName = "sample-container"
	blobName      = "sample-blob"
	sampleFile    = "path/to/sample/file"
)

func main() {
    // create a credential
    cred, err := azidentity.NewDefaultAzureCredential(nil)
    if err != nil {
      // TODO: handle error
    }
    
    // create a client for the specified storage account
    client, err := azblob.NewClient(account, cred, nil)
    if err != nil {
      // TODO: handle error
    }
    
    // TODO: perform some action with the azblob Client
    // _, err = client.DownloadFile(context.TODO(), <containerName>, <blobName>, <target_file>, <DownloadFileOptions>)
}

Come descritto nell'articolo panoramica dell'autenticazione di Azure SDK for Go, DefaultAzureCredential supporta più metodi di autenticazione e determina il metodo di autenticazione usato in fase di esecuzione. Il vantaggio di questo approccio è che l'app può usare metodi di autenticazione diversi in ambienti diversi senza implementare codice specifico dell'ambiente. Quando il codice precedente viene eseguito nella workstation durante lo sviluppo locale, DefaultAzureCredential userà un'entità servizio dell'applicazione determinata dalle impostazioni dell'ambiente oppure utilizzando le credenziali dello strumento di sviluppo per l'autenticazione con altre risorse di Azure. Di conseguenza, lo stesso codice può essere usato per autenticare l'app nelle risorse di Azure durante lo sviluppo locale e quando viene distribuito in Azure.

Importante

DefaultAzureCredential semplifica l'autenticazione durante lo sviluppo di applicazioni distribuite in Azure combinando le credenziali usate negli ambienti di hosting di Azure e le credenziali usate nello sviluppo locale. Nell'ambiente di produzione è preferibile usare un tipo di credenziale specifico, in modo che l'autenticazione sia più prevedibile e più semplice da eseguire.

4 - Implementare ManagedIdentityCredential nell'applicazione

I passaggi per l'implementazione di ManagedIdentityCredential sono gli stessi di quelli per l'uso del tipo DefaultAzureCredential.

Aggiungere prima di tutto il pacchetto azidentity all'applicazione.

go get github.com/Azure/azure-sdk-for-go/sdk/azidentity

Successivamente, per ogni codice Go che crea un'istanza di un client Azure SDK nella tua app, è necessario:

  1. Importare il pacchetto azidentity.
  2. Creare un'istanza di tipo ManagedIdentityCredential.
  3. Passare l'istanza di tipo ManagedIdentityCredential al costruttore del client SDK di Azure.

Un esempio di questi passaggi è illustrato nel segmento di codice seguente con un client di archiviazione di Blob di Azure.

import (
	"context"

	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
)

const (
	account       = "https://<replace_with_your_storage_account_name>.blob.core.windows.net/"
	containerName = "sample-container"
	blobName      = "sample-blob"
	sampleFile    = "path/to/sample/file"
)

func main() {
    // create a credential
    cred, err := azidentity.NewManagedIdentityCredential(nil)
    
    // When using User Assigned Managed Identity use this instead and pass your client id in the options
    // clientID := azidentity.ClientID("abcd1234-...")
    // opts := azidentity.ManagedIdentityCredentialOptions{ID: clientID}
    // cred, err := azidentity.NewManagedIdentityCredential(&opts)
    
    if err != nil {
      // TODO: handle error
    }
    
    // create a client for the specified storage account
    client, err := azblob.NewClient(account, cred, nil)
    if err != nil {
      // TODO: handle error
    }
    
    // TODO: perform some action with the azblob Client
    // _, err = client.DownloadFile(context.TODO(), <containerName>, <blobName>, <target_file>, <DownloadFileOptions>)
}