private static async Task <IKeyVaultClient> CreateKeyVaultClientAsync(KeyRotationConfig rotationConfig) { ServicePrincipalAuthentication authentication = rotationConfig.ServicePrincipal.CreateAuthentication(); IKeyVaultClient keyVaultClient = await authentication.AuthenticateAsync(); return(keyVaultClient); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConfiguration"/> class. /// </summary> /// <param name="configuration">The configuration instance to provide the necessary information during authentication with the correct Azure Service Bus instance.</param> /// <param name="logger">The instance to log diagnostic messages during the interaction with the Azure Service Bus instance.</param> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="configuration"/> or the <paramref name="logger"/> is <c>null</c>.</exception> public ServiceBusConfiguration(KeyRotationConfig configuration, ILogger logger) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(logger, nameof(logger)); _configuration = configuration; _logger = logger; }
private static void AddServiceBusMessagePump(WorkerOptions options, KeyRotationConfig rotationConfig, string jobId) { options.AddServiceBusQueueMessagePump(rotationConfig.KeyVault.SecretName, opt => { opt.JobId = jobId; // Unrealistic big maximum exception count so that we're certain that the message pump gets restarted based on the notification and not the unauthorized exception. opt.MaximumUnauthorizedExceptionsBeforeRestart = 1000; }).WithServiceBusMessageHandler <OrdersAzureServiceBusMessageHandler, Order>(); }
private static void AddSecretStore(WorkerOptions options, KeyRotationConfig rotationConfig) { options.Configure(host => host.ConfigureSecretStore((configuration, stores) => { stores.AddAzureKeyVaultWithServicePrincipal( rotationConfig.KeyVault.VaultUri, rotationConfig.ServicePrincipal.ClientId, rotationConfig.ServicePrincipal.ClientSecret) .AddConfiguration(configuration); })); }
public async Task ServiceBusMessagePump_RotateServiceBusConnectionKeysOnSecretNewVersionNotification_MessagePumpRestartsThenMessageSuccessfullyProcessed() { // Arrange var config = TestConfig.Create(); KeyRotationConfig rotationConfig = config.GetKeyRotationConfig(); _logger.LogInformation("Using Service Principal [ClientID: '{0}']", rotationConfig.ServicePrincipal.ClientId); var client = new ServiceBusConfiguration(rotationConfig, _logger); string freshConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey); IKeyVaultClient keyVaultClient = await CreateKeyVaultClientAsync(rotationConfig); await SetConnectionStringInKeyVaultAsync(keyVaultClient, rotationConfig, freshConnectionString); string jobId = Guid.NewGuid().ToString(); const string connectionStringSecretKey = "ARCUS_KEYVAULT_SECRETNEWVERSIONCREATED_CONNECTIONSTRING"; var options = new WorkerOptions(); options.Configuration.Add(connectionStringSecretKey, rotationConfig.KeyVault.SecretNewVersionCreated.ConnectionString); AddEventGridPublisher(options, config); AddSecretStore(options, rotationConfig); AddServiceBusMessagePump(options, rotationConfig, jobId); options.AddAutoRestartServiceBusMessagePumpOnRotatedCredentialsBackgroundJob( jobId: jobId, subscriptionNamePrefix: "TestSub", serviceBusTopicConnectionStringSecretKey: connectionStringSecretKey, messagePumpConnectionStringKey: rotationConfig.KeyVault.SecretName); await using (var worker = await Worker.StartNewAsync(options)) { string newSecondaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.SecondaryKey); await SetConnectionStringInKeyVaultAsync(keyVaultClient, rotationConfig, newSecondaryConnectionString); await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger)) { // Act string newPrimaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey); // Assert await service.SimulateMessageProcessingAsync(newPrimaryConnectionString); } } }
public async Task ServiceBusMessagePump_RotateServiceBusConnectionKeys_MessagePumpRestartsThenMessageSuccessfullyProcessed() { // Arrange var config = TestConfig.Create(); KeyRotationConfig keyRotationConfig = config.GetKeyRotationConfig(); _logger.LogInformation("Using Service Principal [ClientID: '{ClientId}']", keyRotationConfig.ServicePrincipal.ClientId); var client = new ServiceBusConfiguration(keyRotationConfig, _logger); string freshConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey); ServicePrincipalAuthentication authentication = keyRotationConfig.ServicePrincipal.CreateAuthentication(); IKeyVaultClient keyVaultClient = await authentication.AuthenticateAsync(); await SetConnectionStringInKeyVaultAsync(keyVaultClient, keyRotationConfig, freshConnectionString); var options = new WorkerOptions(); options.AddEventGridPublisher(config) .AddSingleton <ISecretProvider>(serviceProvider => { return(new KeyVaultSecretProvider( new ServicePrincipalAuthentication(keyRotationConfig.ServicePrincipal.ClientId, keyRotationConfig.ServicePrincipal.ClientSecret), new KeyVaultConfiguration(keyRotationConfig.KeyVault.VaultUri))); }) .AddServiceBusQueueMessagePump(keyRotationConfig.KeyVault.SecretName, opt => opt.AutoComplete = true) .WithServiceBusMessageHandler <OrdersAzureServiceBusMessageHandler, Order>(); await using (var worker = await Worker.StartNewAsync(options)) { string newSecondaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.SecondaryKey); await SetConnectionStringInKeyVaultAsync(keyVaultClient, keyRotationConfig, newSecondaryConnectionString); await using (var service = await TestMessagePumpService.StartNewAsync(config, _logger)) { // Act string newPrimaryConnectionString = await client.RotateConnectionStringKeysForQueueAsync(KeyType.PrimaryKey); // Assert await service.SimulateMessageProcessingAsync(newPrimaryConnectionString); } } }
public async Task RotateServiceBusSecrets_WithValidArguments_RotatesPrimarySecondaryAlternatively() { // Arrange var config = TestConfig.Create(); KeyRotationConfig keyRotationConfig = config.GetKeyRotationConfig(); _logger.LogInformation("Using Service Principal [ClientID: '{ClientId}']", keyRotationConfig.ServicePrincipal.ClientId); const ServiceBusEntityType entity = ServiceBusEntityType.Topic; var keyVaultAuthentication = new ServicePrincipalAuthentication( keyRotationConfig.ServicePrincipal.ClientId, keyRotationConfig.ServicePrincipal.ClientSecret); var keyVaultConfiguration = new KeyVaultConfiguration(keyRotationConfig.KeyVault.VaultUri); var secretProvider = new KeyVaultSecretProvider(keyVaultAuthentication, keyVaultConfiguration); AzureServiceBusClient azureServiceBusClient = CreateAzureServiceBusClient(keyRotationConfig, secretProvider, entity); var rotation = new AzureServiceBusKeyRotation(azureServiceBusClient, keyVaultAuthentication, keyVaultConfiguration, _logger); var client = new ServiceBusConfiguration(keyRotationConfig, _logger); AccessKeys keysBefore1stRotation = await client.GetConnectionStringKeysForTopicAsync(); // Act await rotation.RotateServiceBusSecretAsync(keyRotationConfig.KeyVault.SecretName); // Assert string secondaryConnectionString = await secretProvider.GetRawSecretAsync(keyRotationConfig.KeyVault.SecretName); AccessKeys keysAfter1stRotation = await client.GetConnectionStringKeysForTopicAsync(); Assert.True(secondaryConnectionString == keysAfter1stRotation.SecondaryConnectionString, "Secondary connection string should be set in Azure Key Vault after first rotation"); Assert.NotEqual(keysBefore1stRotation.PrimaryConnectionString, keysAfter1stRotation.PrimaryConnectionString); Assert.NotEqual(keysBefore1stRotation.SecondaryConnectionString, keysAfter1stRotation.SecondaryConnectionString); await rotation.RotateServiceBusSecretAsync(keyRotationConfig.KeyVault.SecretName); string primaryConnectionString = await secretProvider.GetRawSecretAsync(keyRotationConfig.KeyVault.SecretName); AccessKeys keysAfter2ndRotation = await client.GetConnectionStringKeysForTopicAsync(); Assert.True(primaryConnectionString == keysAfter2ndRotation.PrimaryConnectionString, "Primary connection string should be set in Azure Key Vault after second rotation"); Assert.NotEqual(keysAfter1stRotation.PrimaryConnectionString, keysAfter2ndRotation.PrimaryConnectionString); Assert.NotEqual(keysAfter2ndRotation.SecondaryConnectionString, keysAfter1stRotation.SecondaryConnectionString); }
private AzureServiceBusClient CreateAzureServiceBusClient( KeyRotationConfig keyRotationConfig, ISecretProvider secretProvider, ServiceBusEntityType entity) { var serviceBusAuthentication = new DefaultAzureServiceBusManagementAuthentication( keyRotationConfig.ServicePrincipal.ClientId, keyRotationConfig.ServicePrincipal.ClientSecretKey, keyRotationConfig.ServiceBusNamespace.SubscriptionId, keyRotationConfig.ServiceBusNamespace.TenantId, secretProvider); var serviceBusLocation = new AzureServiceBusNamespace( keyRotationConfig.ServiceBusNamespace.ResourceGroup, keyRotationConfig.ServiceBusNamespace.Namespace, entity, keyRotationConfig.ServiceBusNamespace.TopicName, keyRotationConfig.ServiceBusNamespace.AuthorizationRuleName); var azureServiceBusClient = new AzureServiceBusClient(serviceBusAuthentication, serviceBusLocation, _logger); return(azureServiceBusClient); }
private static async Task SetConnectionStringInKeyVaultAsync(IKeyVaultClient keyVaultClient, KeyRotationConfig keyRotationConfig, string rotatedConnectionString) { await keyVaultClient.SetSecretAsync( vaultBaseUrl : keyRotationConfig.KeyVault.VaultUri, secretName : keyRotationConfig.KeyVault.SecretName, value : rotatedConnectionString); }