microsoft / navcontainerhelper

Official Microsoft repository for BcContainerHelper, a PowerShell module, which makes it easier to work with Business Central Containers on Docker.
MIT License
379 stars 243 forks source link

Second container on same external database causes previous containers to stop working #1729

Closed ti-jalopez closed 3 years ago

ti-jalopez commented 3 years ago

PLEASE DO NOT INCLUDE ANY PASSWORDS OR TOKENS IN YOUR ISSUE!!!

Describe the issue I've created two container conecting to the same external database. First, I've created the first container and it run fine. Second, I've created the second container equal than the first and it run fine. I've restarted the first containers and the nav service don't start. Get-EventLog show

MessageWithoutPrivateInformation: The NAV application could not be mounted for database 'APBA_MIG_BC17' on database   
                     server '198.168.1.33' due to the following error: La instancia de Microsoft Dynamics 365 Business Central Server no    
                     se puede conectar a la base de datos de la aplicación porque está usando una clave de cifrado de contraseña distinta  
                     a la usada actualmente en la base de datos.

I know in this case I would create the second container using de same key file for password encription. The problem is: when I create a second container conecting to any external database that has a encription key, the system don't show a warning or error to warn everyone after that old containers will stop working

Could you include a new param on New-BcContainerHelper for stop or show a warining in this case? And create a new param to include directly de "keyfile" from the first container to make more easy create the container that use -myScrtips and -encryptionPassword as it has been described in https://github.com/microsoft/navcontainerhelper/issues/1461 ?

Scripts used to create container and cause the issue First container: CONTAINER0

#Contenedor CONTAINER0: 
#Crear contenedor con credenciales NAVUser y TestToolKit, copiar Add-Ins (dll's)
#Conecta con BD SQL Server existente: CRONUSBC17ES
#Crea nuevo usuario NAV/BC, usando tanto para el contenedor creado, como para acceso a BC. Si ya existe la cuenta en BD dará un error que hay que ignorar. Usuario: NAV

#PARAMETROS CONTENEDOR
$containerName = 'CONTAINER0'
$licenseFile = "c:\licencia\BC.flf"
$artifactUrl = Get-BCArtifactUrl -type OnPrem -version 17.1 -country es -select Latest
$publicDnsName = 'MY.DNS.NET'

$databaseServer = "198.168.1.33"
$databaseInstance = "SQL2017"
$databaseName = "CRONUSBC17ES"
$databaseCredential = Get-Credential -Message "Credenciales conexión BD Sql Server (no es credencial Windows)"
$navCredential = Get-Credential -Message "Credenciales usuario BC usado para conectar (no es credencial Windows)"

$additionalparamaters = @()
$addinsFolder = "C:\AddIns"
if ($addinsFolder) {
  $additionalparamaters += "--volume $($addinsFolder):c:\run\add-ins" 
}

Write "ArtifactURL=$artifactUrl"
get-date
#Comando completo para desarrolladores: con -includeAL y -TestToolKit.
New-BCContainer -accept_eula -accept_outdated `
-containerName $containerName `
-Auth UserPassword `
-Credential $navCredential `
-artifactUrl $artifactUrl `
-licenseFile $licenseFile `
-shortcuts CommonStartMenu `
-databaseServer $databaseServer `
-databaseInstance $databaseInstance `
-databaseName $databaseName `
-databaseCredential $databaseCredential `
-doNotExportObjectsToText `
-useTraefik `
-PublicDnsName $publicDnsName `
-useSSL `
-installCertificateOnHost `
-includeAL `
-includeTestToolkit `
-updateHost `
-additionalParameters $additionalparamaters `
-Verbose

Second container: CONTAINER1. Equal than CONTAINER0, only change container name.

Full output of scripts Created CONTAINER0 ok:

