在Ubuntu Linux上使用SSL调用Kestrel上的ASP.NET Core应用程序时出现超时问题。

huangapple go评论140阅读模式
英文:

Timeout when calling ASP.NET Core app on Kestrel in Docker on Ubuntu Linux with SSL

问题

我遇到了在Ubuntu 20.04上使用SSL在Docker中运行一个简单的ASP.NET Core Hello World应用程序的问题。以下是设置的详细信息:

  • 开发环境是位于Windows 10上的Visual Studio 2022。
  • 框架是在.NET 6上运行的ASP.NET Core。
  • 我正在使用在创建新的ASP.NET Core项目时创建的原始Hello World应用程序。除了向应用程序添加Docker文件之外,我没有进行任何更改。
  • 目标运行环境是运行在Linux Docker容器内部的Kestrel服务器,该容器本身运行在Ubuntu 20.04上。
  • 我尝试使用Ubuntu机器上的Firefox访问应用程序。

(采用这种奇怪的设置背后的原因是,我们有一个客户希望在他们的Ubuntu服务器上托管我们的应用程序。然而,我们的遗留代码库和团队的技能集决定我们在Windows上使用.NET进行开发。因此,我只是尝试让一个POC(概念验证)工作,以调查如何在Ubuntu上在Docker中运行ASP.NET Core应用程序。)

我正在使用的Docker文件只是Visual Studio为我创建的未修改版本,当我告诉它添加Docker支持时。以下是Docker文件的内容:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["AspNetSsl/AspNetSsl.csproj", "AspNetSsl/"]
RUN dotnet restore "AspNetSsl/AspNetSsl.csproj"
COPY . .
WORKDIR "/src/AspNetSsl"
RUN dotnet build "AspNetSsl.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "AspNetSsl.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AspNetSsl.dll"]

以下是项目的launchsettings.json:

{
  "profiles": {
    "AspNetSsl": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "dotnetRunMessages": true,
      "applicationUrl": "https://localhost:7033;http://localhost:5041"
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
      "publishAllPorts": true,
      "useSSL": true
    }
  },
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:34953",
      "sslPort": 44378
    }
  }
}

还有appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

以及Program.cs(再次强调,这只是默认的、未修改的模板):

namespace AspNetSsl
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // 将服务添加到容器中。
            builder.Services.AddRazorPages();

            var app = builder.Build();

            // 配置HTTP请求管道。
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // 默认的HSTS值为30天。您可能希望根据生产场景进行更改,参见https://aka.ms/aspnetcore-hsts。
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapRazorPages();

            app.Run();
        }
    }
}

这是我用来构建、部署和运行Docker映像/容器的过程:

在我的Windows开发机上:

  1. 打开PowerShell,导航到解决方案文件夹(其中包含Docker文件)并运行以下命令:
docker build -t aspnetssl .
  1. 仍然在PowerShell中,使用以下命令将映像从Docker导出到.tar文件:
docker save -o aspnetssl.tar aspnetssl
  1. 将.tar文件复制到与我的Ubuntu虚拟机共享的文件夹(称为VMShared)。

在我的Ubuntu 20.04虚拟机上:

首先,我需要创建一个自签名证书,以便应用程序可以使用SSL。我参考了这篇博客文章中的步骤:

  1. 在我的主目录中创建一个名为https.config的文件,内容如下:
[ req ]
default_bits       = 2048
default_md         = sha256
default_keyfile    = key.pem
prompt             = no
encrypt_key        = no

distinguished_name = req_distinguished_name
req_extensions     = v3_req
x509_extensions    = v3_req

[ req_distinguished_name ]
commonName             = "localhost"

[ v3_req ]
subjectAltName      = DNS:localhost
basicConstraints    = critical, CA:false
keyUsage            = critical, keyEncipherment
extendedKeyUsage    = critical, 1.3.6.1.5.5.7.3.1
  1. 运行以下命令生成私钥和证书签名请求:
openssl req -config https.config -new -out csr.pem
  1. 运行以下命令创建自签名证书:
openssl x509 -req -days 365 -extfile https.config -extensions v3_req -in csr.pem -signkey key.pem -out https.crt
  1. 运行以下命
英文:

I am having trouble getting a simple ASP.NET Core Hello World app working in Docker on Ubuntu 20.04 using SSL. Here are the setup details:

  • Development environment is Visual Studio 2022 on Windows 10.
  • Framework is ASP.NET Core running on .NET 6.
  • I am using the vanilla hello world app that gets created when creating a new ASP.NET
    Core project in VS. I have changed nothing except add a Dockerfile to the application.
  • Target run environment is a Kestrel server running inside of a Linux Docker container which itself is running on Ubuntu 20.04.
  • I am trying to access the application using Firefox on the Ubuntu machine.

(The reasoning behind this funky setup is that we have a customer who wants to host our app inside a Docker container on their Ubuntu box. However our legacy codebase and team's skillset predicates that we develop in .NET on Windows. Hence I am just trying to get a POC working to investigate how to run an ASP.NET Core app in Docker on Ubuntu.)

The Dockerfile that I am using is just the unmodified version that Visual Studio created for me when I tell it to add Docker support:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["AspNetSsl/AspNetSsl.csproj", "AspNetSsl/"]
RUN dotnet restore "AspNetSsl/AspNetSsl.csproj"
COPY . .
WORKDIR "/src/AspNetSsl"
RUN dotnet build "AspNetSsl.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "AspNetSsl.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AspNetSsl.dll"]

Here is the launchsettings.json for the project:

{
  "profiles": {
    "AspNetSsl": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "dotnetRunMessages": true,
      "applicationUrl": "https://localhost:7033;http://localhost:5041"
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
      "publishAllPorts": true,
      "useSSL": true
    }
  },
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:34953",
      "sslPort": 44378
    }
  }
}

And the appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

And the Program.cs (again, this is just the default, unmodified template):

namespace AspNetSsl
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddRazorPages();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapRazorPages();

            app.Run();
        }
    }
}

This is the process I go through to build and deploy and run the Docker image/container:

On my Windows development machine:

  1. Open Powershell, navigate to the solution folder (where the Dockerfile lives) and run this command:
docker build -t aspnetssl .
  1. Still in Powershell, export the image from docker to a tar file using this command:
docker save -o aspnetssl.tar aspnetssl
  1. Copy the .tar file to the folder shared with my Ubuntu VM (called VMShared).

On my Ubuntu 20.04 Virtual Machine

First I need to create a self-signed certificate so that the app can use SSL. I referenced this blog post for these steps:

  1. Create an https.config file in my home directory with the following content:
[ req ]
default_bits       = 2048
default_md         = sha256
default_keyfile    = key.pem
prompt             = no
encrypt_key        = no

distinguished_name = req_distinguished_name
req_extensions     = v3_req
x509_extensions    = v3_req

[ req_distinguished_name ]
commonName             = "localhost"

[ v3_req ]
subjectAltName      = DNS:localhost
basicConstraints    = critical, CA:false
keyUsage            = critical, keyEncipherment
extendedKeyUsage    = critical, 1.3.6.1.5.5.7.3.1
  1. Run the following command to generate a private key and certificate signing request:
openssl req -config https.config -new -out csr.pem
  1. Run the following command to create a self-signed certificate:
openssl x509 -req -days 365 -extfile https.config -extensions v3_req -in csr.pem -signkey key.pem -out https.crt
  1. Run the following command to generate a pfx file that can be used with Kestrel:
openssl pkcs12 -export -out https.pfx -inkey key.pem -in https.crt -password pass:password
  1. Copy the https.pfx to my shared folder at /mnt/hgfs/VMShared/https to make it accessible to the Kestrel server (it could go anywhere as long as it can be passed to Docker via a volume mount)

Now I need to trust the certificate:

  1. Copy https.crt to /usr/local/share/ca-certificates/ and run the sudo update-ca-certificates command, which gives an output that indicates 1 certificate was added.

  2. Add the cert to Firefox's trusted certificate store:

  • Find the Firefox default profile

在Ubuntu Linux上使用SSL调用Kestrel上的ASP.NET Core应用程序时出现超时问题。

  • Add the cert to Firefox's cert store for that profile:
sudo certutil -A -n "https" -t "TC,," -i ~/https.crt -d sql:/home/u/.mozilla/firefox/ayi0vczl.default-release
  • Verify that it was put into that store:
sudo certutil -d sql:/home/u/.mozilla/firefox/ayi0vczl.default-release -L

which shows this output:

在Ubuntu Linux上使用SSL调用Kestrel上的ASP.NET Core应用程序时出现超时问题。

Finally I'm ready to run the docker image:

  1. Load the docker image into docker:
sudo docker load -i /mnt/hgfs/VMShared/aspnetssl.tar
  1. Run the docker image by running the following command (packaged inside of a script file):
docker run --rm \
-p 5000:80 \
-p 5001:443 \
-v /mnt/hgfs/VMShared/https:/https/ \
-e ASPNETCORE_Kestrel__Certificates__Default__Password="password" \
-e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/https.pfx \
-e ASPNETCORE_URLS="https://+:443;http://+:80" \
-e ASPNETCORE_ENVIRONMENT=Docker \
aspnetssl:latest

When the container lauches, I see this in the terminal window:

warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
      Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {3ad53391-8a83-486b-9c15-f783ffe52d87} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://[::]:443
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Docker
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app/

No explicit errors, and the warnings look okay to me, so it seems to be running fine. However when I try to navigate to it in Firefox, either to http://localhost:5000 or https://localhost:5001, it just hangs and I eventually get a timeout error.

在Ubuntu Linux上使用SSL调用Kestrel上的ASP.NET Core应用程序时出现超时问题。

My current working theory is that something is wrong with my SSL setup, like Firefox is not able to access the certificate or something. It's difficult to diagnose because I'm not very experienced with SSL and web technologies and I'm not sure what errors or logs I should be looking at. However there are a few possible failure points that I can think of:

  • The self-signed certificate was not generated properly.
  • The cert was not trusted on the Ubuntu VM properly.
    • I also tried adding the https.crt file to the /etc/ssl/certs and /etc/ca-certificates folders, but that didn't seem to work.
  • Firefox does not have access to the cert or cannot trust it for some reason.
  • The Kestrel server is not correctly giving the cert to Firefox.

I'm not sure if any of these make sense, but my gut feeling is that the breakage has something to do with SSL since I was able to get a version of this Hello World app to work over just http by removing some lines from Program.cs as well as setting "useSSL":false in my launchsettings.json file.

Does anyone have any insight on why Firefox is timing out when trying to ping the ASP.NET app? I appreciate any help that is given.

答案1

得分: 1

我发现我的问题不在于所描述的设置,而是在于暂停/恢复虚拟机时出现的问题。 (我使用的是VMWare Workstation 17 Pro,版本17.0.0。另外,我使用桥接网络连接,勾选了“复制物理网络连接状态”选项。)

当我从关机状态启动虚拟机时,上述设置有效,我能够访问我的Hello World应用程序。如果我暂停虚拟机,然后恢复它,我开始出现所描述的超时问题。如果我重新启动虚拟机,问题就消失了。我想知道为什么会发生这种情况,但这是一个完全不同的问题。

英文:

So figured out that my problem is not with anything in the setup described but with suspending/pausing the virtual machine and then resuming. (I'm using VMWare Workstation 17 Pro, version 17.0.0. Also I'm using a bridged network connection with "Replicate physical network connection state" checked.)

When I start up a VM from powered off, then the above setup works and I am able to access my Hello World application. If I suspend the VM and then resume it, I start having the timeout issue described. If I restart the VM, then the issue goes away. I would like to know why this is happening, but that is an entirely different question.

huangapple
  • 本文由 发表于 2023年3月4日 09:15:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75633083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定