Base solution for your next web application
Open Closed

Azure Vault - Configuration Provider #5838


User avatar
0
maharatha created

I am able to add the Azure Vault in th Program.cs and able to add Keys programatically and access them in the Azure Vault. But when it comes to Secret I am having issues. As per the below document I should be able to add the Azure Vault COnfiguration Provider and access the configuration

https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-2.1

My code :

 public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            return new WebHostBuilder()
                .UseKestrel(opt => opt.AddServerHeader = false)
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .ConfigureAppConfiguration((ctx, builder) =>
                {
                   var keyVaultEndpoint = KeyVaultEndpoint;
                    if (string.IsNullOrEmpty(keyVaultEndpoint)) return;
                    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    var keyVaultClient = new KeyVaultClient(
                        new KeyVaultClient.AuthenticationCallback(
                            azureServiceTokenProvider.KeyVaultTokenCallback));
                    builder.AddAzureKeyVault(
                        keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                });

         
        }

        private static string KeyVaultEndpoint => Environment.GetEnvironmentVariable("KEYVAULT_ENDPOINT");

When I trying commenting the configrations in AppSettings.json and adding it in the Azure Vault i am unable to access those values:

e.g. _appConfiguration["ConnectionStrings:Default"]

Am I missing something here ?


9 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @maharatha

    Have you figured it out ?

  • User Avatar
    0
    MarkusBernhardt created

    Any success? We would also love to use Azure Vault

  • User Avatar
    0
    maharatha created

    Yes , you need to Inject Microsoft.Extensions.Configuration.IConfiguration and then access

  • User Avatar
    0
    maharatha created

    I am trying to use the secrets stored in Azure Key Vault. I first created a connection resolver :

      public class KeyValutConnectionResolver : DefaultConnectionStringResolver, IDbPerTenantConnectionStringResolver
        {
            /// <summary>
            /// Reference to the session.
            /// </summary>
            public IAbpSession AbpSession { get; set; }
    
            private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider;
            private readonly ITenantCache _tenantCache;
            private readonly IConfiguration _configuration;
    
    
            /// <summary>
            /// Initializes a new instance of the <see cref="DbPerTenantConnectionStringResolver"/> class.
            /// </summary>
            public KeyValutConnectionResolver(
                IAbpStartupConfiguration configuration,
                ICurrentUnitOfWorkProvider currentUnitOfWorkProvider,
                ITenantCache tenantCache, IConfiguration configuration1)
                : base(
                      configuration)
            {
                _currentUnitOfWorkProvider = currentUnitOfWorkProvider;
                _tenantCache = tenantCache;
                _configuration = configuration1;
    
                AbpSession = NullAbpSession.Instance;
            }
    
            public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
            {
                return args.MultiTenancySide == MultiTenancySides.Host ? _configuration["ConnectionStrings:Default"] : GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args));
            }
    
            public virtual string GetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args)
            {
                if (args.TenantId == null)
                {
                    //Requested for host
                    return base.GetNameOrConnectionString(args);
                   
                }
    
                var tenantCacheItem = _tenantCache.Get(args.TenantId.Value);
                if (tenantCacheItem.ConnectionString.IsNullOrEmpty())
                {
                    //Tenant has not dedicated database
                    return base.GetNameOrConnectionString(args);
                }
    
                return tenantCacheItem.ConnectionString;
            }
    
            protected virtual int? GetCurrentTenantId()
            {
                return _currentUnitOfWorkProvider.Current != null
                    ? _currentUnitOfWorkProvider.Current.GetTenantId()
                    : AbpSession.TenantId;
            }
        }
    

    In EntityFrameworkCore Module Pre-Initialize :

     Configuration.ReplaceService<IConnectionStringResolver, KeyValutConnectionResolver>(DependencyLifeStyle.Transient);
    

    When I debug the connection string i Get is from KeyVault but then i don't know where it gets replaced from Appsettings.json.

    public EDRMSDbContext(DbContextOptions<EDRMSDbContext> options)
                : base(options)
            {
    // I get the cinnection from KeyVault 
            }
    
  • User Avatar
    0
    ismcagdas created
    Support Team
  • User Avatar
    0
    maharatha created

    I believe I need more help on making the changes. My assumption was that if we register the KeyVault as mentioned above or something like below

    return new WebHostBuilder()
                    .ConfigureAppConfiguration((context, config) =>
                    {
                        config.AddJsonFile("Secrets.json");
                        var root = config.Build();
    
                        var vault = root["KeyVault:Vault"];
    
    
                        if (!string.IsNullOrEmpty(vault) && !string.IsNullOrEmpty(root["KeyVault:ClientId"]) && !string.IsNullOrEmpty(root["KeyVault:ClientSecret"]))
                        {
                            config.AddAzureKeyVault(
                                $"https://{root["KeyVault:Vault"]}.vault.azure.net/",
                                root["KeyVault:ClientId"],
                                root["KeyVault:ClientSecret"],new DefaultKeyVaultSecretManager());
                        }
                        else if (!string.IsNullOrEmpty(vault) && !string.IsNullOrEmpty(root["KeyVault:ClientId"]))
                        {
                            using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
                            {
                                store.Open(OpenFlags.ReadOnly);
                                var certs = store.Certificates;
                                Debug.WriteLine("Num of certificates in store: " + certs.Count);
                                var distinguishedName = new X500DistinguishedName(root["KeyVault:SubjectDistinguishedName"]);
                                var certFound = certs.Find(X509FindType.FindBySubjectDistinguishedName,
                                    distinguishedName.Name, false).OfType<X509Certificate2>();
                                if (!certFound.Any())
                                {
                                    Debug.WriteLine("Unable to find the certificate to authenticate and access key vault");
                                }
                                else
                                {
                                    // found the certificate 
                                    config.AddAzureKeyVault($"https://{root["KeyVault:Vault"]}.vault.azure.net/", root["KeyVault:ClientId"], certFound.Single(),new DefaultKeyVaultSecretManager());
                                    store.Close();
                                }
                            }
                        }
    
                    })
                    .UseKestrel(opt => opt.AddServerHeader = false)
    

    it would automatically pick it up.

    But it's clearly not happening.

    So where should I change it, I tried :

     public override void PreInitialize()
            {
                if (!SkipDbContextRegistration)
                {
                    Configuration.Modules.AbpEfCore().AddDbContext<EDRMSDbContext>(options =>
                    {
                        if (options.ExistingConnection != null)
                        {
                            EDRMSDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
                        }
                        else
                        {
                            EDRMSDbContextConfigurer.Configure(options.DbContextOptions, _keyvault["ConnectionStrings:Default"]);
                        }
                    });
                }
    

    It worked for host but when i started creating Tenant with a different db connection string it ended ip creating in the host db.

    So please provide a clear explanation how to register the Azure Key Vault with Abp as I am sure everyone is going to get benefitted out of this.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @maharatha,

    Could you create an issue on GitHub repository ? We can prepare a sample document for this.

    Thanks,

  • User Avatar
    0
    maharatha created

    https://github.com/aspnetzero/aspnet-zero-core/issues/3217

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Thanks, we will create a documentation or provide a solution. Please follow the related issue.