From a97f6f3e49dea911bb79ee348df42ff77f15db7d Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 15 Jul 2025 10:52:18 +0200 Subject: [PATCH 01/50] Upstream patches --- build.ps1 | 5 ++++- build.sh | 3 ++- generateKeys.ps1 | 3 +++ generateKeys.sh | 1 + licenseGen.ps1 | 3 +++ licenseGen.sh | 1 + src/bitBetter/Program.cs | 2 +- src/licenseGen/Dockerfile | 2 +- src/licenseGen/Program.cs | 8 +++++--- 9 files changed, 21 insertions(+), 7 deletions(-) diff --git a/build.ps1 b/build.ps1 index 54f6f6b..f3d79fc 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,6 @@ +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + # define temporary directory $tempdirectory = "$pwd\temp" # define services to patch @@ -30,7 +33,7 @@ Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter" Copy-Item "$pwd\.keys\cert.pfx" -Destination "$pwd\src\licenseGen" # build bitBetter and clean the source directory after -docker build -t bitbetter/bitbetter "$pwd\src\bitBetter" +docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter" Remove-Item "$pwd\src\bitBetter\cert.cert" -Force # gather all running instances diff --git a/build.sh b/build.sh index 97b544d..468e22c 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # define temporary directory TEMPDIRECTORY="$PWD/temp" @@ -33,7 +34,7 @@ cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" # build bitBetter and clean the source directory after -docker build -t bitbetter/bitbetter "$PWD/src/bitBetter" +docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" rm -f "$PWD/src/bitBetter/cert.cert" # gather all running instances diff --git a/generateKeys.ps1 b/generateKeys.ps1 index 690143f..d20aa14 100644 --- a/generateKeys.ps1 +++ b/generateKeys.ps1 @@ -1,3 +1,6 @@ +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + # get the basic openssl binary path $opensslbinary = "$Env:Programfiles\OpenSSL-Win64\bin\openssl.exe" diff --git a/generateKeys.sh b/generateKeys.sh index 4850fc2..d561aee 100755 --- a/generateKeys.sh +++ b/generateKeys.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # Check for openssl command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl required but not found. Aborting."; exit 1; } diff --git a/licenseGen.ps1 b/licenseGen.ps1 index 3907946..36815d6 100644 --- a/licenseGen.ps1 +++ b/licenseGen.ps1 @@ -1,3 +1,6 @@ +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + if ($($args.Count) -lt 1) { echo "USAGE: [License Gen args...]" echo "ACTIONS:" diff --git a/licenseGen.sh b/licenseGen.sh index 29c9e62..3d93aa4 100755 --- a/licenseGen.sh +++ b/licenseGen.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e if [ $# -lt 1 ]; then echo "USAGE: [License Gen args...]" diff --git a/src/bitBetter/Program.cs b/src/bitBetter/Program.cs index e28030a..2d1ed78 100644 --- a/src/bitBetter/Program.cs +++ b/src/bitBetter/Program.cs @@ -29,7 +29,7 @@ internal class Program Console.WriteLine(embeddedResourceToRemove.Name); - EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) {Attributes = embeddedResourceToRemove.Attributes }; + EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) { Attributes = embeddedResourceToRemove.Attributes }; moduleDefMd.Resources.Add(embeddedResourceToAdd); moduleDefMd.Resources.Remove(embeddedResourceToRemove); diff --git a/src/licenseGen/Dockerfile b/src/licenseGen/Dockerfile index b552cca..b5aa0fb 100644 --- a/src/licenseGen/Dockerfile +++ b/src/licenseGen/Dockerfile @@ -12,4 +12,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 WORKDIR /app COPY --from=build /app . -ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--cert", "/app/cert.pfx" ] +ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--executable", "/app/Api", "--cert", "/app/cert.pfx" ] \ No newline at end of file diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 160463f..581789f 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -377,7 +377,6 @@ internal class Program Set("Expires", DateTime.UtcNow.AddYears(100)); Set("Trial", false); Set("LicenseType", Enum.Parse(licenseTypeEnum, "User")); - Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, []))); Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, [cert]))); @@ -437,8 +436,11 @@ internal class Program Set("Trial", false); Set("LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); Set("LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now - Set("AllowAdminAccessToAllCollectionItems", true); - Set("UseRiskInsights", true); + Set("AllowAdminAccessToAllCollectionItems", true); + Set("UseRiskInsights", true); + Set("UseOrganizationDomains", true); + Set("UseAdminSponsoredFamilies", true); + Set("UseRiskInsights", true); Set("UseOrganizationDomains", true); Set("UseAdminSponsoredFamilies", true); Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, []))); From de61195d19eab76aa87387654329f27c850c98d7 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 5 Aug 2025 12:03:30 +0200 Subject: [PATCH 02/50] Fix license generator according to upstream changes (#245) (#249) --- src/licenseGen/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 581789f..71ba1b0 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -360,7 +360,7 @@ internal class Program { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); - Type type = core.GetType("Bit.Core.Models.Business.UserLicense"); + Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Object license = Activator.CreateInstance(type); @@ -393,7 +393,7 @@ internal class Program { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); - Type type = core.GetType("Bit.Core.Models.Business.OrganizationLicense"); + Type type = core.GetType("Bit.Core.Models.Billing.Business.OrganizationLicense"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); From d34041c1e3a62972f66506bc311d4f4677c5b610 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 5 Aug 2025 12:05:05 +0200 Subject: [PATCH 03/50] Test generating user and organization licenses during build check (#252) --- .circleci/config.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6bbf697..c367ed1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,4 +12,13 @@ jobs: command: ./generateKeys.sh - run: name: Build script - command: ./build.sh y \ No newline at end of file + command: ./build.sh y + - run: + name: Build licenseGen + command: ./src/licenseGen/build.sh + - run: + name: Test generating user license + command: ./src/licenseGen/run.sh ./.keys/cert.pfx user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 + - run: + name: Test generating organization license + command: ./src/licenseGen/run.sh ./.keys/cert.pfx org TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 \ No newline at end of file From 273ac7b4eb6807137ee7f2969a4d5c5db0ac15cb Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 12:09:48 +0200 Subject: [PATCH 04/50] Fix ps1 script and update language --- build.ps1 | 9 +++------ build.sh | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/build.ps1 b/build.ps1 index f3d79fc..955c593 100644 --- a/build.ps1 +++ b/build.ps1 @@ -46,13 +46,10 @@ foreach ($instance in $oldinstances) { } # update bitwarden itself -if ($args[0] -eq 'y') -{ +if ($args[0] -eq 'y') { docker pull ghcr.io/bitwarden/self-host:beta -} -else -{ - $confirmation = Read-Host "Update (or get) bitwarden source container" +} else { + $confirmation = Read-Host "Update (or get) bitwarden source container (y/n)" if ($confirmation -eq 'y') { docker pull ghcr.io/bitwarden/self-host:beta } diff --git a/build.sh b/build.sh index 468e22c..ed498d8 100755 --- a/build.sh +++ b/build.sh @@ -50,7 +50,7 @@ done if [ "$1" = "y" ]; then docker pull ghcr.io/bitwarden/self-host:beta else - read -p "Update (or get) bitwarden source container: " -n 1 -r + read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]] then From f360f54e4655fe331af9aee06ab2e6e31139383f Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 12:10:31 +0200 Subject: [PATCH 05/50] Update class path --- src/bitBetter/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitBetter/Program.cs b/src/bitBetter/Program.cs index 2d1ed78..c6c067e 100644 --- a/src/bitBetter/Program.cs +++ b/src/bitBetter/Program.cs @@ -41,7 +41,7 @@ internal class Program Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}"); - IEnumerable services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Services"); + IEnumerable services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Billing.Services"); TypeDef type = services.First(t => t.Name == "LicensingService"); MethodDef constructor = type.FindConstructors().First(); From 7786c4406c598c8be5765842878de97f1a354aff Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 12:18:28 +0200 Subject: [PATCH 06/50] Cleanup code --- build.ps1 | 8 ++++++-- build.sh | 12 ++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/build.ps1 b/build.ps1 index 955c593..5e80bec 100644 --- a/build.ps1 +++ b/build.ps1 @@ -89,8 +89,12 @@ Copy-Item "$tempdirectory\Identity\Core.dll" -Destination "$pwd\src\licenseGen" Remove-Item "$tempdirectory" -Recurse -Force # start all user requested instances -foreach($line in Get-Content "$pwd\.servers\serverlist.txt") { - Invoke-Expression "& $line" +if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) { + foreach($line in Get-Content "$pwd\.servers\serverlist.txt") { + if (!($line.StartsWith("#"))) { + Invoke-Expression "& $line" + } + } } # remove our bitBetter image diff --git a/build.sh b/build.sh index ed498d8..8775cc4 100755 --- a/build.sh +++ b/build.sh @@ -92,10 +92,14 @@ cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" rm -rf "$TEMPDIRECTORY" # start all user requested instances -sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" -cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do - bash -c "$LINE" -done +if [ -f "$PWD/src/bitBetter/cert.cert" ]; then + sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" + cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do + if [[ $LINE == "#*" ]] ; + bash -c "$LINE" + fi + done +fi # remove our bitBetter image docker image rm bitbetter/bitbetter From ddf67ec7064e8dc72ca81c5ef5121580db42d652 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 12:35:54 +0200 Subject: [PATCH 07/50] Cleanup code --- src/licenseGen/Program.cs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 71ba1b0..708e947 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -185,14 +185,8 @@ internal class Program { if (!VerifyTopOptions()) { - if (!CoreExists()) - { - config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); - } - if (!CertExists()) - { - config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); - } + if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); + if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); config.ShowHelp(); return 1; @@ -243,14 +237,8 @@ internal class Program { if (!VerifyTopOptions()) { - if (!CoreExists()) - { - config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); - } - if (!CertExists()) - { - config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); - } + if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); + if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); config.ShowHelp(); return 1; From 52fabd9a95c2f5752ee6dcf0bb554fb787bf47ee Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 13:10:13 +0200 Subject: [PATCH 08/50] Cleanup code --- src/licenseGen/Program.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 708e947..8794728 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -39,7 +39,7 @@ internal class Program Int16 storage = 0; Boolean validGuid = false, validInstallid = false; - Guid guid = new(), installid = new(); + Guid guid = Guid.Empty, installid = Guid.Empty; config.OnExecute(() => { @@ -64,7 +64,7 @@ internal class Program licenseType = "user"; Console.WriteLine("Okay, we will generate a user license."); - while (validGuid == false) + while (!validGuid) { Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); buff = Console.ReadLine(); @@ -78,7 +78,7 @@ internal class Program licenseType = "org"; Console.WriteLine("Okay, we will generate an organization license."); - while (validInstallid == false) + while (!validInstallid) { Console.WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]: "); buff = Console.ReadLine(); @@ -442,4 +442,4 @@ internal class Program type.GetProperty(name)?.SetValue(license, value); } } -} +} \ No newline at end of file From 93cae61d66359d851d177b30519e8e0590d8f406 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 13:42:08 +0200 Subject: [PATCH 09/50] Refactor and fixes --- src/licenseGen/Program.cs | 288 +++++++++++++++++++++----------------- 1 file changed, 163 insertions(+), 125 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 8794728..88df053 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -10,30 +10,33 @@ namespace licenseGen; internal class Program { + private static readonly CommandLineApplication App = new(); + private static readonly CommandOption Cert = App.Option("--cert", "cert file", CommandOptionType.SingleValue); + private static readonly CommandOption CoreDll = App.Option("--core", "path to core dll", CommandOptionType.SingleValue); + private static Int32 Main(String[] args) { - CommandLineApplication app = new(); - CommandOption cert = app.Option("--cert", "cert file", CommandOptionType.SingleValue); - CommandOption coreDll = app.Option("--core", "path to core dll", CommandOptionType.SingleValue); + App.HelpOption("-? | -h | --help"); - Boolean CertExists() + if (!File.Exists(Cert.Value())) { - return File.Exists(cert.Value()); + App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); + App.ShowHelp(); + return 1; + } + if (!File.Exists(CoreDll.Value())) + { + App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); + App.ShowHelp(); + return 1; + } + if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value()) || CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) + { + App.ShowHelp(); + return 1; } - Boolean CoreExists() - { - return File.Exists(coreDll.Value()); - } - - Boolean VerifyTopOptions() - { - return !String.IsNullOrWhiteSpace(cert.Value()) && - !String.IsNullOrWhiteSpace(coreDll.Value()) && - CertExists() && CoreExists(); - } - - app.Command("interactive", config => + App.Command("interactive", config => { String buff, licenseType = "", name = "", email = "", businessName=""; Int16 storage = 0; @@ -43,15 +46,6 @@ internal class Program config.OnExecute(() => { - if (!VerifyTopOptions()) - { - if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); - if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); - - config.ShowHelp(); - return 1; - } - Console.WriteLine("Interactive license mode..."); while (licenseType == "") @@ -59,51 +53,55 @@ internal class Program Console.WriteLine("What would you like to generate, a [u]ser license or an [o]rg license: "); buff = Console.ReadLine(); - if(buff == "u") + switch (buff) { - licenseType = "user"; - Console.WriteLine("Okay, we will generate a user license."); - - while (!validGuid) + case "u": { - Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); - buff = Console.ReadLine(); + licenseType = "user"; + Console.WriteLine("Okay, we will generate a user license."); - if (Guid.TryParse(buff, out guid))validGuid = true; - else Console.WriteLine("The user-guid provided does not appear to be valid!"); - } - } - else if (buff == "o") - { - licenseType = "org"; - Console.WriteLine("Okay, we will generate an organization license."); - - while (!validInstallid) - { - Console.WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]: "); - buff = Console.ReadLine(); - - if (Guid.TryParse(buff, out installid)) validInstallid = true; - else Console.WriteLine("The install-id provided does not appear to be valid."); - } - - while (businessName == "") - { - Console.WriteLine("Please enter a business name, default is BitBetter. [Business Name]: "); - buff = Console.ReadLine(); - if (buff == "") + while (!validGuid) { - businessName = "BitBetter"; - } - else if (CheckBusinessName(buff)) - { - businessName = buff; + Console.WriteLine("Please provide the user's guid — refer to the Readme for details on how to retrieve this. [GUID]: "); + buff = Console.ReadLine(); + + if (Guid.TryParse(buff, out guid))validGuid = true; + else Console.WriteLine("The user-guid provided does not appear to be valid!"); } + break; } - } - else - { - Console.WriteLine("Unrecognized option \'" + buff + "\'."); + case "o": + { + licenseType = "org"; + Console.WriteLine("Okay, we will generate an organization license."); + + while (!validInstallid) + { + Console.WriteLine("Please provide your Bitwarden Install-ID — refer to the Readme for details on how to retrieve this. [Install-ID]: "); + buff = Console.ReadLine(); + + if (Guid.TryParse(buff, out installid)) validInstallid = true; + else Console.WriteLine("The install-id provided does not appear to be valid."); + } + + while (businessName == "") + { + Console.WriteLine("Please enter a business name, default is BitBetter. [Business Name]: "); + buff = Console.ReadLine(); + if (buff == "") + { + businessName = "BitBetter"; + } + else if (CheckBusinessName(buff)) + { + businessName = buff; + } + } + break; + } + default: + Console.WriteLine("Unrecognized option \'" + buff + "\'."); + break; } } @@ -111,7 +109,7 @@ internal class Program { Console.WriteLine("Please provide the username this license will be registered to. [username]: "); buff = Console.ReadLine(); - if ( CheckUsername(buff) ) name = buff; + if (CheckUsername(buff)) name = buff; } while (email == "") @@ -141,39 +139,46 @@ internal class Program } } - if (licenseType == "user") + switch (licenseType) { - Console.WriteLine("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); - buff = Console.ReadLine(); - if ( buff is "" or "y" or "Y" ) + case "user": { - GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, guid, null); + Console.WriteLine("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", User-GUID: \"" + guid + "\"? Y/n"); + buff = Console.ReadLine(); + if (buff is "" or "y" or "Y") + { + GenerateUserLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, guid, null); + } + else + { + Console.WriteLine("Exiting..."); + return 0; + } + + break; } - else + case "org": { - Console.WriteLine("Exiting..."); - return 0; - } - } - else if (licenseType == "org") - { - Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n"); - buff = Console.ReadLine(); - if ( buff is "" or "y" or "Y" ) - { - GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, storage, installid, businessName, null); - } - else - { - Console.WriteLine("Exiting..."); - return 0; + Console.WriteLine("Confirm creation of \"organization\" license for business name: \"" + businessName + "\", username: \"" + name + "\", email: \"" + email + "\", Storage: \"" + storage + " GB\", Install-ID: \"" + installid + "\"? Y/n"); + buff = Console.ReadLine(); + if (buff is "" or "y" or "Y") + { + GenerateOrgLicense(new X509Certificate2(Cert.Value(), "test"), CoreDll.Value(), name, email, storage, installid, businessName, null); + } + else + { + Console.WriteLine("Exiting..."); + return 0; + } + + break; } } return 0; }); }); - app.Command("user", config => + App.Command("user", config => { CommandArgument name = config.Argument("Name", "your name"); CommandArgument email = config.Argument("Email", "your email"); @@ -183,15 +188,6 @@ internal class Program config.OnExecute(() => { - if (!VerifyTopOptions()) - { - if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); - if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); - - config.ShowHelp(); - return 1; - } - if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) { config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'"); @@ -219,12 +215,12 @@ internal class Program storageShort = (Int16) parsedStorage; } - GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); + GenerateUserLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, userId, key.Value); return 0; }); }); - app.Command("org", config => + App.Command("org", config => { CommandArgument name = config.Argument("Name", "your name"); CommandArgument email = config.Argument("Email", "your email"); @@ -235,18 +231,7 @@ internal class Program config.OnExecute(() => { - if (!VerifyTopOptions()) - { - if (!CoreExists()) config.Error.WriteLine($"Can't find core dll at: {coreDll.Value()}"); - if (!CertExists()) config.Error.WriteLine($"Can't find certificate at: {cert.Value()}"); - - config.ShowHelp(); - return 1; - } - - if (String.IsNullOrWhiteSpace(name.Value) || - String.IsNullOrWhiteSpace(email.Value) || - String.IsNullOrWhiteSpace(installId.Value)) + if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value) || String.IsNullOrWhiteSpace(installId.Value)) { config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'"); config.ShowHelp(true); @@ -271,26 +256,24 @@ internal class Program config.ShowHelp(true); return 1; } - storageShort = (Int16) parsedStorage; + storageShort = (Int16)parsedStorage; } - GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); + GenerateOrgLicense(new X509Certificate2(Cert.Value()!, "test"), CoreDll.Value(), name.Value, email.Value, storageShort, installationId, businessName.Value, key.Value); return 0; }); }); - app.OnExecute(() => + App.OnExecute(() => { - app.ShowHelp(); + App.ShowHelp(); return 10; }); - app.HelpOption("-? | -h | --help"); - try { - return app.Execute(args); + return App.Execute(args); } catch (Exception e) { @@ -351,8 +334,33 @@ internal class Program Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); + if (type == null) + { + Console.WriteLine("Could not find type!"); + return; + } + if (licenseTypeEnum == null) + { + Console.WriteLine("Could not find license licenseTypeEnum!"); + return; + } + Object license = Activator.CreateInstance(type); + MethodInfo computeHash = type.GetMethod("ComputeHash"); + if (computeHash == null) + { + Console.WriteLine("Could not find ComputeHash!"); + return; + } + + MethodInfo sign = type.GetMethod("Sign"); + if (sign == null) + { + Console.WriteLine("Could not find sign!"); + return; + } + Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); Set("Id", userId); Set("Name", userName); @@ -365,8 +373,8 @@ internal class Program Set("Expires", DateTime.UtcNow.AddYears(100)); Set("Trial", false); Set("LicenseType", Enum.Parse(licenseTypeEnum, "User")); - Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, []))); - Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, [cert]))); + Set("Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); + Set("Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented)); return; @@ -381,12 +389,42 @@ internal class Program { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); - Type type = core.GetType("Bit.Core.Models.Billing.Business.OrganizationLicense"); + Type type = core.GetType("Bit.Core.Billing.Organizations.Models"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); + if (type == null) + { + Console.WriteLine("Could not find type!"); + return; + } + if (licenseTypeEnum == null) + { + Console.WriteLine("Could not find licenseTypeEnum!"); + return; + } + if (planTypeEnum == null) + { + Console.WriteLine("Could not find planTypeEnum!"); + return; + } + Object license = Activator.CreateInstance(type); + MethodInfo computeHash = type.GetMethod("ComputeHash"); + if (computeHash == null) + { + Console.WriteLine("Could not find ComputeHash!"); + return; + } + + MethodInfo sign = type.GetMethod("Sign"); + if (sign == null) + { + Console.WriteLine("Could not find sign!"); + return; + } + Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); Set("InstallationId", instalId); Set("Id", Guid.NewGuid()); @@ -431,8 +469,8 @@ internal class Program Set("UseRiskInsights", true); Set("UseOrganizationDomains", true); Set("UseAdminSponsoredFamilies", true); - Set("Hash", Convert.ToBase64String((Byte[])type.GetMethod("ComputeHash").Invoke(license, []))); - Set("Signature", Convert.ToBase64String((Byte[])type.GetMethod("Sign").Invoke(license, [cert]))); + Set("Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); + Set("Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented)); return; From e87bc81a9ca9d3bd907f714c3ab5274a72f6be22 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:01:26 +0200 Subject: [PATCH 10/50] Copy all files --- src/bitBetter/Dockerfile-bitwarden-patch | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bitBetter/Dockerfile-bitwarden-patch b/src/bitBetter/Dockerfile-bitwarden-patch index c99e2df..c14b2b3 100644 --- a/src/bitBetter/Dockerfile-bitwarden-patch +++ b/src/bitBetter/Dockerfile-bitwarden-patch @@ -1,4 +1,3 @@ FROM ghcr.io/bitwarden/self-host:beta -COPY ./temp/Api/Core.dll /app/Api/Core.dll -COPY ./temp/Identity/Core.dll /app/Identity/Core.dll +COPY ./temp/ /app/ \ No newline at end of file From 80fcf0cfc62b39b431d02f5fc78b0d95520862e8 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:01:57 +0200 Subject: [PATCH 11/50] Copy files only when needed --- build.ps1 | 16 ++++++++-------- build.sh | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build.ps1 b/build.ps1 index 5e80bec..1d04b8f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -28,9 +28,8 @@ if (!(Test-Path "$pwd\.keys")) { .\generateKeys.ps1 } -# copy the key to bitBetter and licenseGen +# copy the key to bitBetter Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter" -Copy-Item "$pwd\.keys\cert.pfx" -Destination "$pwd\src\licenseGen" # build bitBetter and clean the source directory after docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter" @@ -82,12 +81,6 @@ docker build . --tag bitwarden-patch --file "$pwd\src\bitBetter\Dockerfile-bitwa docker stop bitwarden-patch docker rm bitwarden-patch -# copy our patched library to the licenseGen source directory -Copy-Item "$tempdirectory\Identity\Core.dll" -Destination "$pwd\src\licenseGen" - -# remove our temporary directory -Remove-Item "$tempdirectory" -Recurse -Force - # start all user requested instances if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) { foreach($line in Get-Content "$pwd\.servers\serverlist.txt") { @@ -100,9 +93,16 @@ if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) { # remove our bitBetter image docker image rm bitbetter/bitbetter +# copy our patched library to the licenseGen source directory +Copy-Item "$tempdirectory\Identity\Core.dll" -Destination "$pwd\src\licenseGen" +Copy-Item "$pwd\.keys\cert.pfx" -Destination "$pwd\src\licenseGen" + # build the licenseGen docker build -t bitbetter/licensegen "$pwd\src\licenseGen" # clean the licenseGen source directory Remove-Item "$pwd\src\licenseGen\Core.dll" -Force Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force + +# remove our temporary directory +Remove-Item "$tempdirectory" -Recurse -Force \ No newline at end of file diff --git a/build.sh b/build.sh index 8775cc4..1b4fd4b 100755 --- a/build.sh +++ b/build.sh @@ -29,9 +29,8 @@ if [ ! -d "$PWD/.keys" ]; then ./generateKeys.sh fi -# copy the key to bitBetter and licenseGen +# copy the key to bitBetter cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" -cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" # build bitBetter and clean the source directory after docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" @@ -85,12 +84,6 @@ docker build . --tag bitwarden-patch --file "$PWD/src/bitBetter/Dockerfile-bitwa docker stop bitwarden-patch docker rm bitwarden-patch -# copy our patched library to the licenseGen source directory -cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" - -# remove our temporary directory -rm -rf "$TEMPDIRECTORY" - # start all user requested instances if [ -f "$PWD/src/bitBetter/cert.cert" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" @@ -104,9 +97,16 @@ fi # remove our bitBetter image docker image rm bitbetter/bitbetter +# copy our patched library to the licenseGen source directory +cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" +cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" + # build the licenseGen docker build -t bitbetter/licensegen "$PWD/src/licenseGen" # clean the licenseGen source directory rm -f "$PWD/src/licenseGen/Core.dll" rm -f "$PWD/src/licenseGen/cert.pfx" + +# remove our temporary directory +rm -rf "$TEMPDIRECTORY" \ No newline at end of file From 48c67fc66eca6dff3dcde287725f66bf0f45c856 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:02:21 +0200 Subject: [PATCH 12/50] Make call consistent --- src/bitBetter/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitBetter/Dockerfile b/src/bitBetter/Dockerfile index 91a7f8e..3077c6a 100644 --- a/src/bitBetter/Dockerfile +++ b/src/bitBetter/Dockerfile @@ -11,4 +11,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 WORKDIR /app COPY --from=build /app . -ENTRYPOINT [ "/app/bitBetter" ] \ No newline at end of file +ENTRYPOINT ["dotnet", "/app/bitBetter.dll"] \ No newline at end of file From dfc364e7f343c8bbe1b9d029bc81564985a11795 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:02:30 +0200 Subject: [PATCH 13/50] Simplify call --- src/licenseGen/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/licenseGen/Dockerfile b/src/licenseGen/Dockerfile index b5aa0fb..601bf87 100644 --- a/src/licenseGen/Dockerfile +++ b/src/licenseGen/Dockerfile @@ -12,4 +12,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 WORKDIR /app COPY --from=build /app . -ENTRYPOINT [ "dotnet", "/app/licenseGen.dll", "--core", "/app/Core.dll", "--executable", "/app/Api", "--cert", "/app/cert.pfx" ] \ No newline at end of file +ENTRYPOINT ["dotnet", "/app/licenseGen.dll", "--cert=/app/cert.pfx", "--core=/app/Core.dll"] \ No newline at end of file From 0b0512570f87ff79bf17d439088b653531222b8b Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:02:49 +0200 Subject: [PATCH 14/50] Clarify language --- src/licenseGen/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 88df053..5d7c4e3 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -11,8 +11,8 @@ namespace licenseGen; internal class Program { private static readonly CommandLineApplication App = new(); - private static readonly CommandOption Cert = App.Option("--cert", "cert file", CommandOptionType.SingleValue); - private static readonly CommandOption CoreDll = App.Option("--core", "path to core dll", CommandOptionType.SingleValue); + private static readonly CommandOption Cert = App.Option("--cert", "Certifcate file", CommandOptionType.SingleValue); + private static readonly CommandOption CoreDll = App.Option("--core", "Path to Core.dll", CommandOptionType.SingleValue); private static Int32 Main(String[] args) { From c72fbf5b1c2214e2856de034f9022d7ef75fdb82 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:03:48 +0200 Subject: [PATCH 15/50] Reuse code --- src/licenseGen/Program.cs | 53 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 5d7c4e3..367aedc 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -16,36 +16,16 @@ internal class Program private static Int32 Main(String[] args) { - App.HelpOption("-? | -h | --help"); - - if (!File.Exists(Cert.Value())) - { - App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); - App.ShowHelp(); - return 1; - } - if (!File.Exists(CoreDll.Value())) - { - App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); - App.ShowHelp(); - return 1; - } - if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value()) || CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) - { - App.ShowHelp(); - return 1; - } - App.Command("interactive", config => { String buff, licenseType = "", name = "", email = "", businessName=""; Int16 storage = 0; - Boolean validGuid = false, validInstallid = false; Guid guid = Guid.Empty, installid = Guid.Empty; config.OnExecute(() => { + Check(); Console.WriteLine("Interactive license mode..."); while (licenseType == "") @@ -188,6 +168,8 @@ internal class Program config.OnExecute(() => { + Check(); + if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value)) { config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}'"); @@ -231,6 +213,8 @@ internal class Program config.OnExecute(() => { + Check(); + if (String.IsNullOrWhiteSpace(name.Value) || String.IsNullOrWhiteSpace(email.Value) || String.IsNullOrWhiteSpace(installId.Value)) { config.Error.WriteLine($"Some arguments are missing: Name='{name.Value}' Email='{email.Value}' InstallId='{installId.Value}'"); @@ -273,15 +257,37 @@ internal class Program try { + App.HelpOption("-? | -h | --help"); return App.Execute(args); } - catch (Exception e) + catch (Exception exception) { - Console.Error.WriteLine("Oops: {0}", e); + Console.Error.WriteLine("Oops: {0}", exception); return 100; } } + private static void Check() + { + if (!File.Exists(Cert.Value())) + { + App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); + App.ShowHelp(); + Environment.Exit(1); + } + if (!File.Exists(CoreDll.Value())) + { + App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); + App.ShowHelp(); + Environment.Exit(1); + } + if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value()) || CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) + { + App.ShowHelp(); + Environment.Exit(1); + } + } + // checkUsername Checks that the username is a valid username private static Boolean CheckUsername(String s) { @@ -384,7 +390,6 @@ internal class Program type.GetProperty(name)?.SetValue(license, value); } } - private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); From d8098cb560f80777dcc0fb5ca5752bec86c90234 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:03:54 +0200 Subject: [PATCH 16/50] Cleanup --- src/bitBetter/Program.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/bitBetter/Program.cs b/src/bitBetter/Program.cs index c6c067e..a47fd6b 100644 --- a/src/bitBetter/Program.cs +++ b/src/bitBetter/Program.cs @@ -23,12 +23,7 @@ internal class Program ModuleDefMD moduleDefMd = ModuleDefMD.Load(file); Byte[] cert = File.ReadAllBytes(certFile); - EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources - .OfType() - .First(r => r.Name.Equals("Bit.Core.licensing.cer")); - - Console.WriteLine(embeddedResourceToRemove.Name); - + EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources.OfType().First(r => r.Name.Equals("Bit.Core.licensing.cer")); EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) { Attributes = embeddedResourceToRemove.Attributes }; moduleDefMd.Resources.Add(embeddedResourceToAdd); moduleDefMd.Resources.Remove(embeddedResourceToRemove); @@ -45,10 +40,7 @@ internal class Program TypeDef type = services.First(t => t.Name == "LicensingService"); MethodDef constructor = type.FindConstructors().First(); - Instruction instructionToPatch = - constructor.Body.Instructions - .FirstOrDefault(i => i.OpCode == OpCodes.Ldstr - && String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)); + Instruction instructionToPatch = constructor.Body.Instructions.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr && String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase)); if (instructionToPatch != null) { From 6ba14a7134db2081764e46c6e47a48b401abf484 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:15:31 +0200 Subject: [PATCH 17/50] Cleanup --- src/bitBetter/bitBetter.csproj | 3 --- src/licenseGen/licenseGen.csproj | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/bitBetter/bitBetter.csproj b/src/bitBetter/bitBetter.csproj index d996f47..629fa89 100644 --- a/src/bitBetter/bitBetter.csproj +++ b/src/bitBetter/bitBetter.csproj @@ -1,12 +1,9 @@ - Exe net8.0 - - diff --git a/src/licenseGen/licenseGen.csproj b/src/licenseGen/licenseGen.csproj index 1d6ccd0..c7a3c0a 100644 --- a/src/licenseGen/licenseGen.csproj +++ b/src/licenseGen/licenseGen.csproj @@ -1,14 +1,11 @@ - Exe net8.0 - - From 9ca720af8cbc0bb46fa8e1a6e9f36399ac3b523f Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:16:59 +0200 Subject: [PATCH 18/50] Remove NewtonSoft.Json --- src/licenseGen/Program.cs | 147 +++++++++++++++---------------- src/licenseGen/licenseGen.csproj | 1 - 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 367aedc..47e1e52 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -1,10 +1,11 @@ +using McMaster.Extensions.CommandLineUtils; using System; +using System.ComponentModel; using System.IO; using System.Reflection; using System.Runtime.Loader; using System.Security.Cryptography.X509Certificates; -using McMaster.Extensions.CommandLineUtils; -using Newtonsoft.Json; +using System.Text.Json; namespace licenseGen; @@ -333,6 +334,7 @@ internal class Program return false; } + private static JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { WriteIndented = true }; private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); @@ -367,33 +369,26 @@ internal class Program return; } - Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); - Set("Id", userId); - Set("Name", userName); - Set("Email", email); - Set("Premium", true); - Set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); - Set("Version", 1); - Set("Issued", DateTime.UtcNow); - Set("Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); - Set("Expires", DateTime.UtcNow.AddYears(100)); - Set("Trial", false); - Set("LicenseType", Enum.Parse(licenseTypeEnum, "User")); - Set("Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); - Set("Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); + Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); + Set(type, license, "Id", userId); + Set(type, license, "Name", userName); + Set(type, license, "Email", email); + Set(type, license, "Premium", true); + Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); + Set(type, license, "Version", 1); + Set(type, license, "Issued", DateTime.UtcNow); + Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); + Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "Trial", false); + Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "User")); + Set(type, license, "Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); + Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented)); - return; - - void Set(String name, Object value) - { - type.GetProperty(name)?.SetValue(license, value); - } + Console.WriteLine(JsonSerializer.Serialize(license, _jsonOptions)); } private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); - Type type = core.GetType("Bit.Core.Billing.Organizations.Models"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); @@ -430,59 +425,57 @@ internal class Program return; } - Set("LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); - Set("InstallationId", instalId); - Set("Id", Guid.NewGuid()); - Set("Name", userName); - Set("BillingEmail", email); - Set("BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); - Set("Enabled", true); - Set("Plan", "Enterprise (Annually)"); - Set("PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); - Set("Seats", Int32.MaxValue); - Set("MaxCollections", Int16.MaxValue); - Set("UsePolicies", true); - Set("UseSso", true); - Set("UseKeyConnector", true); - Set("UseScim", true); - Set("UseGroups", true); - Set("UseEvents", true); - Set("UseDirectory", true); - Set("UseTotp", true); - Set("Use2fa", true); - Set("UseApi", true); - Set("UseResetPassword", true); - Set("UseCustomPermissions", true); - Set("MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); - Set("SelfHost", true); - Set("UsersGetPremium", true); - Set("UsePasswordManager", true); - Set("UseSecretsManager", true); - Set("SmSeats", Int32.MaxValue); - Set("SmServiceAccounts", Int32.MaxValue); - Set("Version", 15); //This is set to 15 to use AllowAdminAccessToAllCollectionItems can be changed to 13 to just use Secrets Manager - Set("Issued", DateTime.UtcNow); - Set("Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); - Set("Expires", DateTime.UtcNow.AddYears(100)); - Set("Trial", false); - Set("LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); - Set("LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now - Set("AllowAdminAccessToAllCollectionItems", true); - Set("UseRiskInsights", true); - Set("UseOrganizationDomains", true); - Set("UseAdminSponsoredFamilies", true); - Set("UseRiskInsights", true); - Set("UseOrganizationDomains", true); - Set("UseAdminSponsoredFamilies", true); - Set("Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); - Set("Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); + Set(type, license, "LicenseKey", String.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString("n") : key); + Set(type, license, "InstallationId", instalId); + Set(type, license, "Id", Guid.NewGuid()); + Set(type, license, "Name", userName); + Set(type, license, "BillingEmail", email); + Set(type, license, "BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); + Set(type, license, "Enabled", true); + Set(type, license, "Plan", "Enterprise (Annually)"); + Set(type, license, "PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); + Set(type, license, "Seats", Int32.MaxValue); + Set(type, license, "MaxCollections", Int16.MaxValue); + Set(type, license, "UsePolicies", true); + Set(type, license, "UseSso", true); + Set(type, license, "UseKeyConnector", true); + Set(type, license, "UseScim", true); + Set(type, license, "UseGroups", true); + Set(type, license, "UseEvents", true); + Set(type, license, "UseDirectory", true); + Set(type, license, "UseTotp", true); + Set(type, license, "Use2fa", true); + Set(type, license, "UseApi", true); + Set(type, license, "UseResetPassword", true); + Set(type, license, "UseCustomPermissions", true); + Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); + Set(type, license, "SelfHost", true); + Set(type, license, "UsersGetPremium", true); + Set(type, license, "UsePasswordManager", true); + Set(type, license, "UseSecretsManager", true); + Set(type, license, "SmSeats", Int32.MaxValue); + Set(type, license, "SmServiceAccounts", Int32.MaxValue); + Set(type, license, "Version", 15); //This is set to 15 to use AllowAdminAccessToAllCollectionItems can be changed to 13 to just use Secrets Manager + Set(type, license, "Issued", DateTime.UtcNow); + Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); + Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "Trial", false); + Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); + Set(type, license, "LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now + Set(type, license, "AllowAdminAccessToAllCollectionItems", true); + Set(type, license, "UseRiskInsights", true); + Set(type, license, "UseOrganizationDomains", true); + Set(type, license, "UseAdminSponsoredFamilies", true); + Set(type, license, "UseRiskInsights", true); + Set(type, license, "UseOrganizationDomains", true); + Set(type, license, "UseAdminSponsoredFamilies", true); + Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); + Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - Console.WriteLine(JsonConvert.SerializeObject(license, Formatting.Indented)); - return; - - void Set(String name, Object value) - { - type.GetProperty(name)?.SetValue(license, value); - } + Console.WriteLine(JsonSerializer.Serialize(license, _jsonOptions)); + } + private static void Set(Type type, Object license, String name, Object value) + { + type.GetProperty(name)?.SetValue(license, value); } } \ No newline at end of file diff --git a/src/licenseGen/licenseGen.csproj b/src/licenseGen/licenseGen.csproj index c7a3c0a..6e03496 100644 --- a/src/licenseGen/licenseGen.csproj +++ b/src/licenseGen/licenseGen.csproj @@ -5,7 +5,6 @@ - From 72033542049958d04052906cce6967f45204fdc7 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:18:46 +0200 Subject: [PATCH 19/50] Upgrade dnlib --- src/bitBetter/bitBetter.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitBetter/bitBetter.csproj b/src/bitBetter/bitBetter.csproj index 629fa89..6433b92 100644 --- a/src/bitBetter/bitBetter.csproj +++ b/src/bitBetter/bitBetter.csproj @@ -4,6 +4,6 @@ net8.0 - + From ab8eed492c22b407ec1cdd4e7aa2bc12fc8bd74e Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:23:49 +0200 Subject: [PATCH 20/50] Cleanup --- src/licenseGen/Program.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 47e1e52..f9036b1 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -1,11 +1,10 @@ -using McMaster.Extensions.CommandLineUtils; using System; -using System.ComponentModel; using System.IO; +using System.Text.Json; using System.Reflection; using System.Runtime.Loader; using System.Security.Cryptography.X509Certificates; -using System.Text.Json; +using McMaster.Extensions.CommandLineUtils; namespace licenseGen; @@ -334,7 +333,7 @@ internal class Program return false; } - private static JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { WriteIndented = true }; + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); @@ -384,7 +383,7 @@ internal class Program Set(type, license, "Hash", Convert.ToBase64String(((Byte[])computeHash.Invoke(license, []))!)); Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - Console.WriteLine(JsonSerializer.Serialize(license, _jsonOptions)); + Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); } private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) { @@ -472,7 +471,7 @@ internal class Program Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); Set(type, license, "Signature", Convert.ToBase64String((Byte[])sign.Invoke(license, [cert])!)); - Console.WriteLine(JsonSerializer.Serialize(license, _jsonOptions)); + Console.WriteLine(JsonSerializer.Serialize(license, JsonOptions)); } private static void Set(Type type, Object license, String name, Object value) { From 1ee45a327f1001954061258b56161c4d4ca79140 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:30:35 +0200 Subject: [PATCH 21/50] Fix path issue --- src/licenseGen/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index f9036b1..5d092cd 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -336,7 +336,7 @@ internal class Program private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; private static void GenerateUserLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid userId, String key) { - Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); + Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); Type type = core.GetType("Bit.Core.Billing.Models.Business.UserLicense"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); @@ -387,7 +387,7 @@ internal class Program } private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) { - Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); + Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); Type type = core.GetType("Bit.Core.Billing.Organizations.Models"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); From d07f030d9a8899d5f74660ae6a90744b4a6bc93c Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:32:49 +0200 Subject: [PATCH 22/50] Fix comparator --- licenseGen.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/licenseGen.ps1 b/licenseGen.ps1 index 36815d6..7b69b43 100644 --- a/licenseGen.ps1 +++ b/licenseGen.ps1 @@ -10,7 +10,7 @@ if ($($args.Count) -lt 1) { Exit 1 } -if ($args[0] = "interactive") { +if ($args[0] -eq "interactive") { docker run -it --rm bitbetter/licensegen interactive } else { docker run bitbetter/licensegen $args From b12b4706564c9961f18c4b475592bdb509e22be2 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:32:59 +0200 Subject: [PATCH 23/50] Cleanup circleci --- .circleci/config.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c367ed1..3a43887 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,13 +12,10 @@ jobs: command: ./generateKeys.sh - run: name: Build script - command: ./build.sh y - - run: - name: Build licenseGen - command: ./src/licenseGen/build.sh + command: ./build.sh - run: name: Test generating user license - command: ./src/licenseGen/run.sh ./.keys/cert.pfx user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 + command: ./licenseGen.sh user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 - run: name: Test generating organization license - command: ./src/licenseGen/run.sh ./.keys/cert.pfx org TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 \ No newline at end of file + command: ./licenseGen.sh org TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 \ No newline at end of file From 232de042dd868793f757cb55596adfc64e5baba0 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:37:06 +0200 Subject: [PATCH 24/50] Fix type --- src/licenseGen/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 5d092cd..3561455 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -388,7 +388,7 @@ internal class Program private static void GenerateOrgLicense(X509Certificate2 cert, String corePath, String userName, String email, Int16 storage, Guid instalId, String businessName, String key) { Assembly core = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(corePath)); - Type type = core.GetType("Bit.Core.Billing.Organizations.Models"); + Type type = core.GetType("Bit.Core.Billing.Organizations.Models.OrganizationLicense"); Type licenseTypeEnum = core.GetType("Bit.Core.Enums.LicenseType"); Type planTypeEnum = core.GetType("Bit.Core.Billing.Enums.PlanType"); From b75dfb25129d8a9b4bdd26d02cb2c21e05d4bef6 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:42:52 +0200 Subject: [PATCH 25/50] Fix circleci --- .circleci/config.yml | 2 +- build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3a43887..443f66e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: command: ./generateKeys.sh - run: name: Build script - command: ./build.sh + command: ./build.sh update - run: name: Test generating user license command: ./licenseGen.sh user TestName TestEmail@example.com 4a619d4a-522d-4c70-8596-affb5b607c23 diff --git a/build.sh b/build.sh index 1b4fd4b..4125f8e 100755 --- a/build.sh +++ b/build.sh @@ -46,7 +46,7 @@ for INSTANCE in ${OLDINSTANCES[@]}; do done # update bitwarden itself -if [ "$1" = "y" ]; then +if [ "$1" = "update" ]; then docker pull ghcr.io/bitwarden/self-host:beta else read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r From 874ff182c6e4575d4eef2ea316be84f023a7ee1f Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:45:54 +0200 Subject: [PATCH 26/50] Properly detect previous version --- build.ps1 | 9 ++++++--- build.sh | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/build.ps1 b/build.ps1 index 1d04b8f..37eede9 100644 --- a/build.ps1 +++ b/build.ps1 @@ -55,9 +55,12 @@ if ($args[0] -eq 'y') { } # stop and remove previous existing patch(ed) container -docker stop bitwarden-patch -docker rm bitwarden-patch -docker image rm bitwarden-patch +$oldinstances = docker container ps --all -f Name=bitwarden-patch --format '{{.ID}}' +foreach ($instance in $oldinstances) { + docker stop $instance + docker rm $instance + docker image rm $instance +} # start a new bitwarden instance so we can patch it $patchinstance = docker run -d --name bitwarden-patch ghcr.io/bitwarden/self-host:beta diff --git a/build.sh b/build.sh index 4125f8e..011f89d 100755 --- a/build.sh +++ b/build.sh @@ -58,9 +58,12 @@ else fi # stop and remove previous existing patch(ed) container -docker stop bitwarden-patch -docker rm bitwarden-patch -docker image rm bitwarden-patch +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-patch --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE + docker image rm $INSTANCE +done # start a new bitwarden instance so we can patch it PATCHINSTANCE=$(docker run -d --name bitwarden-patch ghcr.io/bitwarden/self-host:beta) From fe7ac2131eb69f1b12a4778349b40f6bb0769093 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 15:47:20 +0200 Subject: [PATCH 27/50] Add missing parameter --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 011f89d..36e3ddd 100755 --- a/build.sh +++ b/build.sh @@ -91,7 +91,7 @@ docker rm bitwarden-patch if [ -f "$PWD/src/bitBetter/cert.cert" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do - if [[ $LINE == "#*" ]] ; + if [[ $LINE == "#*" ]]; then bash -c "$LINE" fi done From f9055c711af7a9b904609c36a170ccca7fcdb8e2 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:13:11 +0200 Subject: [PATCH 28/50] Better detect running patched containers --- build.ps1 | 24 +++++++++++++++++------- build.sh | 22 ++++++++++++++++------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/build.ps1 b/build.ps1 index 91cece8..f5271c2 100644 --- a/build.ps1 +++ b/build.ps1 @@ -49,21 +49,31 @@ if ($args[0] -eq 'y') { docker pull ghcr.io/bitwarden/self-host:beta } else { $confirmation = Read-Host "Update (or get) bitwarden source container (y/n)" - if ($confirmation -eq 'y') { + if ($confirmation -eq 'update') { docker pull ghcr.io/bitwarden/self-host:beta } } # stop and remove previous existing patch(ed) container -$oldinstances = docker container ps --all -f Name=bitwarden-patch --format '{{.ID}}' +$oldinstances = docker container ps --all -f Ancestor=bitwarden-patch --format '{{.ID}}' foreach ($instance in $oldinstances) { docker stop $instance docker rm $instance +} +$oldinstances = docker image ls bitwarden-patch --format '{{.ID}}' +foreach ($instance in $oldinstances) { docker image rm $instance } +# remove old extract containers +$oldinstances = docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}' +foreach ($instance in $oldinstances) { + docker stop $instance + docker rm $instance +} + # start a new bitwarden instance so we can patch it -$patchinstance = docker run -d --name bitwarden-patch ghcr.io/bitwarden/self-host:beta +$patchinstance = docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta # create our temporary directory New-item -ItemType Directory -Path $tempdirectory @@ -74,16 +84,16 @@ foreach ($component in $components) { docker cp $patchinstance`:/app/$component/Core.dll "$tempdirectory\$component\Core.dll" } +# stop and remove our temporary container +docker stop bitwarden-extract +docker rm bitwarden-extract + # run bitBetter, this applies our patches to the required files docker run -v "$tempdirectory`:/app/mount" --rm bitbetter/bitbetter # create a new image with the patched files docker build . --tag bitwarden-patch --file "$pwd\src\bitBetter\Dockerfile-bitwarden-patch" -# stop and remove our temporary container -docker stop bitwarden-patch -docker rm bitwarden-patch - # start all user requested instances if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) { foreach($line in Get-Content "$pwd\.servers\serverlist.txt") { diff --git a/build.sh b/build.sh index 232ebaf..993a82f 100755 --- a/build.sh +++ b/build.sh @@ -58,15 +58,25 @@ else fi # stop and remove previous existing patch(ed) container -OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-patch --format '{{.ID}}') +OLDINSTANCES=$(docker container ps --all -f Ancestor=bitwarden-patch --format '{{.ID}}') for INSTANCE in ${OLDINSTANCES[@]}; do docker stop $INSTANCE docker rm $INSTANCE +done +OLDINSTANCES=$(docker image ls bitwarden-patch --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do docker image rm $INSTANCE done +# remove old extract containers +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done + # start a new bitwarden instance so we can patch it -PATCHINSTANCE=$(docker run -d --name bitwarden-patch ghcr.io/bitwarden/self-host:beta) +PATCHINSTANCE=$(docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta) # create our temporary directory mkdir $TEMPDIRECTORY @@ -77,16 +87,16 @@ for COMPONENT in ${COMPONENTS[@]}; do docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll" done +# stop and remove our temporary container +docker stop bitwarden-extract +docker rm bitwarden-extract + # run bitBetter, this applies our patches to the required files docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter # create a new image with the patched files docker build . --tag bitwarden-patch --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" -# stop and remove our temporary container -docker stop bitwarden-patch -docker rm bitwarden-patch - # start all user requested instances if [ -f "$PWD/src/bitBetter/cert.cert" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" From a527d425fd63e81cd584f3f11b094dbb6c774ee1 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:15:51 +0200 Subject: [PATCH 29/50] Improve naming --- .servers/serverlist.txt | 2 +- README.md | 2 +- build.ps1 | 6 +- build.sh | 248 ++++++++++++++++++++-------------------- 4 files changed, 129 insertions(+), 129 deletions(-) diff --git a/.servers/serverlist.txt b/.servers/serverlist.txt index f1debc9..3d99d6b 100644 --- a/.servers/serverlist.txt +++ b/.servers/serverlist.txt @@ -1,4 +1,4 @@ # Uncomment a line below and fill in the missing values or add your own. Every line in this file will be called by build.[sh|ps1] once the patched image is built. -# docker run -d --name bitwarden -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patch +# docker run -d --name bitwarden -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patched # # docker-compose -f /docker-compose.yml up -d diff --git a/README.md b/README.md index 6890eee..7225b9a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ From the BitBetter directory, simply run: ./build.[sh|ps1] ``` -This will create a new self-signed certificate in the `.keys` directory if one does not already exist and then create a modified version of the official `ghcr.io/bitwarden/self-host` image called `bitwarden-patch`. +This will create a new self-signed certificate in the `.keys` directory if one does not already exist and then create a modified version of the official `ghcr.io/bitwarden/self-host` image called `bitwarden-patched`. Afterwards it will automatically generate the license generator and start all previously specified containers which are **now ready to accept self-issued licenses.** diff --git a/build.ps1 b/build.ps1 index f5271c2..4d60b6b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -55,12 +55,12 @@ if ($args[0] -eq 'y') { } # stop and remove previous existing patch(ed) container -$oldinstances = docker container ps --all -f Ancestor=bitwarden-patch --format '{{.ID}}' +$oldinstances = docker container ps --all -f Ancestor=bitwarden-patched --format '{{.ID}}' foreach ($instance in $oldinstances) { docker stop $instance docker rm $instance } -$oldinstances = docker image ls bitwarden-patch --format '{{.ID}}' +$oldinstances = docker image ls bitwarden-patched --format '{{.ID}}' foreach ($instance in $oldinstances) { docker image rm $instance } @@ -92,7 +92,7 @@ docker rm bitwarden-extract docker run -v "$tempdirectory`:/app/mount" --rm bitbetter/bitbetter # create a new image with the patched files -docker build . --tag bitwarden-patch --file "$pwd\src\bitBetter\Dockerfile-bitwarden-patch" +docker build . --tag bitwarden-patched --file "$pwd\src\bitBetter\Dockerfile-bitwarden-patch" # start all user requested instances if (Test-Path -Path "$pwd\.servers\serverlist.txt" -PathType Leaf) { diff --git a/build.sh b/build.sh index 993a82f..c8bb337 100755 --- a/build.sh +++ b/build.sh @@ -1,125 +1,125 @@ -#!/bin/bash -set -e - -# define temporary directory -TEMPDIRECTORY="$PWD/temp" - -# define services to patch -COMPONENTS=("Api" "Identity") - -# delete old directories / files if applicable -if [ -d "$TEMPDIRECTORY" ]; then - rm -rf "$TEMPDIRECTORY" -fi - -if [ -f "$PWD/src/licenseGen/Core.dll" ]; then - rm -f "$PWD/src/licenseGen/Core.dll" -fi - -if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then - rm -f "$PWD/src/licenseGen/cert.pfx" -fi - -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then - rm -f "$PWD/src/bitBetter/cert.cert" -fi - -# generate keys if none are available -if [ ! -d "$PWD/.keys" ]; then - ./generateKeys.sh -fi - -# copy the key to bitBetter -cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" - -# build bitBetter and clean the source directory after -docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" -rm -f "$PWD/src/bitBetter/cert.cert" - -# gather all running instances -OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') - -# stop and remove all running instances -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done - -# update bitwarden itself -if [ "$1" = "update" ]; then - docker pull ghcr.io/bitwarden/self-host:beta -else - read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]] - then - docker pull ghcr.io/bitwarden/self-host:beta - fi -fi - -# stop and remove previous existing patch(ed) container -OLDINSTANCES=$(docker container ps --all -f Ancestor=bitwarden-patch --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done -OLDINSTANCES=$(docker image ls bitwarden-patch --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker image rm $INSTANCE -done - -# remove old extract containers -OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done - -# start a new bitwarden instance so we can patch it -PATCHINSTANCE=$(docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta) - -# create our temporary directory -mkdir $TEMPDIRECTORY - -# extract the files that need to be patched from the services that need to be patched into our temporary directory -for COMPONENT in ${COMPONENTS[@]}; do - mkdir "$TEMPDIRECTORY/$COMPONENT" - docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll" -done - -# stop and remove our temporary container -docker stop bitwarden-extract -docker rm bitwarden-extract - -# run bitBetter, this applies our patches to the required files -docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter - -# create a new image with the patched files -docker build . --tag bitwarden-patch --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" - -# start all user requested instances -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then - sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" - cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do - if [[ $LINE == "#*" ]]; then - bash -c "$LINE" - fi - done -fi - -# remove our bitBetter image -docker image rm bitbetter/bitbetter - -# copy our patched library to the licenseGen source directory -cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" -cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" - -# build the licenseGen -docker build -t bitbetter/licensegen "$PWD/src/licenseGen" - -# clean the licenseGen source directory -rm -f "$PWD/src/licenseGen/Core.dll" -rm -f "$PWD/src/licenseGen/cert.pfx" - -# remove our temporary directory +#!/bin/bash +set -e + +# define temporary directory +TEMPDIRECTORY="$PWD/temp" + +# define services to patch +COMPONENTS=("Api" "Identity") + +# delete old directories / files if applicable +if [ -d "$TEMPDIRECTORY" ]; then + rm -rf "$TEMPDIRECTORY" +fi + +if [ -f "$PWD/src/licenseGen/Core.dll" ]; then + rm -f "$PWD/src/licenseGen/Core.dll" +fi + +if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then + rm -f "$PWD/src/licenseGen/cert.pfx" +fi + +if [ -f "$PWD/src/bitBetter/cert.cert" ]; then + rm -f "$PWD/src/bitBetter/cert.cert" +fi + +# generate keys if none are available +if [ ! -d "$PWD/.keys" ]; then + ./generateKeys.sh +fi + +# copy the key to bitBetter +cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" + +# build bitBetter and clean the source directory after +docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" +rm -f "$PWD/src/bitBetter/cert.cert" + +# gather all running instances +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') + +# stop and remove all running instances +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done + +# update bitwarden itself +if [ "$1" = "update" ]; then + docker pull ghcr.io/bitwarden/self-host:beta +else + read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]] + then + docker pull ghcr.io/bitwarden/self-host:beta + fi +fi + +# stop and remove previous existing patch(ed) container +OLDINSTANCES=$(docker container ps --all -f Ancestor=bitwarden-patched --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done +OLDINSTANCES=$(docker image ls bitwarden-patched --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker image rm $INSTANCE +done + +# remove old extract containers +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done + +# start a new bitwarden instance so we can patch it +PATCHINSTANCE=$(docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta) + +# create our temporary directory +mkdir $TEMPDIRECTORY + +# extract the files that need to be patched from the services that need to be patched into our temporary directory +for COMPONENT in ${COMPONENTS[@]}; do + mkdir "$TEMPDIRECTORY/$COMPONENT" + docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll" +done + +# stop and remove our temporary container +docker stop bitwarden-extract +docker rm bitwarden-extract + +# run bitBetter, this applies our patches to the required files +docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter + +# create a new image with the patched files +docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" + +# start all user requested instances +if [ -f "$PWD/src/bitBetter/cert.cert" ]; then + sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" + cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do + if [[ $LINE == "#*" ]]; then + bash -c "$LINE" + fi + done +fi + +# remove our bitBetter image +docker image rm bitbetter/bitbetter + +# copy our patched library to the licenseGen source directory +cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" +cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" + +# build the licenseGen +docker build -t bitbetter/licensegen "$PWD/src/licenseGen" + +# clean the licenseGen source directory +rm -f "$PWD/src/licenseGen/Core.dll" +rm -f "$PWD/src/licenseGen/cert.pfx" + +# remove our temporary directory rm -rf "$TEMPDIRECTORY" \ No newline at end of file From 5cff265a2a03d64a8f3825995337bd238d741a04 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:16:47 +0200 Subject: [PATCH 30/50] Fix line endings --- build.sh | 248 +++++++++++++++++++++++++++---------------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/build.sh b/build.sh index c8bb337..fa43fad 100755 --- a/build.sh +++ b/build.sh @@ -1,125 +1,125 @@ -#!/bin/bash -set -e - -# define temporary directory -TEMPDIRECTORY="$PWD/temp" - -# define services to patch -COMPONENTS=("Api" "Identity") - -# delete old directories / files if applicable -if [ -d "$TEMPDIRECTORY" ]; then - rm -rf "$TEMPDIRECTORY" -fi - -if [ -f "$PWD/src/licenseGen/Core.dll" ]; then - rm -f "$PWD/src/licenseGen/Core.dll" -fi - -if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then - rm -f "$PWD/src/licenseGen/cert.pfx" -fi - -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then - rm -f "$PWD/src/bitBetter/cert.cert" -fi - -# generate keys if none are available -if [ ! -d "$PWD/.keys" ]; then - ./generateKeys.sh -fi - -# copy the key to bitBetter -cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" - -# build bitBetter and clean the source directory after -docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" -rm -f "$PWD/src/bitBetter/cert.cert" - -# gather all running instances -OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') - -# stop and remove all running instances -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done - -# update bitwarden itself -if [ "$1" = "update" ]; then - docker pull ghcr.io/bitwarden/self-host:beta -else - read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]] - then - docker pull ghcr.io/bitwarden/self-host:beta - fi -fi - -# stop and remove previous existing patch(ed) container -OLDINSTANCES=$(docker container ps --all -f Ancestor=bitwarden-patched --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done -OLDINSTANCES=$(docker image ls bitwarden-patched --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker image rm $INSTANCE -done - -# remove old extract containers -OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}') -for INSTANCE in ${OLDINSTANCES[@]}; do - docker stop $INSTANCE - docker rm $INSTANCE -done - -# start a new bitwarden instance so we can patch it -PATCHINSTANCE=$(docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta) - -# create our temporary directory -mkdir $TEMPDIRECTORY - -# extract the files that need to be patched from the services that need to be patched into our temporary directory -for COMPONENT in ${COMPONENTS[@]}; do - mkdir "$TEMPDIRECTORY/$COMPONENT" - docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll" -done - -# stop and remove our temporary container -docker stop bitwarden-extract -docker rm bitwarden-extract - -# run bitBetter, this applies our patches to the required files -docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter - -# create a new image with the patched files -docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" - -# start all user requested instances -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then - sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" - cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do - if [[ $LINE == "#*" ]]; then - bash -c "$LINE" - fi - done -fi - -# remove our bitBetter image -docker image rm bitbetter/bitbetter - -# copy our patched library to the licenseGen source directory -cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" -cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" - -# build the licenseGen -docker build -t bitbetter/licensegen "$PWD/src/licenseGen" - -# clean the licenseGen source directory -rm -f "$PWD/src/licenseGen/Core.dll" -rm -f "$PWD/src/licenseGen/cert.pfx" - -# remove our temporary directory +#!/bin/bash +set -e + +# define temporary directory +TEMPDIRECTORY="$PWD/temp" + +# define services to patch +COMPONENTS=("Api" "Identity") + +# delete old directories / files if applicable +if [ -d "$TEMPDIRECTORY" ]; then + rm -rf "$TEMPDIRECTORY" +fi + +if [ -f "$PWD/src/licenseGen/Core.dll" ]; then + rm -f "$PWD/src/licenseGen/Core.dll" +fi + +if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then + rm -f "$PWD/src/licenseGen/cert.pfx" +fi + +if [ -f "$PWD/src/bitBetter/cert.cert" ]; then + rm -f "$PWD/src/bitBetter/cert.cert" +fi + +# generate keys if none are available +if [ ! -d "$PWD/.keys" ]; then + ./generateKeys.sh +fi + +# copy the key to bitBetter +cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" + +# build bitBetter and clean the source directory after +docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" +rm -f "$PWD/src/bitBetter/cert.cert" + +# gather all running instances +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') + +# stop and remove all running instances +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done + +# update bitwarden itself +if [ "$1" = "update" ]; then + docker pull ghcr.io/bitwarden/self-host:beta +else + read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]] + then + docker pull ghcr.io/bitwarden/self-host:beta + fi +fi + +# stop and remove previous existing patch(ed) container +OLDINSTANCES=$(docker container ps --all -f Ancestor=bitwarden-patched --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done +OLDINSTANCES=$(docker image ls bitwarden-patched --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker image rm $INSTANCE +done + +# remove old extract containers +OLDINSTANCES=$(docker container ps --all -f Name=bitwarden-extract --format '{{.ID}}') +for INSTANCE in ${OLDINSTANCES[@]}; do + docker stop $INSTANCE + docker rm $INSTANCE +done + +# start a new bitwarden instance so we can patch it +PATCHINSTANCE=$(docker run -d --name bitwarden-extract ghcr.io/bitwarden/self-host:beta) + +# create our temporary directory +mkdir $TEMPDIRECTORY + +# extract the files that need to be patched from the services that need to be patched into our temporary directory +for COMPONENT in ${COMPONENTS[@]}; do + mkdir "$TEMPDIRECTORY/$COMPONENT" + docker cp $PATCHINSTANCE:/app/$COMPONENT/Core.dll "$TEMPDIRECTORY/$COMPONENT/Core.dll" +done + +# stop and remove our temporary container +docker stop bitwarden-extract +docker rm bitwarden-extract + +# run bitBetter, this applies our patches to the required files +docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter + +# create a new image with the patched files +docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" + +# start all user requested instances +if [ -f "$PWD/src/bitBetter/cert.cert" ]; then + sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" + cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do + if [[ $LINE == "#*" ]]; then + bash -c "$LINE" + fi + done +fi + +# remove our bitBetter image +docker image rm bitbetter/bitbetter + +# copy our patched library to the licenseGen source directory +cp -f "$TEMPDIRECTORY/Identity/Core.dll" "$PWD/src/licenseGen" +cp -f "$PWD/.keys/cert.pfx" "$PWD/src/licenseGen" + +# build the licenseGen +docker build -t bitbetter/licensegen "$PWD/src/licenseGen" + +# clean the licenseGen source directory +rm -f "$PWD/src/licenseGen/Core.dll" +rm -f "$PWD/src/licenseGen/cert.pfx" + +# remove our temporary directory rm -rf "$TEMPDIRECTORY" \ No newline at end of file From 1d3bbbcd92003702ea985db67c164f543d1bdd78 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:21:20 +0200 Subject: [PATCH 31/50] Fix typo --- build.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index 4d60b6b..407f3fd 100644 --- a/build.ps1 +++ b/build.ps1 @@ -45,11 +45,11 @@ foreach ($instance in $oldinstances) { } # update bitwarden itself -if ($args[0] -eq 'y') { +if ($args[0] -eq 'update') { docker pull ghcr.io/bitwarden/self-host:beta } else { $confirmation = Read-Host "Update (or get) bitwarden source container (y/n)" - if ($confirmation -eq 'update') { + if ($confirmation -eq 'y') { docker pull ghcr.io/bitwarden/self-host:beta } } From 4341ad3beba2531e8cb9ce4b7ca356df3ee118cc Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:22:15 +0200 Subject: [PATCH 32/50] Add comment --- build.ps1 | 2 +- build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index 407f3fd..e86a0b2 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,7 +35,7 @@ Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter" docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter" Remove-Item "$pwd\src\bitBetter\cert.cert" -Force -# gather all running instances +# gather all running instances, cannot run a wildcard filter on Ancestor= :( $oldinstances = docker container ps --all -f Name=bitwarden --format '{{.ID}}' # stop and remove all running instances diff --git a/build.sh b/build.sh index fa43fad..4282079 100755 --- a/build.sh +++ b/build.sh @@ -36,7 +36,7 @@ cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" rm -f "$PWD/src/bitBetter/cert.cert" -# gather all running instances +# gather all running instances, cannot run a wildcard filter on Ancestor= :( OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') # stop and remove all running instances From ca2815411f5ae6e3dbd72f36cd73b9f47e33839a Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:22:48 +0200 Subject: [PATCH 33/50] Fix tabs --- src/licenseGen/Program.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index ed22f7b..3902e6c 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -431,8 +431,8 @@ internal class Program Set(type, license, "BillingEmail", email); Set(type, license, "BusinessName", String.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); Set(type, license, "Enabled", true); - Set(type, license, "Plan", "Enterprise (Annually)"); - Set(type, license, "PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); + Set(type, license, "Plan", "Enterprise (Annually)"); + Set(type, license, "PlanType", Enum.Parse(planTypeEnum, "EnterpriseAnnually")); Set(type, license, "Seats", Int32.MaxValue); Set(type, license, "MaxCollections", Int16.MaxValue); Set(type, license, "UsePolicies", true); @@ -446,7 +446,7 @@ internal class Program Set(type, license, "Use2fa", true); Set(type, license, "UseApi", true); Set(type, license, "UseResetPassword", true); - Set(type, license, "UseCustomPermissions", true); + Set(type, license, "UseCustomPermissions", true); Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); Set(type, license, "SelfHost", true); Set(type, license, "UsersGetPremium", true); @@ -460,12 +460,12 @@ internal class Program Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); Set(type, license, "Trial", false); Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); - Set(type, license, "LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now - Set(type, license, "AllowAdminAccessToAllCollectionItems", true); - Set(type, license, "UseRiskInsights", true); - Set(type, license, "UseOrganizationDomains", true); - Set(type, license, "UseAdminSponsoredFamilies", true); - Set(type, license, "UseRiskInsights", true); + Set(type, license, "LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now + Set(type, license, "AllowAdminAccessToAllCollectionItems", true); + Set(type, license, "UseRiskInsights", true); + Set(type, license, "UseOrganizationDomains", true); + Set(type, license, "UseAdminSponsoredFamilies", true); + Set(type, license, "UseRiskInsights", true); Set(type, license, "UseOrganizationDomains", true); Set(type, license, "UseAdminSponsoredFamilies", true); Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); From 6fbcf13b7ff18b5c33a20fe061fe30086c9a8522 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 16:40:24 +0200 Subject: [PATCH 34/50] Cleanup org license --- src/licenseGen/Program.cs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 3902e6c..6c9c327 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -446,26 +446,24 @@ internal class Program Set(type, license, "Use2fa", true); Set(type, license, "UseApi", true); Set(type, license, "UseResetPassword", true); - Set(type, license, "UseCustomPermissions", true); Set(type, license, "MaxStorageGb", storage == 0 ? Int16.MaxValue : storage); Set(type, license, "SelfHost", true); - Set(type, license, "UsersGetPremium", true); + Set(type, license, "UsersGetPremium", true); + Set(type, license, "UseCustomPermissions", true); + Set(type, license, "Version", 16); + Set(type, license, "Issued", DateTime.UtcNow); + Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); + Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "ExpirationWithoutGracePeriod", DateTime.UtcNow.AddYears(100)); Set(type, license, "UsePasswordManager", true); Set(type, license, "UseSecretsManager", true); Set(type, license, "SmSeats", Int32.MaxValue); Set(type, license, "SmServiceAccounts", Int32.MaxValue); - Set(type, license, "Version", 15); //This is set to 15 to use AllowAdminAccessToAllCollectionItems can be changed to 13 to just use Secrets Manager - Set(type, license, "Issued", DateTime.UtcNow); - Set(type, license, "Refresh", DateTime.UtcNow.AddYears(100).AddMonths(-1)); - Set(type, license, "Expires", DateTime.UtcNow.AddYears(100)); + Set(type, license, "UseRiskInsights", true); + Set(type, license, "LimitCollectionCreationDeletion", true); + Set(type, license, "AllowAdminAccessToAllCollectionItems", true); Set(type, license, "Trial", false); Set(type, license, "LicenseType", Enum.Parse(licenseTypeEnum, "Organization")); - Set(type, license, "LimitCollectionCreationDeletion", true); //This will be used in the new version of BitWarden but can be applied now - Set(type, license, "AllowAdminAccessToAllCollectionItems", true); - Set(type, license, "UseRiskInsights", true); - Set(type, license, "UseOrganizationDomains", true); - Set(type, license, "UseAdminSponsoredFamilies", true); - Set(type, license, "UseRiskInsights", true); Set(type, license, "UseOrganizationDomains", true); Set(type, license, "UseAdminSponsoredFamilies", true); Set(type, license, "Hash", Convert.ToBase64String((Byte[])computeHash.Invoke(license, [])!)); From 99148f6fafa5929f77c2cffd5a7168f8fd6cb203 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 17:06:19 +0200 Subject: [PATCH 35/50] Use proper file extension --- .gitignore | 2 +- README.md | 4 ++-- build.ps1 | 8 ++++---- build.sh | 10 +++++----- generateKeys.sh | 4 ++-- src/bitBetter/Dockerfile | 2 +- src/bitBetter/Program.cs | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 249b5ef..56566e8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,5 @@ src/bitBetter/.vs/* *.pem .vscode/ *.pfx -*.cert +*.cer *.vsidx diff --git a/README.md b/README.md index 7225b9a..78a56f6 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ If you wish to generate your self-signed cert & key manually, you can run the fo ```bash cd .keys -openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.cert -days 36500 -outform DER -passout pass:test -openssl x509 -inform DER -in cert.cert -out cert.pem +openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.cer -days 36500 -outform DER -passout pass:test +openssl x509 -inform DER -in cert.cer -out cert.pem openssl pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -passin pass:test -passout pass:test ``` diff --git a/build.ps1 b/build.ps1 index e86a0b2..a48cfcd 100644 --- a/build.ps1 +++ b/build.ps1 @@ -19,8 +19,8 @@ if (Test-Path -Path "$pwd\src\licenseGen\cert.pfx" -PathType Leaf) { Remove-Item "$pwd\src\licenseGen\cert.pfx" -Force } -if (Test-Path -Path "$pwd\src\bitBetter\cert.cert" -PathType Leaf) { - Remove-Item "$pwd\src\bitBetter\cert.cert" -Force +if (Test-Path -Path "$pwd\src\bitBetter\cert.cer" -PathType Leaf) { + Remove-Item "$pwd\src\bitBetter\cert.cer" -Force } # generate keys if none are available @@ -29,11 +29,11 @@ if (!(Test-Path "$pwd\.keys")) { } # copy the key to bitBetter -Copy-Item "$pwd\.keys\cert.cert" -Destination "$pwd\src\bitBetter" +Copy-Item "$pwd\.keys\cert.cer" -Destination "$pwd\src\bitBetter" # build bitBetter and clean the source directory after docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter" -Remove-Item "$pwd\src\bitBetter\cert.cert" -Force +Remove-Item "$pwd\src\bitBetter\cert.cer" -Force # gather all running instances, cannot run a wildcard filter on Ancestor= :( $oldinstances = docker container ps --all -f Name=bitwarden --format '{{.ID}}' diff --git a/build.sh b/build.sh index 4282079..1938e79 100755 --- a/build.sh +++ b/build.sh @@ -20,8 +20,8 @@ if [ -f "$PWD/src/licenseGen/cert.pfx" ]; then rm -f "$PWD/src/licenseGen/cert.pfx" fi -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then - rm -f "$PWD/src/bitBetter/cert.cert" +if [ -f "$PWD/src/bitBetter/cert.cer" ]; then + rm -f "$PWD/src/bitBetter/cert.cer" fi # generate keys if none are available @@ -30,11 +30,11 @@ if [ ! -d "$PWD/.keys" ]; then fi # copy the key to bitBetter -cp -f "$PWD/.keys/cert.cert" "$PWD/src/bitBetter" +cp -f "$PWD/.keys/cert.cer" "$PWD/src/bitBetter" # build bitBetter and clean the source directory after docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" -rm -f "$PWD/src/bitBetter/cert.cert" +rm -f "$PWD/src/bitBetter/cert.cer" # gather all running instances, cannot run a wildcard filter on Ancestor= :( OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') @@ -98,7 +98,7 @@ docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" # start all user requested instances -if [ -f "$PWD/src/bitBetter/cert.cert" ]; then +if [ -f "$PWD/src/bitBetter/cert.cer" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do if [[ $LINE == "#*" ]]; then diff --git a/generateKeys.sh b/generateKeys.sh index d561aee..a700ce6 100755 --- a/generateKeys.sh +++ b/generateKeys.sh @@ -15,6 +15,6 @@ fi mkdir "$DIR" # Generate new keys -openssl req -x509 -newkey rsa:4096 -keyout "$DIR/key.pem" -out "$DIR/cert.cert" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test -openssl x509 -inform DER -in "$DIR/cert.cert" -out "$DIR/cert.pem" +openssl req -x509 -newkey rsa:4096 -keyout "$DIR/key.pem" -out "$DIR/cert.cer" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test +openssl x509 -inform DER -in "$DIR/cert.cer" -out "$DIR/cert.pem" openssl pkcs12 -export -out "$DIR/cert.pfx" -inkey "$DIR/key.pem" -in "$DIR/cert.pem" -passin pass:test -passout pass:test diff --git a/src/bitBetter/Dockerfile b/src/bitBetter/Dockerfile index 3077c6a..e93b428 100644 --- a/src/bitBetter/Dockerfile +++ b/src/bitBetter/Dockerfile @@ -2,7 +2,7 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /bitBetter COPY . /bitBetter -COPY cert.cert /app/ +COPY cert.cer /app/ RUN dotnet restore RUN dotnet publish -c Release -o /app --no-restore diff --git a/src/bitBetter/Program.cs b/src/bitBetter/Program.cs index a47fd6b..dec4970 100644 --- a/src/bitBetter/Program.cs +++ b/src/bitBetter/Program.cs @@ -14,7 +14,7 @@ internal class Program { private static Int32 Main() { - const String certFile = "/app/cert.cert"; + const String certFile = "/app/cert.cer"; String[] files = Directory.GetFiles("/app/mount", "Core.dll", SearchOption.AllDirectories); foreach (String file in files) From 70517634a58ab1f4c4051ad3cc58705030051e16 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 17:07:00 +0200 Subject: [PATCH 36/50] Add missing file --- generateKeys.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generateKeys.ps1 b/generateKeys.ps1 index d20aa14..1a82714 100644 --- a/generateKeys.ps1 +++ b/generateKeys.ps1 @@ -20,6 +20,6 @@ if (Test-Path "$pwd\.keys") New-item -ItemType Directory -Path "$pwd\.keys" # generate actual keys -Invoke-Expression "& '$opensslbinary' req -x509 -newkey rsa:4096 -keyout `"$pwd\.keys\key.pem`" -out `"$pwd\.keys\cert.cert`" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test" -Invoke-Expression "& '$opensslbinary' x509 -inform DER -in `"$pwd\.keys\cert.cert`" -out `"$pwd\.keys\cert.pem`"" +Invoke-Expression "& '$opensslbinary' req -x509 -newkey rsa:4096 -keyout `"$pwd\.keys\key.pem`" -out `"$pwd\.keys\cert.cer`" -days 36500 -subj '/CN=www.mydom.com/O=My Company Name LTD./C=US' -outform DER -passout pass:test" +Invoke-Expression "& '$opensslbinary' x509 -inform DER -in `"$pwd\.keys\cert.cer`" -out `"$pwd\.keys\cert.pem`"" Invoke-Expression "& '$opensslbinary' pkcs12 -export -out `"$pwd\.keys\cert.pfx`" -inkey `"$pwd\.keys\key.pem`" -in `"$pwd\.keys\cert.pem`" -passin pass:test -passout pass:test" \ No newline at end of file From 08a6b94d4781445e3bd3306cb95dbd7a02188624 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 12 Aug 2025 19:27:37 +0200 Subject: [PATCH 37/50] Migrate cert.cert if exists --- build.ps1 | 9 +++++++-- build.sh | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index a48cfcd..87aa351 100644 --- a/build.ps1 +++ b/build.ps1 @@ -7,7 +7,7 @@ $tempdirectory = "$pwd\temp" $components = "Api","Identity" # delete old directories / files if applicable -if (Test-Path "$tempdirectory") { +if (Test-Path "$tempdirectory" -PathType Container) { Remove-Item "$tempdirectory" -Recurse -Force } @@ -23,8 +23,13 @@ if (Test-Path -Path "$pwd\src\bitBetter\cert.cer" -PathType Leaf) { Remove-Item "$pwd\src\bitBetter\cert.cer" -Force } +if (Test-Path "$pwd\.keys\cert.cert" -PathType Leaf) { + Rename-Item -Path "$pwd\.keys\cert.cert" -NewName "$pwd\.keys\cert.cer" +} + + # generate keys if none are available -if (!(Test-Path "$pwd\.keys")) { +if (!(Test-Path "$pwd\.keys" -PathType Container)) { .\generateKeys.ps1 } diff --git a/build.sh b/build.sh index 1938e79..3f43b92 100755 --- a/build.sh +++ b/build.sh @@ -24,6 +24,10 @@ if [ -f "$PWD/src/bitBetter/cert.cer" ]; then rm -f "$PWD/src/bitBetter/cert.cer" fi +if [ -f "$PWD/.keys/cert.cert" ]; then + mv "$PWD/.keys/cert.cert" "$PWD/.keys/cert.cer" +fi + # generate keys if none are available if [ ! -d "$PWD/.keys" ]; then ./generateKeys.sh From f059d756f7bdd2f19c566b9983c0daf817413330 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 11:38:09 +0200 Subject: [PATCH 38/50] Check for the correct file --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 3f43b92..4fea7dd 100755 --- a/build.sh +++ b/build.sh @@ -102,7 +102,7 @@ docker run -v "$TEMPDIRECTORY:/app/mount" --rm bitbetter/bitbetter docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bitwarden-patch" # start all user requested instances -if [ -f "$PWD/src/bitBetter/cert.cer" ]; then +if [ -f "$PWD/.servers/serverlist.txt" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do if [[ $LINE == "#*" ]]; then From b233e55a6e6e48f3574ad6ccfdd927a57008cf8d Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 11:40:31 +0200 Subject: [PATCH 39/50] Fix character check --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 4fea7dd..2aeac4c 100755 --- a/build.sh +++ b/build.sh @@ -105,7 +105,7 @@ docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bit if [ -f "$PWD/.servers/serverlist.txt" ]; then sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do - if [[ $LINE == "#*" ]]; then + if [[ $LINE != "#"* ]]; then bash -c "$LINE" fi done From 1d268957fdc05661f11710d8274f50033f7b4767 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 11:42:04 +0200 Subject: [PATCH 40/50] Add comment --- build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sh b/build.sh index 2aeac4c..bf3b0c6 100755 --- a/build.sh +++ b/build.sh @@ -103,6 +103,7 @@ docker build . --tag bitwarden-patched --file "$PWD/src/bitBetter/Dockerfile-bit # start all user requested instances if [ -f "$PWD/.servers/serverlist.txt" ]; then + # convert line endings to unix sed -i 's/\r$//' "$PWD/.servers/serverlist.txt" cat "$PWD/.servers/serverlist.txt" | while read -r LINE; do if [[ $LINE != "#"* ]]; then From 7ca7db3932f288f861015b7e0bf479e7229b3327 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 13:17:25 +0200 Subject: [PATCH 41/50] Add more documentation --- README.md | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 78a56f6..e175b31 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,29 @@ If you ran the build script, you can **simply run the license gen in interactive **The license generator will spit out a JSON-formatted license which can then be used within the Bitwarden web front-end to license your user or org!** +## Migrating from mssql to a real database + +Prepare a new database and bwdata directory, download and prepare the new settings.env (https://raw.githubusercontent.com/bitwarden/self-host/refs/heads/main/docker-unified/settings.env) +Make sure you can get the data from either the backup file or by connecting directly to the mssql database (navicat has a trial). +If required (e.g. you cannot connect to your docker mssql server directly) download Microsoft SQL Server 2022 and SQL Server Management Studio (the latter can be used to import the .bak file) +After cloning this repo and modifying .servers/serverlist.txt to suit your new environment do the following: + +``` +docker exec -i bitwarden-mssql /backup-db.sh +./bitwarden.sh stop +``` + +Run build.sh and ensure your new instance serves a webpage AND has populated the new database with the tables (should be empty now) +Proceed to stop the new container for now. + +Copy from the old to the new bwdata directory (do not copy/overwrite identity.pfx!): + - bwdata/core/licenses to bwdata-new/licenses + - bwdata/core/aspnet-dataprotection to bwdata-new/data-protection + +Export data only from the old sql server database, if needed import the .bak file to a local mssql instance. +Only export tables that have rows, makes it much quicker, .json is the easiest with navicat. +Import the rows to the real database, start the new docker container. + --- # FAQ: Questions you might have. @@ -114,9 +137,20 @@ In the past we have done so but they were not focused on the type of customer th UPDATE: Bitwarden now offers a cheap license called [Families Organization](https://bitwarden.com/pricing/) that provides premium features and the ability to self-host Bitwarden for six persons. +## 2fa doesn't work + +Unfortunately the new BitWarden container doesn't set the timezone and ignores TZ= from the environment, can be fixed by: + +``` +docker exec bitwarden ln -s /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime +``` + +## Changes in settings.env + +Require a recreation of the docker container, build.sh will suffice too. # Footnotes 1This tool builds on top of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script. -2 If you wish to change this you'll need to change the value that `licenseGen/Program.cs` uses for its `GenerateUserLicense` and `GenerateOrgLicense` calls. Remember, this is really unnecessary as this certificate does not represent any type of security-related certificate. +2 If you wish to change this you'll need to change the value that `licenseGen/Program.cs` uses for its `GenerateUserLicense` and `GenerateOrgLicense` calls. Remember, this is really unnecessary as this certificate does not represent any type of security-related certificate. \ No newline at end of file From 2cc20501a0ca0b4c0747a30e1f9b2c2083671aeb Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 13:19:13 +0200 Subject: [PATCH 42/50] Add proper line endings --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e175b31..b1c7ab8 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,11 @@ If you ran the build script, you can **simply run the license gen in interactive ## Migrating from mssql to a real database Prepare a new database and bwdata directory, download and prepare the new settings.env (https://raw.githubusercontent.com/bitwarden/self-host/refs/heads/main/docker-unified/settings.env) + Make sure you can get the data from either the backup file or by connecting directly to the mssql database (navicat has a trial). + If required (e.g. you cannot connect to your docker mssql server directly) download Microsoft SQL Server 2022 and SQL Server Management Studio (the latter can be used to import the .bak file) + After cloning this repo and modifying .servers/serverlist.txt to suit your new environment do the following: ``` @@ -111,6 +114,7 @@ docker exec -i bitwarden-mssql /backup-db.sh ``` Run build.sh and ensure your new instance serves a webpage AND has populated the new database with the tables (should be empty now) + Proceed to stop the new container for now. Copy from the old to the new bwdata directory (do not copy/overwrite identity.pfx!): @@ -118,7 +122,9 @@ Copy from the old to the new bwdata directory (do not copy/overwrite identity.pf - bwdata/core/aspnet-dataprotection to bwdata-new/data-protection Export data only from the old sql server database, if needed import the .bak file to a local mssql instance. + Only export tables that have rows, makes it much quicker, .json is the easiest with navicat. + Import the rows to the real database, start the new docker container. --- From d7430bddd756c4014ab4cb85685cc6d90086e943 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 13:30:57 +0200 Subject: [PATCH 43/50] Add potentially correct line --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b1c7ab8..c44f3cf 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ Proceed to stop the new container for now. Copy from the old to the new bwdata directory (do not copy/overwrite identity.pfx!): - bwdata/core/licenses to bwdata-new/licenses - bwdata/core/aspnet-dataprotection to bwdata-new/data-protection + - bwdata/core/attachments to bwdata-new/attachments (untested for now) Export data only from the old sql server database, if needed import the .bak file to a local mssql instance. From 7007356b9b1ce47cc85bf34fb78f8832b6ca061b Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 14 Aug 2025 17:07:39 +0200 Subject: [PATCH 44/50] Add auto restart --- .servers/serverlist.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.servers/serverlist.txt b/.servers/serverlist.txt index 3d99d6b..c4267b0 100644 --- a/.servers/serverlist.txt +++ b/.servers/serverlist.txt @@ -1,4 +1,4 @@ # Uncomment a line below and fill in the missing values or add your own. Every line in this file will be called by build.[sh|ps1] once the patched image is built. -# docker run -d --name bitwarden -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patched +# docker run -d --name bitwarden --restart=always -v \logs:/var/log/bitwarden -v \bwdata:/etc/bitwarden -p 80:8080 --env-file \settings.env bitwarden-patched # # docker-compose -f /docker-compose.yml up -d From 29883a60f602a9c42161e369b2a9f4108cfc9af0 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Thu, 21 Aug 2025 10:38:03 +0200 Subject: [PATCH 45/50] Update comment --- build.ps1 | 3 +-- build.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.ps1 b/build.ps1 index 87aa351..b5e288e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -27,7 +27,6 @@ if (Test-Path "$pwd\.keys\cert.cert" -PathType Leaf) { Rename-Item -Path "$pwd\.keys\cert.cert" -NewName "$pwd\.keys\cert.cer" } - # generate keys if none are available if (!(Test-Path "$pwd\.keys" -PathType Container)) { .\generateKeys.ps1 @@ -40,7 +39,7 @@ Copy-Item "$pwd\.keys\cert.cer" -Destination "$pwd\src\bitBetter" docker build --no-cache -t bitbetter/bitbetter "$pwd\src\bitBetter" Remove-Item "$pwd\src\bitBetter\cert.cer" -Force -# gather all running instances, cannot run a wildcard filter on Ancestor= :( +# gather all running instances, cannot run a wildcard filter on Ancestor= :(, does find all where name = *bitwarden* $oldinstances = docker container ps --all -f Name=bitwarden --format '{{.ID}}' # stop and remove all running instances diff --git a/build.sh b/build.sh index bf3b0c6..3c58d72 100755 --- a/build.sh +++ b/build.sh @@ -40,7 +40,7 @@ cp -f "$PWD/.keys/cert.cer" "$PWD/src/bitBetter" docker build --no-cache -t bitbetter/bitbetter "$PWD/src/bitBetter" rm -f "$PWD/src/bitBetter/cert.cer" -# gather all running instances, cannot run a wildcard filter on Ancestor= :( +# gather all running instances, cannot run a wildcard filter on Ancestor= :(, does find all where name = *bitwarden* OLDINSTANCES=$(docker container ps --all -f Name=bitwarden --format '{{.ID}}') # stop and remove all running instances From a6516572f6e2557469d216b55a3b5fb064d0b694 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Fri, 22 Aug 2025 10:41:30 +0200 Subject: [PATCH 46/50] Improve consistency between bash and powerhell --- build.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.sh b/build.sh index 3c58d72..0b819bb 100755 --- a/build.sh +++ b/build.sh @@ -53,10 +53,8 @@ done if [ "$1" = "update" ]; then docker pull ghcr.io/bitwarden/self-host:beta else - read -p "Update (or get) bitwarden source container (y/n): " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]] - then + read -p "Update (or get) bitwarden source container (y/n): " + if [[ $REPLY =~ ^[Yy]$ ]]; then docker pull ghcr.io/bitwarden/self-host:beta fi fi From be9d7396dc9349c9efd079620385e0d865302410 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Mon, 25 Aug 2025 10:59:24 +0200 Subject: [PATCH 47/50] Update documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c44f3cf..f147d80 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Proceed to stop the new container for now. Copy from the old to the new bwdata directory (do not copy/overwrite identity.pfx!): - bwdata/core/licenses to bwdata-new/licenses - bwdata/core/aspnet-dataprotection to bwdata-new/data-protection - - bwdata/core/attachments to bwdata-new/attachments (untested for now) + - bwdata/core/attachments to bwdata-new/attachments Export data only from the old sql server database, if needed import the .bak file to a local mssql instance. From 594d1bd4ab085e8cf057a7c3b8624b631d89f3cc Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 26 Aug 2025 14:29:39 +0200 Subject: [PATCH 48/50] Detect buildx --- build.ps1 | 3 +++ build.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/build.ps1 b/build.ps1 index b5e288e..5b2bb3a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,6 +1,9 @@ $ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = $true +# detect buildx, ErrorActionPreference will ensure the script stops execution if not found +docker buildx version + # define temporary directory $tempdirectory = "$pwd\temp" # define services to patch diff --git a/build.sh b/build.sh index 0b819bb..0b7938d 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +# detect buildx, set -e will ensure the script stops execution if not found +docker buildx version + # define temporary directory TEMPDIRECTORY="$PWD/temp" From dca67ac781285b52edfa7c22900cea3b9c8e1399 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Sun, 14 Sep 2025 09:52:41 +0200 Subject: [PATCH 49/50] Fix spelling mistake --- src/licenseGen/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 6c9c327..393b03f 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -11,7 +11,7 @@ namespace licenseGen; internal class Program { private static readonly CommandLineApplication App = new(); - private static readonly CommandOption Cert = App.Option("--cert", "Certifcate file", CommandOptionType.SingleValue); + private static readonly CommandOption Cert = App.Option("--cert", "Certificate file", CommandOptionType.SingleValue); private static readonly CommandOption CoreDll = App.Option("--core", "Path to Core.dll", CommandOptionType.SingleValue); private static Int32 Main(String[] args) From 0e34e638b0429153be5778bc1e39bf6100e1fae6 Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Sun, 14 Sep 2025 09:56:00 +0200 Subject: [PATCH 50/50] Fix check order and improve verbosity --- src/licenseGen/Program.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 393b03f..a417af7 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -269,23 +269,30 @@ internal class Program private static void Check() { - if (!File.Exists(Cert.Value())) + if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value())) + { + App.Error.WriteLine("No certificate specified"); + App.ShowHelp(); + Environment.Exit(1); + } + else if (CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) + { + App.Error.WriteLine("No core dll specified"); + App.ShowHelp(); + Environment.Exit(1); + } + else if (!File.Exists(Cert.Value())) { App.Error.WriteLine($"Can't find certificate at: {Cert.Value()}"); App.ShowHelp(); Environment.Exit(1); } - if (!File.Exists(CoreDll.Value())) + else if (!File.Exists(CoreDll.Value())) { App.Error.WriteLine($"Can't find core dll at: {CoreDll.Value()}"); App.ShowHelp(); Environment.Exit(1); } - if (Cert == null || String.IsNullOrWhiteSpace(Cert.Value()) || CoreDll == null || String.IsNullOrWhiteSpace(CoreDll.Value())) - { - App.ShowHelp(); - Environment.Exit(1); - } } // checkUsername Checks that the username is a valid username