BcContainerHelper is version 2.0.5
BcContainerHelper is running as administrator
Host is Microsoft Windows Server 2019 Standard - ltsc2019
Docker Client Version is 19.03.13
Docker Server Version is 19.03.13
Fetching all docker images
Enabling SSL as otherwise all clients will see mixed HTTP / HTTPS request, which will cause problems e.g. on the mobile and modern windows clients
Using image mcr.microsoft.com/businesscentral:10.0.17763.1637
PublicDnsName is MY.DNS.NET
Creating Container CONTAINER0
Version: 17.1.18256.18792-es
Style: onprem
Multitenant: No
Platform: 17.0.18204.18738
Generic Tag: 1.0.1.3
Container OS Version: 10.0.17763.1637 (ltsc2019)
Host OS Version: 10.0.17763.1637 (ltsc2019)
Using process isolation
Using locale es-ES
Adding special CheckHealth.ps1 to enable Traefik support
Disabling the standard eventlog dump to container log every 2 seconds (use -dumpEventLog to enable)
Using license file c:\licencia\BC.flf
Additional Parameters:
--volume C:\AddIns:c:\run\add-ins
--hostname containers
-e webserverinstance=CONTAINER0
-e publicdnsname=MY.DNS.NET
-l "traefik.protocol=https"
-l "traefik.web.frontend.rule=PathPrefix:/CONTAINER0"
-l "traefik.web.port=443"
-l "traefik.soap.frontend.rule=PathPrefix:/CONTAINER0soap;ReplacePathRegex: ^/CONTAINER0soap(.*) /BC$1"
-l "traefik.soap.port=7047"
-l "traefik.rest.frontend.rule=PathPrefix:/CONTAINER0rest;ReplacePathRegex: ^/CONTAINER0rest(.*) /BC$1"
-l "traefik.rest.port=7048"
-l "traefik.dev.frontend.rule=PathPrefix:/CONTAINER0dev;ReplacePathRegex: ^/CONTAINER0dev(.*) /BC$1"
-l "traefik.dev.port=7049"
-l "traefik.dl.frontend.rule=PathPrefixStrip:/CONTAINER0dl"
-l "traefik.dl.port=8080"
-l "traefik.dl.protocol=http"
-l "traefik.enable=true"
-l "traefik.frontend.entryPoints=https"
--env customNavSettings=PublicODataBaseUrl=https://MY.DNS.NET/CONTAINER0rest/odata,PublicSOAPBaseUrl=https://MY.DNS.NET/CONTAINER0soap/ws,PublicWe
bBaseUrl=https://MY.DNS.NET/CONTAINER0
Files in C:\ProgramData\BcContainerHelper\Extensions\CONTAINER0\my:
- AdditionalOutput.ps1
- CheckHealth.ps1
- license.flf
- MainLoop.ps1
- SetupVariables.ps1
- updatehosts.ps1
Creating container CONTAINER0 from image mcr.microsoft.com/businesscentral:10.0.17763.1637
d2063d5f1cbe51c028b1d279576d79557d1a4ddf459b849b7a3c9a06f4a3da8a
Waiting for container CONTAINER0 to be ready
Using artifactUrl https://bcartifacts.azureedge.net/onprem/17.1.18256.18792/es
Using installer from C:\Run\150-new
Installing Business Central
Installing from artifacts
Starting Local SQL Server
Starting Internet Information Server
Copying Service Tier Files
Copying PowerShell Scripts
Copying dependencies
Copying ReportBuilder
Importing PowerShell Modules
Skipping restore of Cronus database
Modifying Business Central Service Tier Config File for Docker
Creating Business Central Service Tier
Installing SIP crypto provider: 'C:\Windows\System32\NavSip.dll'
Copying Web Client Files
Copying Client Files
Copying ModernDev Files
Copying additional files
Copying ConfigurationPackages
Copying Test Assemblies
Copying Applications
Installation took 29 seconds
Installation complete
Initializing...
Setting host.containerhelper.internal to 172.23.144.1 in container hosts file
Starting Container
Hostname is containers
PublicDnsName is MY.DNS.NET
Using NavUserPassword Authentication
Import Encryption Key
Stopping local SQL Server
Creating Self Signed Certificate
Self Signed Certificate Thumbprint 3664A65CFF6FED4FF2CCB8F7B464C2665CFA08A4
Modifying Service Tier Config File with Instance Specific Settings
Modifying Service Tier Config File with settings from environment variable
Setting PublicODataBaseUrl to https://MY.DNS.NET/CONTAINER0rest/odata
Setting PublicSOAPBaseUrl to https://MY.DNS.NET/CONTAINER0soap/ws
Setting PublicWebBaseUrl to https://MY.DNS.NET/CONTAINER0
Starting Service Tier
Registering event sources
Creating DotNetCore Web Server Instance
Using license file 'c:\run\my\license.flf'
Import License
Creating http download site
Container IP Address: 172.23.159.113
Container Hostname  : containers
Container Dns Name  : MY.DNS.NET
Web Client          : https://MY.DNS.NET/CONTAINER0/
Dev. Server         : https://MY.DNS.NET
Dev. ServerInstance : BC
Setting containers to 172.23.159.113 in host hosts file

Files:
http://MY.DNS.NET:8080/ALLanguage.vsix
http://MY.DNS.NET:8080/certificate.cer

WARNING: You are running a container which is 73 days old.
Microsoft recommends that you always run the latest version of our containers.

Container Total Physical Memory is 32.0Gb
Container Free Physical Memory is 7.5Gb

Initialization took 360 seconds
Ready for connections!
Reading CustomSettings.config from CONTAINER0
Certificate with thumbprint 3664A65CFF6FED4FF2CCB8F7B464C2665CFA08A4 imported successfully
Creating Desktop Shortcuts for CONTAINER0
Skipping app 'C:\Applications\TestFramework\TestRunner\Microsoft_Test Runner.app' as it is already installed
Skipping app 'C:\Applications\TestFramework\TestLibraries\Any\Microsoft_Any.app' as it is already installed
Skipping app 'C:\Applications\TestFramework\TestLibraries\Assert\Microsoft_Library Assert.app' as it is already installed
Skipping app 'C:\Applications\TestFramework\TestLibraries\Variable Storage\Microsoft_Library Variable Storage.app' as it is already installed
Skipping app 'C:\Applications\System Application\Test\Microsoft_System Application Test Library.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-TestLibraries.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Bank.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Cash Flow.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Cost Accounting.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-CRM integration.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Data Exchange.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Dimension.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-ERM.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Fixed Asset.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-General Journal.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Graph.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Integration.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Invoicing.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Job.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Local.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Misc.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Monitor Sensitive Fields.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Permissions.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Physical Inventory.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Prepayment.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Rapid Start.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Report.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Resource.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Reverse.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-SCM.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-SMB.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-SMTP.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Upgrade.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-User.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-VAT.app' as it is already installed
Skipping app 'C:\Applications\BaseApp\Test\Microsoft_Tests-Workflow.app' as it is already installed
TestToolkit successfully imported
Creating .net Assembly Reference Folder for VS Code
Copying DLLs from C:\Windows\assembly to assemblyProbingPath
Copying DLLs from C:\Windows\Microsoft.NET\assembly to assemblyProbingPath
Copying DLLs from C:\Program Files\Microsoft Dynamics NAV\170\Service to assemblyProbingPath
Copying DLLs from C:\Program Files (x86)\Microsoft Dynamics NAV\170\RoleTailored Client to assemblyProbingPath
Copying DLLs from C:\Test Assemblies\Mock Assemblies to assemblyProbingPath
Copying DLLs from C:\Program Files (x86)\Open XML SDK to assemblyProbingPath
Container CONTAINER0 successfully created
Because of Traefik, the following URLs need to be used when accessing the container from outside your Docker host:
Web Client:        https://MY.DNS.NET/CONTAINER0?page=
SOAP WebServices:  https://MY.DNS.NET/CONTAINER0soap
OData WebServices: https://MY.DNS.NET/CONTAINER0rest
Dev Service:       https://MY.DNS.NET/CONTAINER0dev
File downloads:    https://MY.DNS.NET/CONTAINER0dl

Use:
Get-BcContainerEventLog -containerName CONTAINER0 to retrieve a snapshot of the event log from the container
Get-BcContainerDebugInfo -containerName CONTAINER0 to get debug information about the container
Enter-BcContainer -containerName CONTAINER0 to open a PowerShell prompt inside the container
Remove-BcContainer -containerName CONTAINER0 to remove the container again...

Created CONTAINER1 ok: same output.

Restart CONTAINER0, error starting NAV service:

Initialization took 13 seconds
Ready for connections!
Initializing...
Setting host.containerhelper.internal to 172.23.144.1 in container hosts file
Restarting Container
PublicDnsName unchanged
Hostname is containers
PublicDnsName is MY.DNS.NET
Using NavUserPassword Authentication
Starting Internet Information Server
Starting Service Tier
Failed to start service 'Dynamics 365 Business Central Server [BC] (MicrosoftDynamicsNavServer$BC)'.
at <ScriptBlock>, C:\Run\navstart.ps1: line 155
at <ScriptBlock>, C:\Run\start.ps1: line 359
at <ScriptBlock>, <No file>: line 1

