Closed Rick-Anderson closed 5 years ago
Subject: RE: Docker for ASP.NET Core 2.1 Preview 2
. Provided that your docker file and docker compose look like the ones that VS generates when you add VS support, there are a couple of steps you need to take to enable it manually. In essence, you need to get the HTTPS development certificate from your machine into the container image as a pfx file in a special location on disk that we recognize and provide the password through user.
• Modify the dockerfile to expose the port 443 with • EXPOSE 443 • Modify the docker-compose override file to map ports, volumes and environment variables as follows: environment:
per Javier: For setting up a non development certificate on kestrel the instructions are the same as for the development case (obviously you need to provide the certificate) and you need to set the path to the certificate in a configuration key. "Kestrel:Certificates:Default:Path"
Is there an update for this now they we are in RTM for 2.1 and preparing for 2.1.1 release?
Can we please avoid things like "Create an application on Visual Studio using the MVC template." in the documentation for a cross-platform framework like .NET Core, that also runs on Linux and Mac devices?
Hi.
The method works fine if I run the application in development environment. Is there are way to specify what certificate and port should kestrel use in production mode?
When using a pfx file generated by dotnet dev-certs on my local Windows machine, I ran into a
error:23076071:PKCS12 routines:PKCS12_parse:mac verify failure
So I switched to simply calling dotnet dev-certs https --export-path ./localhost.pfx -p $LOCALHOST_CERTIFICATE_PWD as part of building the Docker image and then referencing that in Startup.cs using KestrelServerOptions:
listenOptions.UseHttps("../../localhost.pfx", Environment.GetEnvironmentVariable("LOCALHOST_CERTIFICATE_PWD"));
I had an issue with Docker-Compose.yml and Visual Studio.Net 15.7.5. The resolution was adding the entry for secret,json:
{
"Kestrel":{
"Certificates":{
"Default":{
"Path": "/root/.aspnet/https/<AppName>>.pfx",
"Password": "<<Your-Password>>"
}
}
}
}
Do these same rules apply to MacOS users? How about those of us using Kitematic? I'm wondering if I can extrapolate the notes above to fit into the areas on/in the attached screenshots.
In essence, you need to get the HTTPS development certificate from your machine into the container image as a pfx file in a special location on disk that we recognize and provide the password through user.
I have tried this in Docker and unless I specify the certificate location and password in the Startup configuration it crashes with:
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
I followed the guide provided by @Rick-Anderson which seems to imply that UseStartup() now checks for the existence of the cert at root/.aspnet/https/<
The UserSecrets is working and configured correctly as i am using it to get the path and password, it works using this code:
.UseKestrel(options =>
{
var configuration = (IConfiguration)options.ApplicationServices.GetService(typeof(IConfiguration));
var httpsPort = configuration.GetValue("ASPNETCORE_HTTPS_PORT", 443);
var certPassword = configuration.GetValue<string>("Kestrel:Certificates:Development:Password");
var certPath = configuration.GetValue<string>("Kestrel:Certificates:Development:Path");
options.Listen(IPAddress.Any, httpsPort, listenOptions =>
{
listenOptions.UseHttps(certPath, certPassword);
});
});
I have found that if the path to the cert is specified in user secrets or an env var, it breaks.
I was able to get it to work using the default startup by removing the path which i added when trying a bunch of other things:
dotnet user-secrets remove "Kestrel:Certificates:Development:Path"
I can break it again by re-adding the path:
dotnet user-secrets set "Kestrel:Certificates:Development:Path" "/root/.aspnet/https/<<AppName>>.pfx"
The path is correct and the cert file exists as demonstrated in the code above, but for some reason it doesn't like having the path specified even if it is correct. The guide above does only say to add the password, but there should be no reason that adding the correct path, or even any valid path where the cert exists could not be made to work.
This only applies to Development environment, in Production environment it works as per the docs using the 'Default' path:
dotnet user-secrets set "Kestrel:Certificates:Default:Path" "/root/.aspnet/https/<<AppName>>.pfx"
I have published a repo which uses this technique which can be used to reproduce this issue: https://github.com/chrisckc/DotNetCoreAureliaSPA
@danroth27 what's the priority on this? We might need some help - at least for the first draft. @chrisckc could you provide a draft?
@Rick-Anderson I should be revisiting that repo soon to build something out so will have a dig around to see what is causing the issue i posted about.
Ok, i traced back the error message:
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
which occurs when "Kestrel:Certificates:Development:Path" is specified in the configuration from UserSecrets or env vars despite the path being correct and the file existing.
I cloned the KestrelHttpServer repo and traced the message back to the Load() method inside the KestrelConfigurationLoader class (line 216).
The Load() method calls LoadDefaultCert(ConfigurationReader)
LoadDefaultCert checks if the "Kestrel:Certificates:Default" config entry exists, if so then the cert is loaded using the LoadCertificate(CertificateConfig certInfo, string endpointName)
method which just builds the path using Path.Combine(env.ContentRootPath, certInfo.Path)
, which if certInfo.Path is an absolute path just returns certInfo.Path due to the way Path.Combine works.
However if the "Kestrel:Certificates:Default" config entry does not exist is call FindDeveloperCertificateFile(configReader, logger)
which is where things get a little odd.
The following particularly ugly piece of code is used to locate the development certificate:
if (configReader.Certificates.TryGetValue("Development", out var certificateConfig) &&
certificateConfig.Path == null &&
certificateConfig.Password != null &&
TryGetCertificatePath(out certificatePath) &&
File.Exists(certificatePath))
{
var certificate = new X509Certificate2(certificatePath, certificateConfig.Password);
return IsDevelopmentCertificate(certificate) ? certificate : null;
}
else if (!File.Exists(certificatePath))
{
logger.FailedToLocateDevelopmentCertificateFile(certificatePath);
}
The TryGetCertificatePath(out certificatePath) method looks for a cert named $"{appName}.pfx"
in any of the following paths to cover both Mac OSX and Windows:
$APPDATA/ASP.NET/https, $APPDATA/.aspnet/https, $HOME/ASP.NET/https, $HOME/.aspnet/https
That piece of code is completely broken if it's intention is to either use the certificateConfig.Path or go and find the certificate file from a default location and naming convention, which would be the sensible thing to do.
The result of that 'if' statement is that if a certificate path is supplied in the config it is never used.
It also will report a misleading error because a missing password would result in a log error which says that the certificate could not be located and also an exception inside the Load() method stating that that certificate could not be found.
Experiencing misleading errors has always been an issue when working with Microsoft software in the past, was hoping that the situation has been improving.
I also noticed that the IsDevelopmentCertificate method checks is the certificate.Subject is equal to "CN=localhost", which may not always be the case for every developers development environment. For example if you are developing using docker running on another machine or a VM etc.
The code can be simplified, i don't really see the need to use the 2 separate configuration structures: "Kestrel:Certificates:Default" and "Kestrel:Certificates:Development" and check the subject of the cert. Just one config with a check to try and find the cert from the default location and name if it is not supplied in ":Path"?
Looking at the referenced issue on line 332 of KestrelConfigurationLoader.cs
https://github.com/aspnet/Hosting/issues/1294
It looks like the broken code was added as part of that, which seemed to start off with good intentions. Maybe the behaviour is intentional, if so i would like to know the reasoning.
@Rick-Anderson Any documentation written to cover this setup with its current behaviour would look rather odd.
@javiercn @Tratcher
That piece of code is completely broken if it's intention is to either use the certificateConfig.Path or go and find the certificate file from a default location and naming convention,
It's not meant to be used with a path, the development certificate key is only there to support tooling scenarios. If you want to specify a path and a password, simply use the Default key.
I also noticed that the IsDevelopmentCertificate method checks is the certificate.Subject is equal to "CN=localhost", which may not always be the case for every developers development environment.
It's only meant to support localhost scenarios (including docker localhost). Other scenarios will have security implications we would have to worry about.
It also will report a misleading error because a missing password would result in a log error which says that the certificate could not be located and also an exception inside the Load() method stating that that certificate could not be found.
There's no way AFAIK to check the validity of the password on the PFX.
I've been trying to follow this example and I'm tearing my hair out!
I get these exceptions: AuthenticationException: The remote certificate is invalid according to the validation procedure. HttpRequestException: The SSL connection could not be established, see inner exception.
I have two containers, one talks to the other by HTTPS and by use of an alias configured in the compose file. The container being talked to doesn't have a public IP on purpose (Gateway Microservice architecture).
I have generated a certificate and am applying it to the service being called.
Any ideas?
Turns out I needed this: https://blog.roushtech.net/2016/12/20/ignoring-ssl-certificate-errors-net-core-httpclient/
and yes this is in my development environment.
Hey folks, this also affects scenarios when you try things out in Azure Shell or other similar shells. When you do:
dotnet new mvc
then
dotnet run
in first instance of Azure Shell, then open a new instance to test it out with
curl https://localhost:5001
you get:
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Ideally, I shouldn't need to use -k or -i.
I don't know very well the scenario for azure shell or other shells, but you should be able to disable https when you create the app through dotnet new (I think -no-https?).
The development HTTPS certificate is designed for development on the development machine. For "cloud native development" scenarios you need to either disable https or create a certificate and find a way to plumb it through your specific deployment scenario. In this case using -k
should be fine.
Also please keep in mind that it is not recommended to install the SDK on a production machine (as it contains things and setup used for development) and that the recommended approach is to either use the runtime image (as we do in our multi image docker builds) or at least disable the certificate generation on the local box by setting the DOTNET_GENERATE_ASPNET_CERTIFICATE
environment variable to false.
You can read about Azure Shell here. It's used to try things out from documentation, learning or even quick&dirty development sometimes. Biggest advantage of this is that you don't need anything installed on your local machine and it's easily disposable. This is not considered production machine - it's a docker container that has storage mounted to it.
Maybe I am doing this incorrectly but perhaps this line needs to change:
Export the HTTPS certificate into a PFX file using the dev-certs global tool to ${HOME}/.aspnet/https/<>.pfx using a password of your choice (dotnet dev-certs https -ep ${HOME}/.aspnet/https/ -p <>)
to
Export the HTTPS certificate into a PFX file using the dev-certs global tool to ${HOME}/.aspnet/https/<>.pfx using a password of your choice (dotnet dev-certs https -ep ${HOME}/.aspnet/https/yourCertName.pfx -p <>)
When I did it without a filename specified I got the error:
There was an error exporting HTTPS developer certificate to a file.
EDIT: It is insane how frustrating setting this all up is.
This may not be the best thread for this question, but:
We are using docker-compose to setup services that are dependent on one another, so we have external links from one of those services to several others. We have dev-certs setup for all of them, and they all work independently, but the service with the external links does not trust the dev-certs, so container to container traffic over https is not working. the dotnet dev-certs tool apparently does not have a --trust option in Linux. Is there some documentation on trusting those certs for container to container traffic in linux?
@disophisis There is no general way to trust certificates across linux distributions, it varies from distro to distro. The certificate generated by dotnet dev-certs is meant to be used for more basic scenarios where you are containerizing an app into a single container.
For the topology that you have, I imagine your machine to machine traffic uses service names instead of localhost, so my recommendation is to generate individual https certificates for those services through openssl or powershell, configure kestrel to use the appropriate certificate on each service and do whatever it takes to trust other certificates across the services. In this scenario it might make sense for you to create a CA certificate for development and generate TLS certificates with the CA certificate as the root, so that you only need to trust the CA certificate across services.
Hope this helps
Help me understand the issue here now that VS 15.9.1 does it automatically? Can we document what VS is doing automatically on Windows (with a Linux Container) so it's easier everywhere?
I'm currently running into a similar problem, in that I want to have a base docker-compose.yml
file for common configuration, a docker-compose.override.yml
file for development and a docker-compose.prod.yml
file for production, using external secrets
and configs
.
My current issue is that, since I'm using a reverse proxy configuration, according to the note here, I shouldn't need https from Kestrel. However, the code for the application doesn't have any mention of https, secure services or anything else, and yet after building a Dockerfile for this application and running nginx, aspnetcore and sql-server on Linux containers, I still get this damnable Unhandled Exception: System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
error.
Why would ASP.NET still require HTTPS in this instance? Would WsFederation authentication have anything to do with it?
No in-app components will request https for you, it has to come from config. Have you tried logging the config as observed by the app? https://github.com/aspnet/KestrelHttpServer/blob/5c1fcd664d39db8fe5c8e38052a3cc29f90322f6/samples/SampleApp/Startup.cs#L178-L185
I have not, I will try this after the holidays and get back to you then.
Okay, I put that section in my Program.cs
:
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
namespace Vue2Spa {
public class Program {
public static void Main(string[] args) {
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel(LogDevelopmentConfiguration)
.UseStartup<Startup>();
private static void LogDevelopmentConfiguration(
WebHostBuilderContext context,
KestrelServerOptions options) {
if (context.HostingEnvironment.IsDevelopment())
ShowConfig(context.Configuration);
}
private static void ShowConfig(IConfiguration configuration) {
foreach (var pair in configuration.GetChildren()) {
Console.WriteLine($"{pair.Path} - {pair.Value}");
ShowConfig(pair);
}
}
}
}
However, nothing gets logged and it still fails with the certificate error.
app_1_70b2dc094d0a | : Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
app_1_70b2dc094d0a | User profile is available. Using '/root/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
app_1_70b2dc094d0a | info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[58]
app_1_70b2dc094d0a | Creating key {8b718e81-eb89-4b52-ae29-918542806596} with creation date 2018-11-26 11:28:54Z, activation date 2018-11-26 11:28:54Z, and expiration date 2019-02-24 11:28:54Z.
app_1_70b2dc094d0a | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
app_1_70b2dc094d0a | No XML encryptor configured. Key {8b718e81-eb89-4b52-ae29-918542806596} may be persisted to storage in unencrypted form.
app_1_70b2dc094d0a | info: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[39]
app_1_70b2dc094d0a | Writing data to file '/root/.aspnet/DataProtection-Keys/key-8b718e81-eb89-4b52-ae29-918542806596.xml'.
app_1_70b2dc094d0a | crit: Microsoft.AspNetCore.Server.Kestrel[0]
app_1_70b2dc094d0a | Unable to start Kestrel.
app_1_70b2dc094d0a | System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
app_1_70b2dc094d0a | To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'.
app_1_70b2dc094d0a | For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(IServerAddressesFeature addresses, KestrelServerOptions serverOptions, ILogger logger, Func`2 createBinding)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
app_1_70b2dc094d0a | 2018-11-26 11:29:15,365 [1] FATAL Microsoft.AspNetCore.Server.Kestrel - Unable to start Kestrel.
app_1_70b2dc094d0a |
app_1_70b2dc094d0a | Unhandled Exception: System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
app_1_70b2dc094d0a | To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'.
app_1_70b2dc094d0a | For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(IServerAddressesFeature addresses, KestrelServerOptions serverOptions, ILogger logger, Func`2 createBinding)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.Internal.WebHost.StartAsync(CancellationToken cancellationToken)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String shutdownMessage)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token)
app_1_70b2dc094d0a | at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
app_1_70b2dc094d0a | at Vue2Spa.Program.Main(String[] args) in /source/Vue2Spa/Program.cs:line 14
app_app_1_70b2dc094d0a exited with code 139
EDIT: If it helps, I have made a gist containing the basic setup of the application.
@ryakstis You are telling Kestrel to listen on https on your dockerfile https://gist.github.com/ryakstis/4ea1c9883b6cec4c770fd5dfb1e100ae#file-dockerfile-L39
@javiercn haha, wow, I feel dumb now. That should have been obvious, but I never thought to look at environment variables when thinking about configuration. Thank you!
If it helps, I did manage to use the microsoft/dotnet:2.1-sdk-alpine
image to resolve the dev-cert
issue temporarily, since that automatically includes a dev cert. Knowing I can change the URL format helps make that no longer necessary.
@ryakstis No problem. Its easy to miss details like that.
Sorry but things in this thread is quite confusing for me as I am quite new to Docker. Please help me with these:
First of all, the default Dockerfile
file VS created cannot be built. I changed microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-1803
into microsoft/dotnet:2.1-aspnetcore-runtime
and microsoft/dotnet:2.1-sdk-nanoserver-1803
into microsoft/dotnet:2.1-sdk
and it pulled the files successfully. Is that the right way?
I assume I should create a docker-compose.yaml
file in the same folder as Dockerfile
? Do I need to run any command or Docker will automatically call it?
What is the content of that file? Can anyone give me a full content? @Rick-Anderson mentioned # Replace the values on the left by the values on your launchSettings.json
but I have no such file in my project. I also cannot find the port in Properties page (Debug tab) as usual because the profile is Docker. From Dockerfile
, I see those 2 ports btw:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
It would be great if someone could make a details steps on these.
(I am using VS version 15.9.3 and .NET Core 2.1.1)
@datvm
docker-compose up
to run your docker-compose.yml
file, and if you want to use the image built by your Dockerfile
, then you'd have to have a service in your docker-compose.yml
file with a build
value (see here).launchSettings.json
file would exist under/inside the Properties
folder of your project. In Visual Studio, you can expand Properties like a folder and under it you will see launchSettings.json
under normal circumstances.
EDIT: Microsoft has more documentation on launchSettings.json
here.I am trying to run my localhost to https in docker container but i am getting the following error (given in very next double quotes) and it is working perfectly over the http. I also attache the image of error "curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed" I am using the following technology Asp.net core 2.0, Docker version 18.09.0,
Docker compose is given bellow ` version: '3.4'
services:
tt.core.web: image: ${DOCKER_REGISTRY-}ttcoreweb build: context: . dockerfile: TT.Core.Web/Dockerfile environment:
${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro depends_on:
tt.core.downtime.webjob: image: ${DOCKER_REGISTRY-}ttcoredowntimewebjob environment: CORE_ENVIRONMENT: devdocker build: context: . dockerfile: TT.Core.Downtime.Webjob/Dockerfile depends_on:
tt.core.iothub.simulator: image: ${DOCKER_REGISTRY-}ttcoreiothubsimulator environment: CORE_ENVIRONMENT: devdocker build: context: . dockerfile: TT.Core.IotHub.Simulator/Dockerfile depends_on:
tt.core.labourtelemetry.webjob: image: ${DOCKER_REGISTRY-}ttcorelabourtelemetrywebjob environment: CORE_ENVIRONMENT: devdocker build: context: . dockerfile: TT.Core.LabourTelemetry.WebJob/Dockerfile depends_on:
tt.core.telemetry.webjob: image: ${DOCKER_REGISTRY-}ttcoretelemetrywebjob environment: CORE_ENVIRONMENT: devdocker build: context: . dockerfile: TT.Core.Telemetry.WebJob/Dockerfile depends_on:
db: image: microsoft/mssql-server-linux container_name: db environment: SA_PASSWORD: "Thingtrax%1" ACCEPT_EULA: "Y" MSSQL_PID: Express ports:
networks: app-network: driver: bridge
`
The environment needs to be development for the dev certificate to work
Yòu mean it should be development in place of devdocker? Devdocker is my development environment
@bilalMlaik The Development certifícate only gets loaded for the Development environment. If you want to use a different key for your development environment then you need to load the certificate manually providing the path to the certificate and the password in config.
@javiercn i loaded it manually in usekesteral method program.cs file by giving path of .pfx and userscret key in appsetting.json. but i am not giving the path of certificate.cert. the .pfx and key is getting move into container in the specified path
@bilalMlaik this documentatiom might be helpful Hosting ASP.NET Core Images with Docker over HTTPS.
I ran into situation where if I am using dotnet dev cert. I have to add manually the following to the apps secret:
"Kestrel:Certificates:Development:Password": "43f5d164-3813-4dba-8e34-6337096f2df0"
The id must match the project secret entry:
<UserSecretsId>8c5d84b3-27c0-4964-a32b-91dd9b1c01d3</UserSecretsId>
@shanselman With regard to the original issue, yes it works out-of-the-box without any additional config when using Visual Studio but is fragile due to the issue i raised earlier in this thread. Users of VSCode who look to set this up manually may run into issues.
I have tested the out-of-the-box experience on both Windows and MacOS:
Windows Using Visual Studio Community 2017 v15.9.3 with dotnet v2.2 SDK and "Docker For Windows" (Docker Desktop Community Version 2.0.0.0-win81 (29211)) File > New > Project > Visual C# > .Net Core > ASP.NET Core Web Application and then check "Docker support"
Run the app in Debug (F5) works without error after allowing the certificate trust prompts (look out for it in the background) , the app then runs from the local docker container on HTTPS using self signed localhost certificate.
The certificate is volume mapped into the container from the local filesystem, here is how Visual Studio builds and runs the container (for the benefit of VSCode users):
docker build -f "C:\Users\username\source\repos\DotNetCoreMvcTest\DotNetCoreMvcTest\Dockerfile" -t dotnetcoremvctest:dev --target base --label "com.microsoft.created-by=visual-studio" "C:\Users\username\source\repos\DotNetCoreMvcTest"
docker run -dt -v "C:\Users\username\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Users\username\source\repos\DotNetCoreMvcTest\DotNetCoreMvcTest:/app" -v "C:\Users\username\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\username\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\username\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "ASPNETCORE_URLS=https://+:443;http://+:80" -e "ASPNETCORE_HTTPS_PORT=44348" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 50286:80 -p 44348:443 --entrypoint tail dotnetcoremvctest:dev -f /dev/null
Notice the 2 volume mappings:
"C:\Users\username\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro"
"C:\Users\username\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro"
The self signed Certificate generated for the project is here: "C:\Users\username\AppData\Roaming\ASP.NET\Https\DotNetCoreMvcTest.pfx" and is made available in the container at "/root/.aspnet/https/DotNetCoreMvcTest.pfx"
The UserSecrets JSON file is here: "C:\Users\username\AppData\Roaming\Microsoft\UserSecrets{guid}\secrets.json" and is made available in the container at "/root/.microsoft/usersecrets/{guid}/secrets.json"
Contents of secrets.json (the certificate password is a generated guid, unrelated to the UserSecretsId)
{
"Kestrel:Certificates:Development:Password": "{guid}"
}
The cert path is not required as dotnet looks for "/root/.aspnet/https/{AppName}.pfx"
Ok, now in reference to the issues that i raised: https://github.com/aspnet/Docs/issues/6199#issuecomment-418194220 let's break it:
Open up command prompt inside the project directory and run the command: dotnet user-secrets set "Kestrel:Certificates:Development:Path" "/root/.aspnet/https/DotNetCoreMvcTest.pfx"
contents of secrets.json are now this:
{
"Kestrel:Certificates:Development:Password": "22c3f8ae-4857-4ab7-a8e6-570ed636b85f",
"Kestrel:Certificates:Development:Path": "/root/.aspnet/https/DotNetCoreMvcTest.pfx"
}
Now run the app and Boom!
System.InvalidOperationException: 'Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found.
This should not happen as the path is actually correct, the excuse that a path is not meant to be used in development is lame, the option is there so it should work, a developer may wish to use a different dev cert, rename the cert or move the cert.
It's easy to fix in the source, i even went to the trouble of finding out the cause: https://github.com/aspnet/Docs/issues/6199#issuecomment-427834083
Setting this up does need to be documented, but it should be clearly stated in the docs that a path can't be used with "Kestrel:Certificates:Development" as the dev cert handling in KestrelConfigurationLoader is broken.
MacOS Visual Studio for Mac Community v7.7.1 (build 15) with dotnet v2.2 SDK and "Docker For Mac" (Docker Desktop Community Version 2.0.0.0-mac81 (29211)) File > New Solution > .NET Core > App > ASP.NET Core Web App (MVC) The new project workflow is a little different and there is no option for docker support upon project creation in Visual Studio for Mac, it's just a mildly updated rebrand of Xamarin Studio.
Once the project is created, right click on the project > Add > Add Docker Support This creates a Docker file and a docker-compose file whereas VS on Windows only created a Dockerfile when Docker support was selected. (to get docker compose support on Windows, right click on project > Add > "Container Orchestrator support" and select docker compose.)
Running the app reveals that it does not work, failing with the following:
Error: Creating network "dockercompose........_default" with the default driver The path /usr/local/share/dotnet/sdk/NuGetFallbackFolder\r\nis not shared from OS X and is not known to Docker
To fix that, add this path to the Docker File sharing settings "/usr/local/share/dotnet/sdk/NuGetFallbackFolder" This requirement is not documented anywhere as far as i know.
The app can be broken in the same way by attempting to specify the dev cert path.
Summary The easiest way to get working Docker / Docker Compose support when using VSCode is to create the project using Visual Studio / Visual Studio for Mac.
For anyone who wishes to know how VS run the project when docker compose is used (Container Orchestrator support) , here are the build and run commands extracted from VS on Windows.
BTW. make sure that you run 'docker stop' against the container that was generated before adding "Container Orchestrator support" otherwise the build will fail with:
Bind for 0.0.0.0:{port} failed: port is already allocated
Build:
docker-compose -f "C:\Users\username\source\repos\DotNetCoreMvcTest\docker-compose.yml" -f "C:\Users\username\source\repos\DotNetCoreMvcTest\docker-compose.override.yml" -f "C:\Users\username\source\repos\DotNetCoreMvcTest\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose9785789912942731059 --no-ansi build
Run:
docker-compose -f "C:\Users\username\source\repos\DotNetCoreMvcTest\docker-compose.yml" -f "C:\Users\username\source\repos\DotNetCoreMvcTest\docker-compose.override.yml" -f "C:\Users\username\source\repos\DotNetCoreMvcTest\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose9785789912942731059 --no-ansi up -d --no-build --force-recreate --remove-orphans
As can be seen there is a "docker-compose.override.yml" file referenced, which can be created if required, and also a secret docker-compose file "docker-compose.vs.debug.g.yml" that is added to the mix by VS, this file is transient so i can't easily grab the contents but i can get the resulting docker compose config that is generated as its included in the docker build output:
services:
dotnetcoremvctest:
build:
context: C:\Users\chris.claxton\source\repos\DotNetCoreMvcTest
dockerfile: DotNetCoreMvcTest/Dockerfile
target: base
entrypoint: tail -f /dev/null
environment:
ASPNETCORE_ENVIRONMENT: Development
ASPNETCORE_HTTPS_PORT: '44348'
ASPNETCORE_URLS: https://+:443;http://+:80
DOTNET_USE_POLLING_FILE_WATCHER: '1'
NUGET_FALLBACK_PACKAGES: /root/.nuget/fallbackpackages
image: dotnetcoremvctest:dev
labels:
com.microsoft.visualstudio.debuggee.arguments: ' --additionalProbingPath /root/.nuget/packages
--additionalProbingPath /root/.nuget/fallbackpackages bin/Debug/netcoreapp2.2/DotNetCoreMvcTest.dll'
com.microsoft.visualstudio.debuggee.killprogram: /bin/bash -c "if PID=$$(pidof
-x dotnet); then kill $$PID; fi"
com.microsoft.visualstudio.debuggee.program: dotnet
com.microsoft.visualstudio.debuggee.workingdirectory: /app
ports:
- published: 50286
target: 80
- published: 44348
target: 443
volumes:
- C:\Users\username\source\repos\DotNetCoreMvcTest\DotNetCoreMvcTest:/app:rw
- C:\Users\username\vsdbg\vs2017u5:/remote_debugger:ro
- C:\Users\username\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro
- C:\Users\username\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro
- C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages:ro
- C:\Users\username\.nuget\packages:/root/.nuget/packages:ro
The contents of the secret file are the above minus what is already in the docker-compose file:
version: '3.4'
services:
dotnetcoreapitest:
image: ${DOCKER_REGISTRY-}dotnetcoreapitest
build:
context: .
dockerfile: DotNetCoreApiTest/Dockerfile
@chrisckc and @shanselman,
Unfortunately, I am running into situations where existing projects when reopen in Visual Studio.NET don't re-create secret.json
file and also don't generate the Dev SSL Certificate. I made a Powershell script to fix that issue can be found here
This script also enables SSL certificates for VSCode on Windows dev boxes.
This issue needs to be a official MS Document and a standardised approach for Windows and Linux Containers, using a variety of methods to orchestrate your containers (Like docker-compose).
So, dot it from scratch, DockerDotNET.sample, project created, compiled, cert created. Great. Run visual studio docker-compose, Wau -its worked via https: even browsed. Next time its broken, you can add it to trash. this is after generating user secrets with correct password which was added to appettings.Development.json also added to docker-compose.override.yml
And its sometimes accept password, next time already not. And even everything this Kestrel passes -> its not browsable. Its not working on local machine. I'm already not talking about deployment on remote machine. Its absolutely headache. Anybody have some stable solution that with corresponding steps, code samples, advice? This config hell. I can't load my UI, to just develop application, add features, services. I'm just trying some configs, waiting minutes & hours to get t load to see that it not worked.
So. I have 4 web app that should work under https on different port under linux docker containers, main tool is visual studio, latest version, netcore latest version. Will be appreciated for help. I'm in despair...
@inpicksys, I ran into this situation all the time try using my script to reset password and cert, https://github.com/kdcllc/win10andwsldev/blob/master/vsnet-docker-ssl-issue.md
@kdcllc : Your script has saved me a shed load of time, good work there mate.
@kdcllc Thanks. I'm used your scripts. But docker containers are not started under linux. Kestrel not started. Googling doesn't help. So, as this is only for dev purposes, only way is just refuse Kestrel with https, as its take too much time to handle this. Will dig in nginx/apache proxy with https as its for production. Production & dev will match. Hope to get it worked with nginx. Thanks.
@inpicksys I notice you have "KestrelCertificatesDevelopment__Path specified which is the same as setting "Kestrel:Certificates:Development:Path" in my example.
This breaks the config due to a bug as i have described here: https://github.com/aspnet/Docs/issues/6199#issuecomment-418194220
So your issue may likely to be a result of the certificate not being found, the error is probably misleading as usual.
The only way to get it work work using the development config is to have the cert in the default path with the default name (same as the project name). If you look at the source code you will see why its broken, i described the problem here: https://github.com/aspnet/Docs/issues/6199#issuecomment-427834083
You can use "Kestrel:Certificates:Default:Path" just fine, but you have to name your "ASPNETCORE_ENVIRONMENT" env var something other than "Development"
@bilalMlaik this documentatiom might be helpful Hosting ASP.NET Core Images with Docker over HTTPS.
I ran into situation where if I am using dotnet dev cert. I have to add manually the following to the apps secret:
"Kestrel:Certificates:Development:Password": "43f5d164-3813-4dba-8e34-6337096f2df0"
The id must match the project secret entry:
<UserSecretsId>8c5d84b3-27c0-4964-a32b-91dd9b1c01d3</UserSecretsId>
@kdcllc , I did all these things but nothing achieved. I am still facing same error in case of HTTPS.
related #3310 Javier is contact: This needs to go in Enforce HTTPS in an ASP.NET Core The first time you run dotnet after installing the SDK you get this message Successfully installed the ASP.NET Core HTTPS Development Certificate. To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only). For establishing trust on other platforms please refer to the platform specific documentation. For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
Copied from #3310 We also need to cover how to setup the dev certificate when using Docker in development:
Create an application on Visual Studio using the MVC template.
Run the app to ensure its working.
Add docker support for the application through the tooling.
Modify the dockerfile to expose the port 443 with EXPOSE 443
Modify the docker-compose override file to map ports, volumes and environement variables as follows (this will all be unnecessary after docker tooling has support for HTTPS):
Export the HTTPS certificate into a PFX file using the dev-certs global tool to %APPDATA%/ASP.NET/Https/<>.pfx using a password of your choice (recommended password new-guid on powershell)
On your project, open user secrets and add the following configuration keys:
Run your application within the container.
Navigate to the HTTP endpoint on your application