簡體   English   中英

在帶有 IdentityServer4 認證/http 錯誤的 azure Web 應用容器上部署 .net Core 3 linux 容器

[英]Deploying .net Core 3 linux container on azure web app container with IdentityServer4 certification/http error

我正在嘗試使用.Net Core Clean Architecture App Template並讓它在容器中運行並通過 azure CI/CD 管道進行部署

我在具有端口 5001 的 linux 容器中本地運行模板的容器化版本,一切正常。

我的 azure 管道構建過程正常工作,它在我的容器注冊表中創建圖像。

問題是,一旦我部署/發布到容器的 Web 應用程序,該應用程序就會失敗並引發以下錯誤:

應用程序啟動異常 System.InvalidOperationException:在 Microsoft.AspNetCore.ApiAuthorization.IdentityServer.SigningKeysLoader.LoadFromStoreCert(String subject, String storeName, StoreLocation storeLocation, DateTimeOffset currentTime)

我做了什么:

  1. 按照MS 的這些文檔,我創建了一個本地開發證書:

    dotnet dev-certs https -ep %USERPROFILE%\\.aspnet\\https\\aspnetapp.pfx -p { password here }

    dotnet dev-certs https --trust

  2. 然后我將其作為私有 .pfx 證書導入到 Web 應用程序中。

  3. 我添加了一個應用程序設置WEBSITE_LOAD_CERTIFICATES與證書的“拇指”值

  4. 我在 Identity Server appSettings.json 部分使用了導入證書的“主機名” (在我的情況下為主機名 = 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"
    }
  }

但是,這又出現了另一個錯誤:

加載證書時出錯。 密碼不正確或進程沒有權限將密鑰存儲在 Keyset 'EphemeralKeySet' Interop+Crypto+OpenSslCryptographicException: error:23076071:PKCS12routines:PKCS12_parse:mac verify failure

編輯 3:有效的 Azure 應用程序證書

我購買了 Azure 應用證書並添加了一個設置了 TSL 的自定義域,但出現了相同的錯誤

編輯 4:在代碼 startup.cs 中加載證書 - 新錯誤:

我現在知道我不能使用證書存儲 CurrentUser/My 因為那是用於 Windows 的。 Linux 容器必須在代碼中手動加載證書。

我正在使用已添加到 azure web 應用程序的 aa 應用程序證書的指紋。 它是一個私有的 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 configureservicesapp.UseIdentityServer()在 startup.cs configure

未處理的異常。 System.InvalidOperationException:裝飾器已注冊類型:IAuthenticationService。

我不確定如何將證書添加到 app.UseIdentityServer(); 線。

編輯 5

經過更多的挖掘,不幸的是@tnc1997 答案將不起作用。 在 asp.net core 3 中,我的satrtup.cs 中app.UseIdentityServer 的調用在內部尊重一種將在 appsetting(environment).json 文件中查找身份服務器 Key、File、Pass 等的方法。

結果,即使我在 tnc1997 所示的代碼中加載了證書,應用程序仍然會在設置文件中查找。 因此,設置文件必須包含 IS4 密鑰的正確詳細信息。

此外,azure 不會將證書放在 linux 容器中典型的受信任位置。 從我讀過的內容來看,似乎唯一的方法是掛載一個卷(在這種情況下是一個 azure 存儲文件共享)並使用上傳到該文件共享的證書。

我可以確認這在本地有效,但現在我在運行容器時仍然遇到問題,前端加載並且 web api 項目似乎沒有啟動。 我將發布另一個問題來解決該問題。

原答案

我認為問題可能是您嘗試使用 Windows 證書存儲在 Linux 容器中加載證書。

此處的文檔很好地概述了如何在 Linux 托管應用程序中使用應用程序服務私有證書:

  1. 在 Azure 門戶中,從左側菜單中選擇應用服務 > <app-name>。
  2. 從應用程序的左側導航中,選擇 TLS/SSL 設置,然后選擇私鑰證書 (.pfx) 或公鑰證書 (.cer)。
  3. 找到您要使用的證書並復制指紋。
  4. 要訪問應用代碼中的證書,請將其指紋添加到 WEBSITE_LOAD_CERTIFICATES 應用設置。
  5. WEBSITE_LOAD_CERTIFICATES 應用程序設置使您的 Linux 托管應用程序(包括自定義容器應用程序)可以作為文件訪問指定的證書。 這些文件位於以下目錄下:
    • 私有證書 - /var/ssl/private(.p12 文件)
    • 公共證書 - /var/ssl/certs(.der 文件)
  6. 使用下面的代碼示例將指定的證書加載到您的 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);

簽署憑證

以下是我用來生成簽名憑據的步驟:

  1. 安裝OpenSSL
  2. 生成私鑰和公共證書。
    1. 運行openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout example.com.key -out example.com.crt -subj "/CN=example.com" -days 365用站點名稱替換example.com .
  3. 將以上內容合並為一個 PFX 文件。
    1. 運行openssl pkcs12 -export -out example.com.pfx -inkey example.com.key -in example.com.crt用站點名稱替換example.com
  4. 將 PFX 文件上傳到 Azure。
    1. 在 Azure 門戶中,從左側菜單中選擇應用服務 > <app-name>。
    2. 從應用程序的左側導航中,選擇 TLS/SSL 設置,然后選擇私鑰證書 (.pfx),然后上傳上述 PFX 文件。
  5. 配置應用程序設置。
    1. 將上述 PFX 文件的指紋添加到應用服務中的 WEBSITE_LOAD_CERTIFICATES 應用設置。

身份服務器

下面的代碼示例顯示了一個完整的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

我在 linux 應用服務上運行 .net core spa 模板時遇到了這個問題。 我還創建了一個自簽名 .pfx,如 tnc1997 的回答中所述。 雖然答案可以拼湊起來,但對我來說,問題是:

  • 引用您的證書路徑時,請勿使用上傳的 .pfx 文件名。 相反,如前所述,您的證書文件獲得了一個新名稱“.p12”,並且(在 linux 容器中)位於“/var/ssl/private/”下。
  • 指定一個空白密碼。 不要為上傳的 .pfx 文件指定密碼。 而是將 appsetting "IdentityServer__Key__Password" 設置為 ""(空)。

.Net Clean Architecture正在調用services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(); DependencyInjection.cs ,這是擴展方法。 這個方法在內部調用了一堆其他方法,其中一個是.AddSigningCredentials() 不幸的是,這種默認方法在 Linux 環境中會失敗,因為它無法讀取裸私鑰。 根據這個問題你需要在Linux中自己構建PFX。

在我看來的解決方案:

  1. 刪除.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
  2. 編寫自己的方法
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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM