Configuración de conexiones sin contraseña entre varias aplicaciones y servicios de Azure
Artículo
Las aplicaciones a menudo requieren conexiones seguras entre varios servicios de Azure simultáneamente. Por ejemplo, una instancia de Azure App Service empresarial podría conectarse a varias cuentas de almacenamiento diferentes, una instancia de base de datos de Azure SQL, un bus de servicio, etc.
Las identidades administradas son la opción de autenticación recomendada para conexiones seguras y sin contraseña entre recursos de Azure. Los desarrolladores no tienen que realizar un seguimiento manual y administrar muchos secretos diferentes para las identidades administradas, ya que Azure controla internamente la mayoría de estas tareas. En este tutorial se explora cómo administrar las conexiones entre varios servicios mediante identidades administradas y la biblioteca cliente de Azure Identity.
Comparación de los tipos de identidades administradas
Azure proporciona los siguientes tipos de identidades administradas:
Las identidades administradas asignadas por el sistema están vinculadas directamente a un único recurso de Azure. Al habilitar una identidad administrada asignada por el sistema en un servicio, Azure creará una identidad vinculada y controlará las tareas administrativas de esa identidad internamente. Cuando se elimina el recurso de Azure, también se elimina la identidad.
Las identidades administradas asignadas por el usuario son identidades independientes creadas por un administrador y se pueden asociar a uno o varios recursos de Azure. El ciclo de vida de la identidad es independiente de esos recursos.
Las identidades administradas se implementan generalmente en el código de la aplicación a través de una clase llamada DefaultAzureCredential desde la biblioteca cliente Azure.Identity. DefaultAzureCredential admite varios métodos de autenticación y determina automáticamente qué se debe usar en tiempo de ejecución. Puede obtener más información sobre este enfoque en la introducción a DefaultAzureCredential.
Conexión de una aplicación hospedada de Azure a varios servicios de Azure
Se le ha encargado conectar una aplicación existente a varios servicios y bases de datos de Azure mediante conexiones sin contraseña. La aplicación es una API web de ASP.NET Core hospedada en Azure App Service, aunque los pasos siguientes también se aplican a otros entornos de hospedaje de Azure, como Azure Spring Apps, Virtual Machines, Container Apps y AKS.
Este tutorial se aplica a las siguientes arquitecturas, aunque también se puede adaptar a muchos otros escenarios mediante cambios mínimos de configuración.
En los pasos siguientes se muestra cómo configurar una aplicación para usar una identidad administrada asignada por el sistema y su cuenta de desarrollo local para conectarse a varios servicios de Azure.
Creación de una identidad administrada asignada por el sistema
En Azure Portal, vaya a la aplicación hospedada que desea conectar a otros servicios.
En la página de información general del servicio, seleccione Identidad.
Cambie la opción Estado a Activado para habilitar una identidad administrada asignada por el sistema para el servicio.
Asignación de roles a la identidad administrada para cada servicio conectado
Vaya a la página de información general de la cuenta de almacenamiento a la que desea conceder acceso a su identidad.
Seleccione Access Control (IAM) en la navegación de la cuenta de almacenamiento.
Elija + Agregar y, luego, Agregar asignación de roles.
En el cuadro de búsqueda Rol, busque Colaborador de datos de Storage Blob, que concede permisos para realizar operaciones de lectura y escritura en datos de blobs. Puede asignar cualquier rol que sea adecuado para su caso de uso. Seleccione Colaborador de datos de blobs de almacenamiento en la lista y elija Siguiente.
En la pantalla Agregar asignación de roles, en la opción Asignar acceso a, seleccione Identidad administrada. A continuación, elija +Seleccionar miembros.
En el control flotante, escriba el nombre del servicio de aplicaciones para buscar la identidad administrada que creó. Seleccione la identidad asignada por el sistema y, a continuación, elija Seleccionar para cerrar el menú flotante.
Seleccione Siguiente un par de veces hasta que pueda seleccionar Revisar y asignar para finalizar la asignación de roles.
Repita este proceso para los demás servicios a los que desea conectarse.
Consideraciones de desarrollo local
También puede habilitar el acceso a los recursos de Azure para el desarrollo local mediante la asignación de roles a una cuenta de usuario de la misma manera que asignó roles a la identidad administrada.
Después de asignar el rol Colaborador de datos de Storage Blob a la identidad administrada, en Asignar acceso a, esta vez seleccione Usuario, grupo o entidad de servicio. Elija + Seleccionar miembros para volver a abrir el menú flotante.
Busque la cuenta de usuario@dominio o el grupo de seguridad de Microsoft Entra al que desea conceder acceso por dirección de correo electrónico o nombre y selecciónelo. Debe ser la misma cuenta que usa para iniciar sesión en las herramientas de desarrollo local, como Visual Studio o la CLI de Azure.
Nota:
También puede asignar estos roles a un grupo de seguridad de Microsoft Entra si trabaja en un equipo con varios desarrolladores. Después, puede colocar cualquier desarrollador dentro del grupo que necesite acceso para desarrollar la aplicación localmente.
Dentro del proyecto, agregue una referencia al paquete de NuGet Azure.Identity. Esta biblioteca contiene todas las entidades necesarias para implementar DefaultAzureCredential. También puede agregar cualquier otra biblioteca de Azure que sea relevante para la aplicación. En este ejemplo, los paquetes Azure.Storage.Blobs y Azure.KeyVault.Keys se agregan para conectarse a Blob Storage y Key Vault.
En la parte superior del archivo Program.cs, agregue lo siguiente mediante instrucciones:
using Azure.Identity;
using Azure.Storage.Blobs;
using Azure.Security.KeyVault.Keys;
En el archivo Program.cs del código del proyecto, cree instancias de los servicios necesarios a los que se conectará la aplicación. Los ejemplos siguientes se conectan a Blob Storage y Service Bus mediante las clases de SDK correspondientes.
var blobServiceClient = new BlobServiceClient(
new Uri("https://<your-storage-account>.blob.core.windows.net"),
new DefaultAzureCredential(credOptions));
var serviceBusClient = new ServiceBusClient("<your-namespace>", new DefaultAzureCredential());
var sender = serviceBusClient.CreateSender("producttracking");
En el proyecto, agregue la dependencia azure-identity a su archivo pom.xml. Esta biblioteca contiene todas las entidades necesarias para implementar DefaultAzureCredential. También puede agregar cualquier dependencia de Azure que sea relevante para la aplicación. En este ejemplo, las dependencias azure-storage-blob y azure-messaging-servicebus se agregan para conectarse a Blob Storage y Key Vault.
En el código del proyecto, cree instancias de los servicios necesarios a los que se conectará la aplicación. Los ejemplos siguientes se conectan a Blob Storage y Service Bus mediante las clases de SDK correspondientes.
class Demo {
public static void main(String[] args) {
DefaultAzureCredential defaultAzureCredential = new DefaultAzureCredentialBuilder().build();
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
.endpoint("https://<your-storage-account>.blob.core.windows.net")
.credential(defaultAzureCredential)
.buildClient();
ServiceBusClientBuilder clientBuilder = new ServiceBusClientBuilder().credential(defaultAzureCredential);
ServiceBusSenderClient serviceBusSenderClient = clientBuilder.sender()
.queueName("producttracking")
.buildClient();
}
}
En el mismo proyecto, solo es necesario agregar las dependencias de servicio que use. En este ejemplo, las dependencias spring-cloud-azure-starter-storage-blob y spring-cloud-azure-starter-servicebus se agregan para conectarse a Blob Storage y Key Vault.
En el código del proyecto, cree instancias de los servicios necesarios a los que se conectará la aplicación. Los ejemplos siguientes se conectan a Blob Storage y Service Bus mediante las clases de SDK correspondientes.
@Service
public class ExampleService {
@Autowired
private BlobServiceClient blobServiceClient;
@Autowired
private ServiceBusSenderClient serviceBusSenderClient;
}
Dentro del proyecto, use npm para agregar una referencia al paquete @azure/identity. Esta biblioteca contiene todas las entidades necesarias para implementar DefaultAzureCredential. Instale cualquier otra bibliotecas de Azure SDK que sean relevantes para la aplicación.
En la parte superior del archivoindex.js, agregue las siguientes instrucciones import para importar las clases de cliente necesarias para los servicios a los que se conectará la aplicación:
import { DefaultAzureCredential } from "@azure/identity";
import { BlobServiceClient } from "@azure/storage-blob";
import { KeyClient } from "@azure/keyvault-keys";
En el archivo index.js, cree objetos de cliente para los servicios de Azure a los que se conectará la aplicación. Los ejemplos siguientes se conectan a Blob Storage y Key Vault mediante las clases de SDK correspondientes.
// Azure resource names
const storageAccount = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const keyVaultName = process.env.AZURE_KEYVAULT_NAME;
// Create client for Blob Storage using managed identity
const blobServiceClient = new BlobServiceClient(
`https://${storageAccount}.blob.core.windows.net`,
new DefaultAzureCredential()
);
// Create client for Key Vault using managed identity
const keyClient = new KeyClient(`https://${keyVaultName}.vault.azure.net`, new DefaultAzureCredential());
// Create a new key in Key Vault
const result = await keyClient.createKey(keyVaultName, "RSA");
Cuando este código de aplicación se ejecuta localmente, DefaultAzureCredential buscará una cadena de credenciales para las primeras credenciales disponibles. Si Managed_Identity_Client_ID es null localmente, usará automáticamente las credenciales de la CLI de Azure local o del inicio de sesión de Visual Studio. Puede obtener más información sobre este proceso en la introducción a la biblioteca de identidades de Azure.
Cuando la aplicación se implemente en Azure, DefaultAzureCredential recuperará automáticamente la variable Managed_Identity_Client_ID del entorno de App Service. Ese valor está disponible cuando una identidad administrada está asociada a la aplicación.
Este proceso general garantiza que la aplicación se pueda ejecutar localmente de forma segura y en Azure sin necesidad de realizar cambios en el código.
Conexión de varias aplicaciones mediante varias identidades administradas
Aunque las aplicaciones del ejemplo anterior comparten todos los mismos requisitos de acceso de servicio, los entornos reales suelen tener un poco más de matices. Considere un escenario en el que varias aplicaciones se conectan a las mismas cuentas de almacenamiento, pero dos de las aplicaciones también acceden a diferentes servicios o bases de datos.
Para configurar estos parámetros en el código, asegúrese de que la aplicación registra servicios independientes para conectarse a cada cuenta de almacenamiento o base de datos. Asegúrese de extraer los identificadores de cliente de identidad administrada correctos para cada servicio al configurar DefaultAzureCredential. En el ejemplo de código siguiente se configuran las siguientes conexiones de servicio:
Dos conexiones a cuentas de almacenamiento independientes mediante una identidad administrada asignada por el usuario compartida
Una conexión a los servicios de Azure Cosmos DB y Azure SQL mediante una segunda identidad administrada asignada por el usuario compartida
// Get the first user-assigned managed identity ID to connect to shared storage
const clientIdStorage = Environment.GetEnvironmentVariable("Managed_Identity_Client_ID_Storage");
// First blob storage client that using a managed identity
BlobServiceClient blobServiceClient = new BlobServiceClient(
new Uri("https://<receipt-storage-account>.blob.core.windows.net"),
new DefaultAzureCredential()
{
ManagedIdentityClientId = clientIDstorage
});
// Second blob storage client that using a managed identity
BlobServiceClient blobServiceClient2 = new BlobServiceClient(
new Uri("https://<contract-storage-account>.blob.core.windows.net"),
new DefaultAzureCredential()
{
ManagedIdentityClientId = clientIDstorage
});
// Get the second user-assigned managed identity ID to connect to shared databases
var clientIDdatabases = Environment.GetEnvironmentVariable("Managed_Identity_Client_ID_Databases");
// Create an Azure Cosmos DB client
CosmosClient client = new CosmosClient(
accountEndpoint: Environment.GetEnvironmentVariable("COSMOS_ENDPOINT", EnvironmentVariableTarget.Process),
new DefaultAzureCredential()
{
ManagedIdentityClientId = clientIDdatabases
});
// Open a connection to Azure SQL using a managed identity
string ConnectionString1 = @"Server=<azure-sql-hostname>.database.windows.net; User Id=ClientIDOfTheManagedIdentity; Authentication=Active Directory Default; Database=<database-name>";
using (SqlConnection conn = new SqlConnection(ConnectionString1))
{
conn.Open();
}
class Demo {
public static void main(String[] args) {
// Get the first user-assigned managed identity ID to connect to shared storage
String clientIdStorage = System.getenv("Managed_Identity_Client_ID_Storage");
// Get the DefaultAzureCredential from clientIdStorage
DefaultAzureCredential storageCredential =
new DefaultAzureCredentialBuilder().managedIdentityClientId(clientIdStorage).build();
// First blob storage client that using a managed identity
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
.endpoint("https://<receipt-storage-account>.blob.core.windows.net")
.credential(storageCredential)
.buildClient();
// Second blob storage client that using a managed identity
BlobServiceClient blobServiceClient2 = new BlobServiceClientBuilder()
.endpoint("https://<contract-storage-account>.blob.core.windows.net")
.credential(storageCredential)
.buildClient();
// Get the second user-assigned managed identity ID to connect to shared databases
String clientIdDatabase = System.getenv("Managed_Identity_Client_ID_Databases");
// Create an Azure Cosmos DB client
CosmosClient cosmosClient = new CosmosClientBuilder()
.endpoint("https://<cosmos-db-account>.documents.azure.com:443/")
.credential(new DefaultAzureCredentialBuilder().managedIdentityClientId(clientIdDatabase).build())
.buildClient();
// Open a connection to Azure SQL using a managed identity
String connectionUrl = "jdbc:sqlserver://<azure-sql-hostname>.database.windows.net:1433;"
+ "database=<database-name>;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database"
+ ".windows.net;loginTimeout=30;Authentication=ActiveDirectoryMSI;";
try {
Connection connection = DriverManager.getConnection(connectionUrl);
Statement statement = connection.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Spring Cloud Azure no admite la configuración de varios clientes del mismo servicio, por lo que los códigos siguientes crean varios elementos bean para esta situación.
@Configuration
public class AzureStorageConfiguration {
@Bean("secondBlobServiceClient")
public BlobServiceClient secondBlobServiceClient(BlobServiceClientBuilder builder) {
return builder.endpoint("https://<receipt-storage-account>.blob.core.windows.net").buildClient();
}
@Bean("firstBlobServiceClient")
public BlobServiceClient firstBlobServiceClient(BlobServiceClientBuilder builder) {
return builder.buildClient();
}
}
Dentro del proyecto, use npm para agregar una referencia al paquete @azure/identity. Esta biblioteca contiene todas las entidades necesarias para implementar DefaultAzureCredential. Instale cualquier otra bibliotecas de Azure SDK que sean relevantes para la aplicación.
En la parte superior del archivoindex.js, agregue las siguientes instrucciones import para importar las clases de cliente necesarias para los servicios a los que se conectará la aplicación:
import { DefaultAzureCredential } from "@azure/identity";
import { BlobServiceClient } from "@azure/storage-blob";
import { KeyClient } from "@azure/keyvault-keys";
En el archivo index.js, cree objetos de cliente para los servicios de Azure a los que se conectará la aplicación. Los ejemplos siguientes se conectan a Blob Storage, Cosmos DB y Azure SQL mediante las clases de SDK correspondientes.
// Get the first user-assigned managed identity ID to connect to shared storage
const clientIdStorage = process.env.MANAGED_IDENTITY_CLIENT_ID_STORAGE;
// Storage account names
const storageAccountName1 = process.env.AZURE_STORAGE_ACCOUNT_NAME_1;
const storageAccountName2 = process.env.AZURE_STORAGE_ACCOUNT_NAME_2;
// First blob storage client that using a managed identity
const blobServiceClient = new BlobServiceClient(
`https://${storageAccountName1}.blob.core.windows.net`,
new DefaultAzureCredential({
managedIdentityClientId: clientIdStorage
})
);
// Second blob storage client that using a managed identity
const blobServiceClient2 = new BlobServiceClient(
`https://${storageAccountName2}.blob.core.windows.net`,
new DefaultAzureCredential({
managedIdentityClientId: clientIdStorage
})
);
// Get the second user-assigned managed identity ID to connect to shared databases
const clientIdDatabases = process.env.MANAGED_IDENTITY_CLIENT_ID_DATABASES;
// Cosmos DB Account endpoint
const cosmosDbAccountEndpoint = process.env.COSMOS_ENDPOINT;
// Create an Azure Cosmos DB client
const client = new CosmosClient({
endpoint: cosmosDbAccountEndpoint,
credential: new DefaultAzureCredential({
managedIdentityClientId: clientIdDatabases
})
});
// Open a connection to Azure SQL using a managed identity with mssql package
// mssql reads the environment variables to get the managed identity
const server = process.env.AZURE_SQL_SERVER;
const database = process.env.AZURE_SQL_DATABASE;
const port = parseInt(process.env.AZURE_SQL_PORT);
const type = process.env.AZURE_SQL_AUTHENTICATIONTYPE;
const config = {
server,
port,
database,
authentication: {
type // <---- Passwordless connection
},
options: {
encrypt: true
}
};
await sql.connect(sqlConfig);
También puede asociar una identidad administrada asignada por el usuario, así como una identidad administrada asignada por el sistema a un recurso simultáneamente. Esto puede ser útil en escenarios en los que todas las aplicaciones requieren acceso a los mismos servicios compartidos, pero una de las aplicaciones también tiene una dependencia muy específica de un servicio adicional. El uso de una identidad asignada por el sistema también garantiza que la identidad asociada a esa aplicación específica se elimine cuando se elimine la aplicación, lo que puede ayudar a mantener su entorno limpio.
En este tutorial, ha aprendido a migrar una aplicación a conexiones sin contraseña. Puede leer los siguientes recursos para explorar los conceptos que se describen en este artículo con más detalle: