I'd like to fetch both App Configuration and KeyVault values directly from IConfiguration. This is from a console application in .Net 7
Program.cs:
var host = Host.CreateDefaultBuilder()
.ConfigureLogging(a => a.AddConsole())
.ConfigureHostConfiguration(config => config.AddEnvironmentVariables())
.ConfigureAppConfiguration(config =>
{
config.ConfigureKeyVault();
})
.ConfigureServices((context, services) =>
{
var env = context.HostingEnvironment;
var startUp = new Startup(env);
startUp.ConfigureServices(services);
startUp.ConfigureConsoleMethods(services);
_serviceProvider = services.BuildServiceProvider(true);
})
.Build();
Extension Method:
public static void ConfigureKeyVault(this IConfigurationBuilder config)
{
var settings = config.Build();
var appConfigConnString = settings.GetConnectionString("AppConfig");
var keyVaultEndpoint = settings.GetValue<string>("KeyVault:Endpoint");
var kvOptions = new DefaultAzureCredentialOptions { ManagedIdentityClientId = settings.GetValue<string>("KeyVault:ClientId") };
config.AddAzureAppConfiguration(options =>
{
options.Connect(appConfigConnString);
options.ConfigureKeyVault(x => x.SetCredential(new DefaultAzureCredential(kvOptions)));
});
}
With this setup, I can fetch my KeyVault keys like this:
services.AddScoped<IApiFactory, ApiFactory>(x =>
{
var keyVault = x.GetRequiredService<IKeyVaultService>();
return new ApiFactory(
keyVault.GetSecret("SomeObj:ClientId"),
keyVault.GetSecret("SomeObj:ClientSecret"));
});
But I would rather get my key's using IConfiguration, like this:
services.AddScoped<IApiFactory, ApiFactory>(x =>
{
return new ApiFactory(
this.Configuration.GetValue<string>("SomeObj:ClientId"),
this.Configuration.GetValue<string>("SomeObj:ClientSecret"));
});
Question
How can I fetch my KeyVault values from IConfiguration?
If you set up a key vault reference in Azure App Configuration, the secret retrieved from the key vault should be accessible from IConfiguration.
Make sure the key name (e.g. "SomeObj:ClientId") is the one that you set in Azure App Configuration instead of the secret name you set in Key Vault.
Make sure the configuration is built before you attempt to access it.
Related
How can I setup listening multiple ports? On first port I want to have default app with https, on another I want to use HTTPS and require SSL based authentication with client certificates. How to do it? This is my current Startup.cs code:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.ConfigureHttpsDefaults(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
});
});
var services = builder.Services;
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, cfg =>
{
cfg.ReturnUrlParameter = "returnUrl";
cfg.LoginPath = "/account/login";
cfg.LogoutPath = "/account/logout";
})
.AddCertificate(CertificateAuthenticationDefaults.AuthenticationScheme, cfg =>
{
cfg.AllowedCertificateTypes = CertificateTypes.All;
cfg.RevocationMode = X509RevocationMode.Online;
});
services.AddControllersWithViews();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
My goal is to use Certificate authentication on some endpoints (and don't display certificate request e.g. for web explorer users) and not use delayed certificates.
I did it with kestrelOptions.ListenLocalhost:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.ListenLocalhost(8080, cfg =>
{
cfg.UseHttps(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});
kestrelOptions.ListenLocalhost(8081, cfg =>
{
cfg.UseHttps(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.NoCertificate;
});
});
});
Now one port is for mTLS (8080) and another don't require certificate! Works really nice.
I am creating a Worker application using Net 6 and I have in Program.cs:
IHostBuilder builder = Host.CreateDefaultBuilder(args);
builder.ConfigureHostConfiguration(x => {
x.AddJsonFile("settings.json", false, true);
x.AddJsonFile($"settings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", false, true);
x.AddEnvironmentVariables();
});
builder.UseSerilog(new LoggerBuilder(
new LoggerOptions {
ConnectionString = builder.Configuration.Get<Options>().ConnectionString
},
).CreateLogger());
In LoggerOptions I need to get Options and the ConnectionString from it.
I tried the following because that is what I do when using WebApplicationBuilder:
builder.Configuration.Get<Options>().ConnectionString
But this does not compile as it seems IHostBuilder does not have a Configuration property.
How can I do this?
Simple example:
var hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.UseSerilog((hostContext, services) =>
{
var connectionString = hostContext.Configuration.GetConnectionString("MyConnectionString");
});
hostBuilder.ConfigureServices((hostContext, services) =>
{
var connectionString = hostContext.Configuration.GetConnectionString("MyConnectionString");
}
You can access it by using the configure services overload that accepts the HostBuilderContext. I don't typically use the LoggerBuilder:
IHost host = Host.CreateDefaultBuilder(args)
.UseSerilog((context, loggerConfiguration) =>
{
loggerConfiguration.ReadFrom.Configuration(context.Configuration);
})
.Build();
await host.RunAsync();
I am new to Azure Cloud and trying to follow one tutorial on how to connect with Azure Key Vault. Here is the link for the tutorial.
Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var vaultName = builtConfig["VaultName"];
var keyVaultClient = new KeyVaultClient(async (authority, resource, scope) =>
{
var credential = new DefaultAzureCredential(false);
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://vault.azure.net/.default" }));
return token.Token;
});
config.AddAzureKeyVault(vaultName, keyVaultClient, new DefaultKeyVaultSecretManager());
});
appsettings.json
"VaultName": "https://connectionstringkeyvault.vault.azure.net/"
In Azure, I have created a Key Vault with the name ConnectionStringKeyVault and I have defined a secret as well.
This is the access policy that I have created in Key Vault:
And, I have created a storage account with the following details:
But, whenever I try to execute my code I am getting the below exception:
What am I doing wrong?
Solution 1:
To fix access denied you need to configure Active Directory permissions. Grant access to KeyVault.
1. Using PowerShell Run the command:
Set-AzureRmKeyVaultAccessPolicy -VaultName 'XXXXXXX' -ServicePrincipalName XXXXX -PermissionsToKeys decrypt,sign,get,unwrapKey
2. Using the Azure portal
Open Key Vaults
Select Access Policies from the Key Vault resource blade.
Click the [+ Add Access Policy] button at the top of the blade
Click Select Principal to select the application you created earlier
From the Key permissions drop down, select "Decrypt", "Sign", "Get",
"UnwrapKey" permissions
Save changes
Solution 2:
According to https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme, Try with replacing the DefaultAzureCredential with ChainedTokenCredential, the key value secret was successfully retrieved.
Example code:
var cred = new ChainedTokenCredential(new ManagedIdentityCredential(), new AzureCliCredential());
SecretClient client = new SecretClient(new Uri(keyvaultUri), cred);
Response<KeyVaultSecret> secret = await client.GetSecretAsync("kv-sec-test");
For more details refer this document
I need to implement Client Certificate authentication on some of the endpoints in my .NET 5 Web API. So I don't want to enable HTTPS across all endpoint as described here in the MS docs. I am using Kestrel on my local machine and not IIS express or IIS.
I have tried the following three methods with no luck on either of them:
var clientCertHeaders = context.HttpContext.Request.Headers;
This one returns the normal headers for the request but no certificate.
var clientCert = context.HttpContext.Connection.ClientCertificate;
var clientCertAsync = context.HttpContext.Connection.GetClientCertificateAsync().Result;
These two both return null.
I've tried applying the following to my services:
services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-SSL-CERT";
options.HeaderConverter = (headerValue) =>
{
X509Certificate2 clientCertificate = null;
if(!string.IsNullOrWhiteSpace(headerValue))
{
var bytes = Encoding.UTF8.GetBytes(headerValue);
clientCertificate = new X509Certificate2(bytes);
}
return clientCertificate;
};
});
Even with that enabled in my services I am not retrieving the client certificate.
I am using Postman to make the requests to the API requests.
You need to configure Kestrel to allow client certificates in the program.cs The default value is ClientCertificateMode.NoCertificate so in your ConfigureWebHostDefaults you need to change that to ClientCertificateMode.AllowCertificate.
Here's an edited chunk of code from the docs you sent where I did that:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
o.ClientCertificateMode =
ClientCertificateMode.AllowCertificate);
});
});
}
I need to get the list of tenants in my Startup so that I can validate few items.
I tried adding the following lines in Startup.cs
services.AddTransient<TenantAppService>();
var tenantService = services.BuildServiceProvider().GetService<ITenantAppService>();
var tennants = tenantService.GetAll(null);
But tenantService is always null
Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
string connectionString = _appConfiguration.GetConnectionString("Default");
var cb = new SqlConnectionStringBuilder(connectionString);
var migrationsAssembly = typeof(PlatformDbContext).GetTypeInfo().Assembly.GetName().Name;
IdentityRegistrar.Register(services);
services
.AddIdentityServer(options =>
{
//options.Discovery.CustomEntries.Add("custom_endpoint", "~/api/custom");
})
.AddAbpIdentityServer<User>(options=>
{
options.UpdateAbpClaimTypes = true; // default is true
options.UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap = true; // default is true
})
.AddAbpPersistedGrants<IAbpPersistedGrantDbContext>()
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddSigningCredential(new SigningCredentials(AuthConfigHelper.GetSecurityKey(), SecurityAlgorithms.RsaSha256));
services.AddTransient<TenantAppService>();
var tenantService = services.BuildServiceProvider().GetService<ITenantAppService>();
var tennants = tenantService.GetAll(null);
AuthConfigurer.Configure(services, _appConfiguration);
services.AddSignalR();
// Configure CORS for angular2 UI
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
)
);
// Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info { Title = "Platform API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
// Define the BearerAuth scheme that's in use
options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = "header",
Type = "apiKey"
});
// Registering File upload operation
options.OperationFilter<FormFileSwaggerFilter>();
});
// MVC
services.AddMvc(
options => options.Filters.Add(new CorsAuthorizationFilterFactory(_defaultCorsPolicyName))
);
// Configure Abp and Dependency Injection
return services.AddAbp<PlatformWebHostModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
}
Either I need to pass tenants or tenantService instance to AuthConfigurer.Configure(services, _appConfiguration); for validation inside AuthConfigurer.Configure
How do I achieve this?
You can use IocManager to resolve any dependencies. For instance,
var jobManager = Configuration.IocManager.Resolve<IQuartzScheduleJobManager>();
For your issue, you can use:
var tenantService = Configuration.IocManager.Resolve<ITenantAppService>();
var tennants = tenantService.GetAll(null);