0

I have 2 .NET core projects that work perfectly with each other when I run the normal, I have an API and an MVC UI. When I run them in docker containers, I go on my API with swagger, I figured out how to connect my PostgreSQL and insert data successfully. When I go on my UI and I call the API I get this error: An unhandled exception occurred while processing the request AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch, RemoteCertificateChainErrors. I created a certificate and have it in my .NET project. I am guessing I have to do something different to make this work with a container. (If this is what is wrong, I’m not sure yet.) To put it in my normal project I did this.

.UseKestrel(serverOptions =>
                {
                    serverOptions.Listen(IPAddress.Loopback, 5010);
                    serverOptions.Listen(IPAddress.Loopback, 5011, listenOptions =>
                    {
                        listenOptions.UseHttps("localpfx.pfx", "password");
                    });
                });

I have a Dockerfile in my .NET core project. I am not using docker-compose. Any ideas on how to get my UI to call my API both running in separate containers? This is the last part of my project. Thank you.

P.S.

MVC UI project =   "httpPort": 5010,        "sslPort":  5011
API project =      "httpPort": 5000,        "sslPort": 5001

Edit: Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
#USER ContainerAdministrator
#RUN net localgroup administrators /add "User Manager\ContainerUser"
#USER ContainerUser
WORKDIR /app
EXPOSE 5011
#EXPOSE 5010
EXPOSE 80
ENV ASPNETCORE_ENVIRONMENT=Development
#ENV ASPNETCORE_URLS=https://*:5011

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

FROM build AS publish
RUN dotnet publish "UserUISolution.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "UserUISolution.dll"]
Bigbear
  • 489
  • 2
  • 5
  • 21
  • Please share you dockerfile's – Alexander Feb 24 '21 at 17:05
  • Are you using self signed certificate? If so, did you make it trusted on your machine? I think in this case it would work ok on your machine but not on container, because container doesn't trust the certificate by default – Alexander Feb 24 '21 at 18:49
  • Yes I created a self signed cert, It is trusted as well. The lock icon in browser says Valid for certificate. When I run project in kestrel everything works great. When I run on container they both work alone but when UI calls API I get the Error. Separately I can use Swagger on API. – Bigbear Feb 24 '21 at 19:33
  • So last night I took off all the HTTPS on my containers. My containers worked perfectly than! My UI talked to my API no issues. So now I'm 100% sure this is a HTTPS certificate issue. I'm assuming I need to get my cert in my Docker Containers then it may work. – Bigbear Feb 25 '21 at 14:25
  • How do you call API from UI? Directly via js or UI calls API from controller using `HttpClient`? – Alexander Feb 25 '21 at 18:09
  • Opt #2. using (var httpclient = new HttpClient()) { using (var response = await httpclient.GetAsync("http://localhost:5000/api/account/getusers")) – Bigbear Feb 25 '21 at 18:45

1 Answers1

1

Obviously the error occurs because you are using self-signed certificate and it is not trusted. You need to make the UI container to trust the certificate used for API. You can either make the container to trust a certificate or trust it programatically. For the second option it's needed to configure HttpClient to trust the specific certificate.

For ASP.NET Core it's preferred to use IHttpClientFactory. Configuration in UI project will be the following

//this validator will trust incoming certificate with matching thumbprint
public class SingleCertificateValidator
{
    private readonly X509Certificate2 _trustedCertificate;

    public SingleCertificateValidator(X509Certificate2 trustedCertificate)
    {
        _trustedCertificate = trustedCertificate;
    }

    public bool Validate(HttpRequestMessage httpRequestMessage, X509Certificate2 x509Certificate2, X509Chain x509Chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        string thumbprint = x509Certificate2.GetCertHashString();
        //todo: add more validation logic if needed
        return thumbprint == _trustedCertificate.Thumbprint;
    }
}

In Startup.cs

services
    .AddHttpClient("Default")
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var certificate = new X509Certificate2("localpfx.pfx", "password");
        var certificateValidator = new SingleCertificateValidator(certificate);

        return new HttpClientHandler
        {
            ServerCertificateCustomValidationCallback = certificateValidator.Validate
        };
    });

Usage

public class TestController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public TestController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<IActionResult> TestHttpClient()
    {
        var client = _httpClientFactory.CreateClient("Default");
        var response = await client.GetAsync("url");

        //...
        return Json(result);
    }
}

For "manually" instantiated HttpClient (new HttpClient)

//...
var certificateValidator = new SingleCertificateValidator(certificate);
var clientHandler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = certificateValidator.Validate
};
var client = new HttpClient(clientHandler);
//...
Alexander
  • 9,104
  • 1
  • 17
  • 41