From a97f6f3e49dea911bb79ee348df42ff77f15db7d Mon Sep 17 00:00:00 2001 From: Michiel Hazelhof Date: Tue, 15 Jul 2025 10:52:18 +0200 Subject: [PATCH 01/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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/37] 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