Automatización de la rotación de un secreto para recursos que usan un conjunto de credenciales de autenticación

Aunque la mejor forma de realizar la autenticación en los servicios de Azure es mediante una identidad administrada, hay algunos escenarios en los que no es posible. En estos casos, se usan claves de acceso o secretos. Tanto las claves de acceso como los secretos deben rotarse periódicamente.

En este tutorial se muestra cómo automatizar la rotación periódica de secretos de bases de datos y servicios que usan un conjunto de credenciales de autenticación. Específicamente, en este tutorial se rotan las contraseñas de SQL Server almacenadas en Azure Key Vault mediante el uso de una función desencadenada por una notificación de Azure Event Grid:

Diagrama de la solución de rotación

  1. Treinta días antes de la fecha de expiración de un secreto, Key Vault publica el evento de "expiración cercana" en Event Grid.
  2. Event Grid comprueba las suscripciones del evento y usa HTTP POST para llamar al punto de conexión de la aplicación de funciones suscrita al evento.
  3. La aplicación de funciones recibe la información del secreto, genera una nueva contraseña aleatoria y crea una versión del secreto con la contraseña nueva en Key Vault.
  4. La aplicación de funciones actualiza SQL Server con la nueva contraseña.

Nota:

Podría haber un retraso entre los pasos 3 y 4. Durante ese tiempo, el secreto de Key Vault no podrá autenticarse en SQL Server. En caso de que se produzca un error en cualquiera de los pasos, Event Grid hace reintentos durante dos horas.

Requisitos previos

Si no tiene instancias de Key Vault y SQL Server existentes, puede usar este vínculo de implementación:

Imagen que muestra un botón con la etiqueta

  1. En Grupo de recursos, seleccione Crear nuevo. Asigne un nombre al grupo. En este tutorial se usa akvrotation.
  2. En Inicio de sesión de administrador de SQL, escriba el id. de inicio de sesión del administrador de SQL.
  3. Seleccione Revisar + crear.
  4. Seleccione Crear

Crear un grupo de recursos

Ahora ya tiene una instancia de Key Vault, una instancia de SQL Server y una base de datos SQL. Puede comprobar esta configuración en la CLI de Azure mediante la ejecución del comando siguiente:

az resource list -o table -g akvrotation

El resultado tendrá un aspecto similar a la salida siguiente:

Name                     ResourceGroup         Location    Type                               Status
-----------------------  --------------------  ----------  ---------------------------------  --------
akvrotation-kv           akvrotation      eastus      Microsoft.KeyVault/vaults
akvrotation-sql          akvrotation      eastus      Microsoft.Sql/servers
akvrotation-sql/master   akvrotation      eastus      Microsoft.Sql/servers/databases
akvrotation-sql2         akvrotation      eastus      Microsoft.Sql/servers
akvrotation-sql2/master  akvrotation      eastus      Microsoft.Sql/servers/databases

Creación e implementación de la función de rotación de contraseñas de SQL Server

Importante

Esta plantilla requiere que el almacén de claves, SQL Server y la función de Azure estén en el mismo grupo de recursos.

A continuación, cree una aplicación de funciones con una identidad administrada por el sistema, además de los otros componentes necesarios, e implemente las funciones de rotación de contraseñas de SQL Server.