Run inside the CONTAINER0: Get-EventLog -LogName Application -Source 'MicrosoftDynamicsNavServer$BC' -EntryType Error -Newest 2 | Select-Object -Property *

MessageWithoutPrivateInformation: The NAV application could not be mounted for database 'APBA_MIG_BC17' on database   
                     server '198.168.1.33' due to the following error: La instancia de Microsoft Dynamics 365 Business Central Server no    
                     se puede conectar a la base de datos de la aplicación porque está usando una clave de cifrado de contraseña distinta  
                     a la usada actualmente en la base de datos.

Screenshots If applicable, add screenshots to help explain your problem.

Additional context

freddydk commented 3 years ago

When you create container number 2 on the same database, you need to use the same dynamicsnav.key as the first container. https://github.com/microsoft/navcontainerhelper/issues/1518

ti-jalopez commented 3 years ago

Hi @freddydk, I know if I add the keyFile in the param -myScripts $myScripts, I solve that.

I suggest an improvement in New-BcContainerHelper to reduce much spended time when you create more than one containers with different key files using the same database.

If you add a error or warning when the container is created and the target database has a different key file, after create the container we'll know that there is a problem, and we can decide to use old key file or overwrite with a new one.

Maybe, adding a new param -CheckDifferentKeyFile to determine to show a warning, error o force create new key.

freddydk commented 3 years ago

I don't know if I can determine that they is different, I will have a look

ti-jalopez commented 3 years ago

Maybe this can help you a bit:

image image

Best regards,

freddydk commented 3 years ago

The issue is really that the key in the database only contains the public part of the key. The key on disk (inside container 1) also contains the private part. In order to create a second container using the same database, you need the private part - you need the key created by the first container (either in the keys folder or DynamicsNAV.key)

I decided to fix this in containerhelper and create a new setting called useSharedEncryptionKeys, which default is true. If you useSharedEncryptionKeys, then the DynamicsNAV.key file created by containers will be stored in a durable folder (not under the container) and reused in subsequent attempts to access the same database server.

You can set useSharedEncryptionKeys to false in the settings file if you do not want this behavior. If you share your own DynamicsNAV.key to the my folder, then that will be used instead of the cached one.

ti-jalopez commented 3 years ago

Great, This solves the problem to create more than a container in the same host and database. And also allow to copy de shared key to other host, if you need to create more containers in other hosts.

Temporary, while the fix will be to release in a new version, I've create my own script to pass the key file to a new container, renaming the file if it is different from Dynamics.key. I attach an example:

$keyFile = (Join-Path $bcContainerHelperConfig.hostHelperFolder "myKeys\myDatabase_myUser_DynamicsNAV.key"
$myScripts = @()
if ($keyFile) {
  #Key file will be the name DynamicsNAV.key
  $requiredFileName = 'DynamicsNAV.key'
  $currKeyFileName = [System.IO.Path]::GetFileName($keyFile)
  if ($currKeyFileName  -eq $requiredFileName) {
    $myScripts += $keyFile
  } else {
    $tempPath = [System.IO.Path]::GetTempPath()
    $newKeyFile = (Join-Path $tempPath $requiredFileName)
    Write-Host "KeyFileName debe $requiredFileName. Creado temporal $newKeyFile"
    Copy-Item $keyFile $newKeyFile -Force
    $myScripts += $newKeyFile
  }
}

Finally, in New-BcContainer add param -myScripts $myScripts.

Mybe, add a new param EncryptionKeyFile to add directly de file (renaming if required), it would be an additional improvement to consider.

In any case bccontainerhelper is a very good product, managed by a great professional, CONGRATULATIONS 🥇

freddydk commented 3 years ago

Thanks

My solution is similar https://github.com/microsoft/navcontainerhelper/commit/c18a4434032d8526833cde6f07755a2dda4bbe5e Although I am storing the key file under an MDA256 hash of the database password and using that key for all encryptions using the same password. With this solution you should never need to specify encryptionKeyFile yourself - only if you run things on a different host. If you do - you could synchronize the encryptionKey folder to the other host.

freddydk commented 3 years ago

Shipped in 2.0.6-preview355

freddydk commented 3 years ago

Shipped in BcContainerHelper 2.0.6 https://freddysblog.com/2021/02/28/running-business-central-in-docker-using-sql-on-the-host/