Compartir vía


Autenticación de aplicaciones Go en servicios de Azure durante el desarrollo local mediante entidades de servicio

Al crear aplicaciones en la nube, los desarrolladores deben depurar y probar aplicaciones en su estación de trabajo local. Cuando una aplicación se ejecuta en la estación de trabajo de un desarrollador durante el desarrollo local, todavía debe autenticarse en los servicios de Azure usados por la aplicación. En este artículo se explica cómo configurar objetos principal de servicio de aplicación dedicados, los cuales se usarán durante el desarrollo local.

Diagrama en el que se muestra cómo una aplicación que se ejecuta en el desarrollador local obtiene la entidad de servicio de la aplicación de un archivo .env y, a continuación, usa esa identidad para conectarse a los recursos de Azure.

Las entidades de servicio de aplicaciones dedicadas para el desarrollo local permiten seguir el principio de privilegios mínimos durante el desarrollo de aplicaciones. Dado que los permisos se limitan exactamente a lo que se necesita para la aplicación durante el desarrollo, se impide que el código de la aplicación acceda accidentalmente a un recurso de Azure destinado a su uso por otra aplicación. Esto también impide que se produzcan errores cuando la aplicación se mueve a producción porque la aplicación se ha sobreprivilegado en el entorno de desarrollo.

Cuando la aplicación se registra en Azure, se configura una entidad de servicio de aplicación. Al registrar aplicaciones para el desarrollo local, se recomienda:

  • Cree registros de aplicaciones independientes para cada desarrollador que trabaje en la aplicación. Esto creará entidades de servicio de aplicación independientes para que cada desarrollador la use durante el desarrollo local y evitará la necesidad de que los desarrolladores compartan credenciales para una sola entidad de servicio de aplicación.
  • Cree registros de aplicaciones independientes por aplicación. Esto limita los permisos de la aplicación solo a lo que necesita la aplicación.

Durante el desarrollo local, las variables de entorno se establecen con la identidad de la entidad de servicio de la aplicación. El SDK de Azure para Go lee estas variables de entorno y usa esta información para autenticar la aplicación en los recursos de Azure que necesita.

1- Registro de la aplicación en Azure

Los objetos principales de servicio de la aplicación se crean con un registro de aplicaciones en Azure. Esto se puede hacer mediante Azure Portal o la CLI de Azure.

Los comandos de Azure CLI se pueden ejecutar en el Azure Cloud Shell o en una estación de trabajo con el Azure CLI instalado.

En primer lugar, use el comando az ad sp create-for-rbac para crear una nueva entidad de servicio para la aplicación. El comando también crea el registro de la app para la aplicación al mismo tiempo.

az ad sp create-for-rbac --name <service-principal-name>

La salida de este comando tendrá un aspecto similar al siguiente. Anote estos valores o mantenga abierta esta ventana, ya que necesitará estos valores en los pasos siguientes y no podrá volver a ver el valor de contraseña (secreto de cliente). Sin embargo, puede agregar una nueva contraseña más adelante sin invalidar la entidad de servicio o las contraseñas existentes si es necesario.

{
  "appId": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "displayName": "<service-principal-name>",
  "password": "Ee5Ff~6Gg7.-Hh8Ii9Jj0Kk1Ll2Mm3_Nn4Oo5Pp6",
  "tenant": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
}

2- Creación de un grupo de seguridad de Microsoft Entra para el desarrollo local

Dado que normalmente hay varios desarrolladores que trabajan en una aplicación, se recomienda crear un grupo de seguridad de Microsoft Entra para encapsular los roles (permisos) que la aplicación necesita en el desarrollo local, en lugar de asignar los roles a objetos de entidad de servicio individuales. Esto ofrece las siguientes ventajas:

  • Se garantiza que todos los desarrolladores tienen asignados los mismos roles, ya que los roles se asignan en el nivel de grupo.
  • Si se necesita un nuevo rol para la aplicación, solo debe agregarse al grupo de Microsoft Entra correspondiente.
  • Si un nuevo desarrollador se une al equipo, se crea una nueva entidad de servicio de aplicación para el desarrollador y se agrega al grupo, lo que garantiza que el desarrollador tiene los permisos adecuados para trabajar en la aplicación.

El comando az ad group create se usa para crear grupos de seguridad en Microsoft Entra ID. Se requieren los parámetros --display-name y --main-nickname. El nombre proporcionado al grupo debe basarse en el nombre de la aplicación. También resulta útil incluir una frase como "local-dev" en el nombre del grupo para indicar el propósito del grupo.

az ad group create \
    --display-name MyDisplay \
    --mail-nickname MyDisplay  \
    --description "<group-description>"

Copie el valor de la propiedad id en la salida del comando. Este es el identificador de objeto del grupo. Lo necesitarás en pasos posteriores. También puede usar el comando az ad group show para recuperar esta propiedad.

Para agregar miembros al grupo, necesitará el id. de objeto de la entidad de servicio de la aplicación, que es diferente al id. de la aplicación. Use az ad sp list para enumerar las entidades de servicio disponibles. El comando de parámetro --filter acepta filtros de estilo OData y se puede usar para filtrar la lista como se muestra. El parámetro --query limita a las columnas solo a las de interés.

az ad sp list \
    --filter "startswith(displayName, 'msdocs')" \
    --query "[].{objectId:id, displayName:displayName}" \
    --output table

El comando az ad group member add se puede usar para agregar miembros a grupos.

