I've the following problem (not sure if that's the right pattern to implement, but that's how far I got with this topic):
Situation:
- I've a ASP.NET core 2.1 solution with multiple projects (some class libraries and two services)
- I want to generate two linux based docker containers: one for each service
- I want two of them to use the same (self-signed) SSL certificates (not sure if this is the good pattern, but they will live on the same domain, and both will communicate with users)
- I plan to generate a base docker image with docker compose, then reuse this base in both service dockers
- I've a solution which is working only with one docker container, but both containers at the same time does not seem to work
Questions:
- How could I reuse the docker image in my Dockerfile if it was created by another Dockerfile and define this behavior in the docker-compose.yml file? How could I create the docker image with certificate to serve as a base for both service docker images?
- Is the above imagined pattern even right/correct/well designed? Are there possible improvements in the design?
This is working for one container:
docker-compose.yml:
version: '3.4'
services:
afr_webapi:
image: afr/webapi:latest
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+:443;http://+:80
ports:
- "53538:80"
- "44376:443"
build:
context: .
dockerfile: AFRWebAPI/Dockerfile
#afr_webui: ... (an another service)
AFRWebAPI/Dockerfile:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
# Set password for the certificate as 1234
ENV certPassword 1234
# Use opnssl to generate a self signed certificate cert.pfx with password $env:certPassword
RUN openssl genrsa -des3 -passout pass:${certPassword} -out server.key 2048 \
&& openssl rsa -passin pass:${certPassword} -in server.key -out server.key \
&& openssl req -sha256 -new -key server.key -out server.csr -subj '/CN=localhost' \
&& openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt \
&& openssl pkcs12 -export -out cert.pfx -inkey server.key -in server.crt -certfile server.crt -passout pass:${certPassword}
# Expose port 443 for the application.
EXPOSE 443
EXPOSE 53538
EXPOSE 44376
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY . .
WORKDIR "/src"
RUN dir \
&& dotnet build "AFRCore/AFRCore.csproj" -c Release -o /app \
&& dotnet build "AFRCoreWorkflow/AFRCoreWorkflow.csproj" -c Release -o /app \
&& dotnet build "DBFHandler/DBFHandler.csproj" -c Release -o /app \
&& dotnet build "AFRInfrastructure/AFRInfrastructure.csproj" -c Release -o /app \
&& dotnet build "AFRWebAPI/AFRWebAPI.csproj" -c Release -o /app
FROM build AS publish
WORKDIR "/src/AFRWebAPI"
RUN dotnet publish "AFRWebAPI.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "AFRWebAPI.dll"]
Result:
afr_webapi_1 | Hosting environment: Development
afr_webapi_1 | Content root path: /app
afr_webapi_1 | Now listening on: https://0.0.0.0:443
afr_webapi_1 | Application started. Press Ctrl+C to shut down.
The above pattern is not working when I want move out the certification generation part to an other docker image for later reuse by two service containers:
(Please note that in this example only one service container is enough to illustrate the problem)
docker-compose.yml:
version: '3.4'
services:
afr_certbase:
image: afr/certbase:latest # <------ This image I want to reuse ...
build:
context: .
dockerfile: Certificate/Dockerfile
afr_webapi: # <------ ... during composing this image
image: afr/webapi:latest
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+:443;http://+:80
ports:
- "53538:80"
- "44376:443"
build:
context: .
dockerfile: AFRWebAPI/Dockerfile
depends_on:
- afr_certbase
#afr_webui: ... (an another service)
Certificate/Dockerfile:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS cert_base # <------- This should be an image used by the two services
# Set password for the certificate as 1234
ENV certPassword 1234
# Use opnssl to generate a self signed certificate cert.pfx with password $env:certPassword
RUN openssl genrsa -des3 -passout pass:${certPassword} -out server.key 2048 \
&& openssl rsa -passin pass:${certPassword} -in server.key -out server.key \
&& openssl req -sha256 -new -key server.key -out server.csr -subj '/CN=localhost' \
&& openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt \
&& openssl pkcs12 -export -out cert.pfx -inkey server.key -in server.crt -certfile server.crt -passout pass:${certPassword}
# Expose port 443 for the application.
EXPOSE 443
AFRWebAPI/Dockerfile:
FROM afr/certbase:latest AS base # <------- This is were I want to reuse the afr/certbase
WORKDIR /app
EXPOSE 53538
EXPOSE 44376
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY . .
WORKDIR "/src"
RUN dir \
&& dotnet build "AFRCore/AFRCore.csproj" -c Release -o /app \
&& dotnet build "AFRCoreWorkflow/AFRCoreWorkflow.csproj" -c Release -o /app \
&& dotnet build "DBFHandler/DBFHandler.csproj" -c Release -o /app \
&& dotnet build "AFRInfrastructure/AFRInfrastructure.csproj" -c Release -o /app \
&& dotnet build "AFRWebAPI/AFRWebAPI.csproj" -c Release -o /app
FROM build AS publish
WORKDIR "/src/AFRWebAPI"
RUN dir
RUN dotnet publish "AFRWebAPI.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "AFRWebAPI.dll"]
Result:
afr_webapi_1 | Application startup exception: Interop+Crypto+OpenSslCryptographicException: error:2006D080:BIO routines:BIO_new_file:no such file
afr_webapi_1 | at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
afr_webapi_1 | at Internal.Cryptography.Pal.CertificatePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, String fileName, String password)
afr_webapi_1 | at AFRWebAPI.Program.<>c__DisplayClass2_0.<BuildWebHost>b__1(ListenOptions listenOptions) in /src/AFRWebAPI/Program.cs:line 30
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPEndPoint endPoint, Action`1 configure)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPAddress address, Int32 port, Action`1 configure)
afr_webapi_1 | at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.<Get>b__0()
afr_webapi_1 | at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
afr_webapi_1 | at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
afr_webapi_1 | at System.Lazy`1.CreateValue()
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.Get(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.get_Value()
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.CreateServiceContext(IOptions`1 options, ILoggerFactory loggerFactory)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer..ctor(IOptions`1 options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
afr_webapi_1 | --- End of stack trace from previous location where exception was thrown ---
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureServer()
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
afr_webapi_1 | crit: Microsoft.AspNetCore.Hosting.Internal.WebHost[6]
afr_webapi_1 | Application startup exception
afr_webapi_1 | Interop+Crypto+OpenSslCryptographicException: error:2006D080:BIO routines:BIO_new_file:no such file
afr_webapi_1 | at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
afr_webapi_1 | at Internal.Cryptography.Pal.CertificatePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, String fileName, String password)
afr_webapi_1 | at AFRWebAPI.Program.<>c__DisplayClass2_0.<BuildWebHost>b__1(ListenOptions listenOptions) in /src/AFRWebAPI/Program.cs:line 30
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPEndPoint endPoint, Action`1 configure)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPAddress address, Int32 port, Action`1 configure)
afr_webapi_1 | at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.<Get>b__0()
afr_webapi_1 | at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
afr_webapi_1 | at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
afr_webapi_1 | at System.Lazy`1.CreateValue()
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.Get(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.get_Value()
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.CreateServiceContext(IOptions`1 options, ILoggerFactory loggerFactory)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer..ctor(IOptions`1 options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
afr_webapi_1 | --- End of stack trace from previous location where exception was thrown ---
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureServer()
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
afr_webapi_1 |
afr_webapi_1 | Unhandled Exception: Interop+Crypto+OpenSslCryptographicException: error:2006D080:BIO routines:BIO_new_file:no such file
afr_webapi_1 | at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle)
afr_webapi_1 | at Internal.Cryptography.Pal.CertificatePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
afr_webapi_1 | at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, String fileName, String password)
afr_webapi_1 | at AFRWebAPI.Program.<>c__DisplayClass2_0.<BuildWebHost>b__1(ListenOptions listenOptions) in /src/AFRWebAPI/Program.cs:line 30
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPEndPoint endPoint, Action`1 configure)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.Listen(IPAddress address, Int32 port, Action`1 configure)
afr_webapi_1 | at Microsoft.Extensions.Options.ConfigureNamedOptions`1.Configure(String name, TOptions options)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.<Get>b__0()
afr_webapi_1 | at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
afr_webapi_1 | at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
afr_webapi_1 | at System.Lazy`1.CreateValue()
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.Get(String name)
afr_webapi_1 | at Microsoft.Extensions.Options.OptionsManager`1.get_Value()
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.CreateServiceContext(IOptions`1 options, ILoggerFactory loggerFactory)
afr_webapi_1 | at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer..ctor(IOptions`1 options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
afr_webapi_1 | --- End of stack trace from previous location where exception was thrown ---
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
afr_webapi_1 | at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureServer()
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.Internal.WebHost.StartAsync(CancellationToken cancellationToken)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String shutdownMessage)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token)
afr_webapi_1 | at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
afr_webapi_1 | at AFRWebAPI.Program.Main(String[] args) in /src/AFRWebAPI/Program.cs:line 14
aruforgalmirendszer_afr_webapi_1 exited with code 139