La aplicación de funciones requiere estos componentes:

  • Un plan de Azure App Service
  • Una aplicación de funciones con funciones de rotación de contraseñas SQL con desencadenador de eventos y desencadenador http.
  • Una cuenta de almacenamiento necesaria para la administración del desencadenador de la aplicación de funciones
  • Una directiva de acceso para la identidad de Function App para acceder a los secretos de Key Vault.
  • Una suscripción a los eventos de Event Grid para el evento SecretNearExpiry.
  1. Seleccione el vínculo de implementación de la plantilla de Azure:

    Imagen que muestra un botón con la etiqueta

  2. En la lista Grupo de recursos, seleccione akvrotation.

  3. En Nombre de SQL Server, escriba el nombre de la instancia de SQL Server con la contraseña que se va a rotar.

  4. En Nombre del almacén de claves, escriba el nombre del almacén.

  5. En Nombre de la aplicación de funciones, escriba el nombre de la aplicación.

  6. En Nombre del secreto, escriba el nombre del secreto en el que se almacenará la contraseña.

  7. En Dirección URL del repositorio, escriba la ubicación del código de la función en GitHub (https://github.com/Azure-Samples/KeyVault-Rotation-SQLPassword-Csharp.git).

  8. Seleccione Revisar + crear.

  9. Seleccione Crear.

Selección de Revisar y crear

Tras completar los pasos anteriores tendrá una cuenta de almacenamiento, una granja de servidores y una aplicación de funciones. Puede comprobar esta configuración en la CLI de Azure mediante la ejecución del comando siguiente:

az resource list -o table -g akvrotation

El resultado tendrá un aspecto similar al de la salida siguiente:

Name                     ResourceGroup         Location    Type                               Status
-----------------------  --------------------  ----------  ---------------------------------  --------
akvrotation-kv           akvrotation       eastus      Microsoft.KeyVault/vaults
akvrotation-sql          akvrotation       eastus      Microsoft.Sql/servers
akvrotation-sql/master   akvrotation       eastus      Microsoft.Sql/servers/databases
cfogyydrufs5wazfunctions akvrotation       eastus      Microsoft.Storage/storageAccounts
akvrotation-fnapp        akvrotation       eastus      Microsoft.Web/serverFarms
akvrotation-fnapp        akvrotation       eastus      Microsoft.Web/sites
akvrotation-fnapp        akvrotation       eastus      Microsoft.insights/components

Para obtener información sobre cómo crear una aplicación de funciones y usar una identidad administrada para acceder a Key Vault, consulte Creación de una aplicación de funciones desde Azure Portal, Cómo usar identidades administradas para App Service y Azure Functions y Asignación de una directiva de acceso de Key Vault mediante Azure Portal.

Función de rotación

La función implementada en el paso anterior utiliza un evento para desencadenar la rotación de un secreto mediante la actualización de Key Vault y la base de datos SQL.

Evento de desencadenador de la función

Esta función lee los datos del evento y ejecuta la lógica de la rotación:

public static class SimpleRotationEventHandler
{
   [FunctionName("AKVSQLRotation")]
   public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
   {
      log.LogInformation("C# Event trigger function processed a request.");
      var secretName = eventGridEvent.Subject;
      var secretVersion = Regex.Match(eventGridEvent.Data.ToString(), "Version\":\"([a-z0-9]*)").Groups[1].ToString();
      var keyVaultName = Regex.Match(eventGridEvent.Topic, ".vaults.(.*)").Groups[1].ToString();
      log.LogInformation($"Key Vault Name: {keyVaultName}");
      log.LogInformation($"Secret Name: {secretName}");
      log.LogInformation($"Secret Version: {secretVersion}");

      SecretRotator.RotateSecret(log, secretName, keyVaultName);
   }
}

Lógica de rotación de secretos

Este método de rotación lee la información de la base de datos del secreto, crea una nueva versión del secreto y actualiza la base de datos con el nuevo secreto:

    public class SecretRotator
    {
		private const string CredentialIdTag = "CredentialId";
		private const string ProviderAddressTag = "ProviderAddress";
		private const string ValidityPeriodDaysTag = "ValidityPeriodDays";

		public static void RotateSecret(ILogger log, string secretName, string keyVaultName)
        {
            //Retrieve Current Secret
            var kvUri = "https://" + keyVaultName + ".vault.azure.net";
            var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
            KeyVaultSecret secret = client.GetSecret(secretName);
            log.LogInformation("Secret Info Retrieved");

            //Retrieve Secret Info
            var credentialId = secret.Properties.Tags.ContainsKey(CredentialIdTag) ? secret.Properties.Tags[CredentialIdTag] : "";
            var providerAddress = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
            var validityPeriodDays = secret.Properties.Tags.ContainsKey(ValidityPeriodDaysTag) ? secret.Properties.Tags[ValidityPeriodDaysTag] : "";
            log.LogInformation($"Provider Address: {providerAddress}");
            log.LogInformation($"Credential Id: {credentialId}");

            //Check Service Provider connection
            CheckServiceConnection(secret);
            log.LogInformation("Service  Connection Validated");
            
            //Create new password
            var randomPassword = CreateRandomPassword();
            log.LogInformation("New Password Generated");

            //Add secret version with new password to Key Vault
            CreateNewSecretVersion(client, secret, randomPassword);
            log.LogInformation("New Secret Version Generated");

            //Update Service Provider with new password
            UpdateServicePassword(secret, randomPassword);
            log.LogInformation("Password Changed");
            log.LogInformation($"Secret Rotated Successfully");
        }
}

Puede encontrar el código completo en GitHub.

Adición del secreto a Key Vault

Establezca una directiva de acceso para conceder a los usuarios el permiso para administrar secretos.

az keyvault set-policy --upn <email-address-of-user> --name akvrotation-kv --secret-permissions set delete get list

Cree un nuevo secreto con etiquetas que contengan el identificador de recurso de SQL Server, el nombre del inicio de sesión de SQL Server y el período de validez del secreto en días. Proporcione el nombre del secreto, la contraseña inicial de la base de datos SQL (en nuestro ejemplo, "Simple123") e incluya una fecha de expiración establecida para mañana.

$tomorrowDate = (get-date).AddDays(+1).ToString("yyy-MM-ddThh:mm:ssZ")
az keyvault secret set --name sqlPassword --vault-name akvrotation-kv --value "Simple123" --tags "CredentialId=sqlAdmin" "ProviderAddress=<sql-database-resource-id>" "ValidityPeriodDays=90" --expires $tomorrowDate

La creación de un secreto con una fecha de expiración corta publicará un evento SecretNearExpiry en un plazo de 15 minutos, lo que a su vez desencadenará la función para rotar el secreto.

Prueba y comprobación

Para comprobar que el secreto ha rotado, vaya a Key Vault>Secretos:

Captura de pantalla en la que se muestra cómo acceder a Key Vault > Secretos.

Abra el secreto sqlPassword y vea la versión original y la rotada:

Ir a Secretos

Creación de una aplicación web

Para comprobar las credenciales de SQL, cree una aplicación web. Esta aplicación web obtendrá el secreto de Key Vault, extraerá la información y las credenciales de la base de datos SQL del secreto, y probará la conexión con SQL Server.

La aplicación web requiere estos componentes:

  • Una aplicación web con una identidad administrada por el sistema.
  • Una directiva de acceso para acceder a los secretos de Key Vault mediante la identidad administrada de la aplicación web.
  1. Seleccione el vínculo de implementación de la plantilla de Azure:

    Imagen que muestra un botón con la etiqueta

  2. Seleccione el grupo de recursos akvrotation.

  3. En Nombre de SQL Server, escriba el nombre de la instancia de SQL Server con la contraseña que se va a rotar.

  4. En Nombre del almacén de claves, escriba el nombre del almacén.

  5. En Nombre del secreto, escriba el nombre del secreto en el que se almacena la contraseña.

  6. En Dirección URL del repositorio, escriba la ubicación del código de la aplicación web en GitHub (https://github.com/Azure-Samples/KeyVault-Rotation-SQLPassword-Csharp-WebApp.git).

  7. Seleccione Revisar + crear.

  8. Seleccione Crear.

Abrir la aplicación web

Vaya a la dirección URL de la aplicación implementada:

'https://akvrotation-app.azurewebsites.net/'

Cuando la aplicación se abra en el explorador, verá el valor de secreto generado y un valor de Base de datos conectada de true.

Más información