如何解决带有IdentityServer4认证/ http错误的{@ 44}在Azure Web应用程序容器上部署.net Core 3 linux容器
我正在尝试使用.Net Core Clean Architecture App Template并使其在容器中运行并通过蔚蓝的CI / CD管道进行部署
我的模板的容器化版本在具有5001端口的linux容器中本地运行,并且一切正常。
我的azure管道构建过程正常运行,并且在我的容器注册表中创建了图像。
问题是,一旦我将其部署/发布到容器的Web应用程序,该应用程序将失败并抛出以下错误:
应用程序启动异常 System.InvalidOperationException:在以下位置的“ CurrentUser \ My”上找不到主题为“ CN = localhost”的有效证书 Microsoft.AspNetCore.ApiAuthorization.IdentityServer.SigningKeysLoader.LoadFromStoreCert(String 主题,字符串storeName,StoreLocation storeLocation,DateTimeOffset currentTime)
我所做的:
-
在these docs from MS之后,我创建了本地开发证书:
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p { password here }
dotnet dev-certs https --trust
-
然后我将其作为私有.pfx证书导入到Web应用程序中。
-
我添加了一个应用设置 WEBSITE_LOAD_CERTIFICATES ,其证书的“ thumb”值为
-
我在Identity Server的appSettings.json部分中使用了导入证书的“主机名”。(在我的情况下为hostname = localhost)
加载Web应用程序时,它会显示:(应用程序错误,并且docker日志显示了我上面引用的错误。
我非常确定这与身份服务器设置和此处的appSettings.json值有关:
"IdentityServer": {
"Key": {
"Type": "Store","StoreName": "My","StoreLocation": "CurrentUser","Name": "CN=localhost"
}
}
有人可以帮我弄清楚如何解决此错误吗?
编辑1-手动为IdentityServer密钥指定文件
这肯定与身份服务器有关。我试图像这样在appSettings.json中手动将证书设置为文件:
"IdentityServer": {
"Key": {
"Type": "File","FilePath": "aspnetapp.pfx","Password": "Your_password123"
}
}
现在我收到此错误:
将证书文件加载到带有存储标志的“ /app/aspnetapp.pfx” ”。应用程序启动异常System.InvalidOperationException: 加载证书时出错。文件 找不到'/app/aspnetapp.pfx'。 Microsoft.AspNetCore.ApiAuthorization.IdentityServer.SigningKeysLoader.LoadFromFile
我将其添加到了dockerfile中:
WORKDIR /app
COPY ["/aspnetapp.pfx","/app"]
RUN find /app
从下图可以看到,文件显示在应用程序的构建目录中:
我还确保.gitignore或.dockerignore文件不会忽略aspnetapp.pfx。
我不知道为什么它不能加载该文件。看来它应该在应该存在的地方存在。
使用证书拇指和更新的路径进行编辑2
所以我使用了tnc1977建议,并将其作为我的身份密钥设置
"IdentityServer": {
"Key": {
"Type": "File","FilePath": "/var/ssl/private/<thumb_value>.p12","Password": "Your_password123"
}
}
但是,这又产生了一个错误:
加载证书时出错。密码是 错误或进程没有权限将密钥存储在 键集“ EphemeralKeySet” Interop + Crypto + OpenSslCryptographicException:错误:23076071:PKCS12 例程:PKCS12_parse:mac验证失败
编辑3:有效的Azure应用证书
我购买了Azure应用证书,并添加了具有TSL设置的自定义域,并且出现了相同的错误
编辑4:在代码startup.cs中加载证书-新错误:
我现在知道我不能使用证书存储库CurrentUser / My,因为它用于Windows。 Linux容器必须手动在代码中加载证书。
我正在使用已添加到azure Web应用程序中的应用程序证书的指纹。这是私有的Azure应用证书,已针对自定义域进行了验证。
我将此代码添加到了statup.cs configureservices中(我知道对这些值进行硬编码不是最佳实践,但我只想看看它是否可以加载证书,我会尝试使用env变量和密钥库):
// linux file path for private keys
var cryptBytes = File.ReadAllBytes("/var/ssl/private/<thumbprint>.p12");
var cert = new X509Certificate2(cryptBytes,"");
services.AddIdentityServer().AddSigningCredential(cert);
我输入一个空密码,因为我认为这是您应该做的。我现在在docker日志中遇到以下错误,这使我相信证书已加载,并且现在使用 services.AddIdentityServer()。AddSigningCredential(cert); 都与我有关在startup.cs configureservices 和 app.UseIdentityServer() 在startup.cs configure 中:
未处理的异常。 System.InvalidOperationException:装饰器已经注册为类型:IAuthenticationService。
我不确定如何将证书添加到应用程序。UseIdentityServer();线。
编辑5
经过更多的挖掘,不幸的是@ tnc1997答案无效。 在asp.net核心3中,我的 satrtup.cs 中的 app.UseIdentityServer 调用会在内部使用一种寻找身份服务器密钥的方法, appsetting(environment).json文件中的File,Pass等。
因此,即使我以tnc1997所示的代码加载了证书,该应用程序仍会在设置文件中查找。因此,设置文件必须包含IS4密钥的核心详细信息。
此外,azure不会将证书放置在linux容器中的典型受信任位置。根据我的阅读,看来唯一的方法是挂载卷(在这种情况下为Azure存储文件共享),并使用上载到该文件共享的证书。
我可以确认它在本地可以运行,但是现在我仍然在运行容器时遇到问题,前端加载,并且看来Web api项目无法启动。我将发布另一个问题来解决该问题。
解决方法
原始答案
我认为问题可能是您正在尝试使用Windows证书存储区在Linux容器中加载证书。
文档here很好地概述了如何在Linux托管的应用程序中使用应用程序服务私有证书:
- 在Azure门户的左侧菜单中,选择“应用程序服务”>“ ”。
- 从应用程序的左侧导航中,选择TLS / SSL设置,然后选择私钥证书(.pfx)或公钥证书(.cer)。
- 找到您要使用的证书并复制指纹。
- 要在您的应用程序代码中访问证书,请将其指纹添加到WEBSITE_LOAD_CERTIFICATES应用程序设置中。
- WEBSITE_LOAD_CERTIFICATES应用程序设置使指定的证书可作为文件供Linux托管应用程序(包括自定义容器应用程序)访问。这些文件位于以下目录中:
- 私人证书-/var/ssl/private(.p12文件)
- 公共证书-/var/ssl/certs(.der文件)
- 使用下面的代码示例将指定的证书加载到Linux托管的应用程序(包括自定义容器应用程序)中:
using System; using System.IO; using System.Security.Cryptography.X509Certificates; var bytes = File.ReadAllBytes($"/var/ssl/private/{Configuration["WEBSITE_LOAD_CERTIFICATES"]}.p12"); var cert = new X509Certificate2(bytes);
签名证书
以下是我用来生成签名凭证的步骤:
- 安装OpenSSL。
- 生成私钥和公共证书。
- 运行
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout example.com.key -out example.com.crt -subj "/CN=example.com" -days 365
用站点名称替换example.com
。
- 运行
- 将以上内容合并为一个PFX文件。
- 运行
openssl pkcs12 -export -out example.com.pfx -inkey example.com.key -in example.com.crt
,将example.com
替换为网站名称。
- 运行
- 将PFX文件上传到Azure。
- 在Azure门户的左侧菜单中,选择“应用程序服务”>“ ”。
- 从应用程序的左侧导航中,选择TLS / SSL设置,然后选择私钥证书(.pfx),然后上传上述PFX文件。
- 配置应用程序设置。
- 将上述PFX文件的指纹添加到App Service中的WEBSITE_LOAD_CERTIFICATES应用程序设置中。
IdentityServer
下面的代码示例显示了完整的Startup.cs
配置,可用于启动和运行IdentityServer应用程序:
namespace IdentityServer
{
public class Startup
{
public Startup(IConfiguration configuration,IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application,visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
void ConfigureDbContext(DbContextOptionsBuilder builder)
{
builder.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"));
}
var builder = services.AddIdentityServer()
.AddConfigurationStore(options => { options.ConfigureDbContext = ConfigureDbContext; })
.AddOperationalStore(options => { options.ConfigureDbContext = ConfigureDbContext; });
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
try
{
var bytes = File.ReadAllBytes($"/var/ssl/private/{Configuration["WEBSITE_LOAD_CERTIFICATES"]}.p12");
var certificate = new X509Certificate2(bytes);
builder.AddSigningCredential(certificate);
}
catch (FileNotFoundException)
{
throw new Exception($"The certificate with the thumbprint \"{Configuration["WEBSITE_LOAD_CERTIFICATES"].Substring(0,8)}...\" could not be found.");
}
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
}
}
清洁架构
以下代码示例显示了完整的DependencyInjection.cs
配置,可用于启动和运行Clean Architecture应用程序:
namespace CleanArchitecture.Infrastructure
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services,IConfiguration configuration)
{
void ConfigureDbContext(DbContextOptionsBuilder builder)
{
if (configuration.GetValue<bool>("UseInMemoryDatabase"))
{
builder.UseInMemoryDatabase("CleanArchitectureDb");
}
else
{
builder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName));
}
}
services.AddDbContext<ApplicationDbContext>(ConfigureDbContext);
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IDomainEventService,DomainEventService>();
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
var builder = services.AddIdentityServer()
.AddConfigurationStore(options => { options.ConfigureDbContext = ConfigureDbContext; })
.AddOperationalStore(options => { options.ConfigureDbContext = ConfigureDbContext; })
.AddAspNetIdentity<ApplicationUser>();
var bytes = File.ReadAllBytes($"/var/ssl/private/{Configuration["WEBSITE_LOAD_CERTIFICATES"]}.p12");
var certificate = new X509Certificate2(bytes);
builder.AddSigningCredential(certificate);
services.AddTransient<IDateTime,DateTimeService>();
services.AddTransient<IIdentityService,IdentityService>();
services.AddTransient<ICsvFileBuilder,CsvFileBuilder>();
services.AddAuthentication()
.AddIdentityServerJwt();
return services;
}
}
}
,
我认为问题在于容器中的应用程序不信任本地创建的开发人员证书。您只能在计算机上使用它,因为您的计算机上已安装了开发者根证书。
容器永远不会信任dotnet dev-certs创建的证书。
您需要获得一个适当可信的证书,例如从LetsEncrypt获得。
, .Net Clean Architecture正在services.AddIdentityServer() .AddApiAuthorization<ApplicationUser,ApplicationDbContext>();
中调用DependencyInjection.cs
,这是扩展方法。此方法在内部调用其他方法,其中一堆是.AddSigningCredentials()
。不幸的是,此默认方法在Linux环境中将失败,因为它无法读取裸露的私钥。根据此issue,您需要在Linux中自己构建PFX。
我认为解决方案:
- 删除
.AddApiAuthorization<ApplicationUser,ApplicationDbContext>();
- 编写自己的方法
var bytes = File.ReadAllBytes($"/var/ssl/private/{thump_print_goes_here}.p12");
var certificate = new X509Certificate2(bytes);
var builder = services.AddIdentityServer()
.AddAspNetIdentity<ApplicationUser>()
.AddOperationalStore<ApplicationDbContext>()
.AddIdentityResources()
.AddApiResources()
.AddClients()
.AddSigningCredential(certificate);
,
我遇到了在Linux应用程序服务上运行.net core spa模板的问题。我还创建了一个自签名的.pfx,如tnc1997的答案中所述。尽管答案可以拼凑,但对我来说,陷阱是:
- 引用证书路径时,请勿使用上载的.pfx文件名。相反,如前所述,您的证书文件获得了一个新名称“ .p12”,并且在Linux容器中的“ / var / ssl / private /”下找到了。
- 指定空白密码。不要为上传的.pfx文件指定密码。而是将appsetting的“ IdentityServer__Key__Password”设置为“”(空)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。