az ad group member add \
    --group <group-name> \
    --member-id <object-id>

Nota

De forma predeterminada, la creación de grupos de seguridad de Microsoft Entra se limita a determinados roles con privilegios en un directorio. Si no puede crear un grupo, póngase en contacto con un administrador de su directorio. Si no puede agregar miembros a un grupo existente, póngase en contacto con el propietario del grupo o con un administrador de directorios. Para obtener más información, consulte Administrar grupos de Microsoft Entra y la membresía en grupos.

3- Asignación de roles a la aplicación

A continuación, debe determinar qué roles (permisos) necesita la aplicación en qué recursos y asignar esos roles a la aplicación. En este ejemplo, los roles se asignan al grupo Microsoft Entra creado en el paso 2. Los roles se pueden asignar en el ámbito de recurso, grupo de recursos o suscripción. En este ejemplo se muestra cómo asignar roles en el ámbito del grupo de recursos, ya que la mayoría de las aplicaciones agrupan todos sus recursos de Azure en un único grupo de recursos.

A un usuario, grupo o entidad de servicio de aplicación se le asigna el rol en Azure con el comando az role assignment create. Puede especificar un grupo con su identificador de objeto. Puede especificar una entidad de servicio de aplicación con su appId.

az role assignment create --assignee <appId or objectId> \
    --scope /subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName> \
    --role "<roleName>" 

Para obtener los nombres de roles que se pueden asignar, use el comando az role definition list.

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

Por ejemplo, para permitir que la entidad de servicio de aplicación con el appId de 00001111-aaaa-2222-bbbb-3333cccc4444 lea, escriba y elimine el acceso a los contenedores y datos de blobs de Azure Storage para todas las cuentas de almacenamiento del grupo de recursos msdocs-go-sdk-auth-example en la suscripción con ID aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e, asignaría el rol de Colaborador de datos de blobs de almacenamiento a la entidad de servicio de la aplicación mediante el siguiente comando.

az role assignment create --assignee 00001111-aaaa-2222-bbbb-3333cccc4444 \
    --scope /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/msdocs-go-sdk-auth-example \
    --role "Storage Blob Data Contributor"

Para obtener información sobre cómo asignar permisos en el nivel de recurso o suscripción mediante la CLI de Azure, consulte el artículo Asignación de roles de Azure mediante la CLI de Azure.

4- Establecimiento de variables de entorno de desarrollo local

El objeto DefaultAzureCredential buscará información sobre la entidad de servicio en un conjunto de variables de entorno en tiempo de ejecución. Dado que la mayoría de los desarrolladores trabajan en varias aplicaciones, se recomienda usar un paquete como godotenv para acceder al entorno desde un archivo .env almacenado en el directorio de la aplicación durante el desarrollo. Esto limita las variables de entorno que se usan para autenticar la aplicación en Azure de modo que esta aplicación solo las pueda usar.

El archivo .env nunca se comprueba en el control de código fuente, ya que contiene la clave secreta de aplicación para Azure. El archivo .gitignore estándar para Go excluye automáticamente el archivo .env de la protección.

Para usar el paquete godotenv, instale primero el paquete en la aplicación.

go get github.com/joho/godotenv

A continuación, cree un archivo .env en el directorio raíz de la aplicación. Establezca los valores de las variables de entorno con los valores obtenidos del proceso de registro de aplicaciones de la siguiente manera:

  • AZURE_CLIENT_ID → el valor del identificador de la aplicación.
  • AZURE_TENANT_ID → el valor del identificador de inquilino.
  • AZURE_CLIENT_SECRET → La contraseña o credencial generada para la aplicación.
AZURE_CLIENT_ID=00001111-aaaa-2222-bbbb-3333cccc4444
AZURE_TENANT_ID=aaaabbbb-0000-cccc-1111-dddd2222eeee
AZURE_CLIENT_SECRET=Ee5Ff~6Gg7.-Hh8Ii9Jj0Kk1Ll2Mm3_Nn4Oo5Pp6

Por último, en el código de inicio de la aplicación, use la biblioteca de godotenv para leer las variables de entorno del archivo .env al iniciarse.

// Imports of fmt, log, and os omitted for brevity 
import "github.com/joho/godotenv"

environment := os.Getenv("ENVIRONMENT")

if environment == "development" {
    fmt.Println("Loading environment variables from .env file")
    
    // Load the .env file
    err := godotenv.Load(".env")
    if err != nil {
        log.Fatalf("Error loading .env file: %v", err)
    }
}

5- Implementar DefaultAzureCredential en la aplicación

Para autenticar objetos de cliente del SDK de Azure en Azure, la aplicación debe usar la clase DefaultAzureCredential del paquete de azidentity. En este escenario, DefaultAzureCredential detectará que las variables de entorno AZURE_CLIENT_ID, AZURE_TENANT_IDy AZURE_CLIENT_SECRET están establecidas y leerá dichas variables para obtener la información del principal del servicio de la aplicación para conectar con Azure.

En primer lugar, agregue el paquete azidentity a la aplicación.

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

A continuación, para cualquier código de Go que esté creando un objeto de cliente del SDK de Azure en tu aplicación, debes:

  1. Importe el paquete de azidentity.
  2. Cree una instancia del tipo DefaultAzureCredential.
  3. Pase la instancia de tipo DefaultAzureCredential en el constructor del cliente del SDK de Azure.

Un ejemplo de esto se muestra en el siguiente segmento de código.

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>)
}