From 6c8789cd679f5b45a911ca5af03a87e304a2fc37 Mon Sep 17 00:00:00 2001 From: Jeff Alyanak Date: Wed, 5 Jun 2019 14:19:39 -0400 Subject: [PATCH] License Generator Interactive Mode (#23) * Added a Key Generating script To make the keygen process a bit easier I've added a `generate-keys.sh` script that can be found in the `.keys` directory. It will generate the key & cert and bundle them into the required pkcs#12 file. I've updated the readme to include instructions on the script. * Generate bitbetter/identiry container with modified Core.dll Added the generation of a second modified container, bitbetter/identity, which contains the modified dll. Fixes #12. This works on my testing environment but has not gone through extensive testing. I'd recommend a review and cleanup of this commit before it is merged into the develop or master branches. * Updated Docs I've taken the steps written out by @online-stuff and consolidated/organized them into the README. This closes #13. In a future update it might be worth adding a docs/ directory and breaking the readme into several docs that link to one another. * Updated build.sh Build now checks for and creates missing .keys directories. * Added subj to allow for non-interactive use. * Generate keys on build. * Circle-ci needs to gen keys to test build * Generate keys if they don't exist. Don't overwrite if keys already exist. * Generate keys online in the .keys directory * Updated README.md * Added initial interactive options * Functional implementation of licensegen interactive mode. * Bumped Newtonson.Json version Never versions of the dotnet-sdk have issues with older Newtonsoft versions. 12.0.1 seems to satisfy the widest variety of sdk versions. * Removing old readme * Removed Duplicate Section * Fixed typo This fixes and closes issue #24. --- README.md | 49 ++++++--- ReadMeInstall | 95 ------------------ src/licenseGen/Program.cs | 165 ++++++++++++++++++++++++++++++- src/licenseGen/licenseGen.csproj | 2 +- src/licenseGen/run.sh | 10 +- 5 files changed, 205 insertions(+), 116 deletions(-) delete mode 100644 ReadMeInstall diff --git a/README.md b/README.md index 5b98ca6..9c07f7c 100644 --- a/README.md +++ b/README.md @@ -28,28 +28,28 @@ Aside from docker, which you also need for Bitwarden, BitBetter requires the fol With your pre-requisites installed, begin the installation of BitBetter by downloading it through Github or using the git command: ```bash -git clone https://github.com/online-stuff/BitBetter.git +git clone https://github.com/jakeswenson/BitBetter.git ``` First, we need to add the correct version of Newtonsoft.Json to the license generator and the BitBetter docker directories. ```bash cd BitBetter/src/licenseGen/ -dotnet add package Newtonsoft.Json --version 11.0.0 +dotnet add package Newtonsoft.Json --version 12.0.1 cd ../bitBetter -dotnet add package Newtonsoft.Json --version 11.0.0 +dotnet add package Newtonsoft.Json --version 12.0.1 ``` ## Building BitBetter -Now that you've set up your build environment, you can run the main `BitBetter/build.sh` script to generate a modified version of the `bitwarden/api` and `bitwarden/identity` docker images. +Now that you've set up your build environment, you can **run the main build script** to generate a modified version of the `bitwarden/api` and `bitwarden/identity` docker images. From the BitBetter directory, simply run: ```bash ./build.sh ``` -This will create a new self-signed certificate in the `.keys` directory one does not already exist and then create a modified version of the official `bitwarden/api` called `bitbetter/api` and a modified version of the `bitwarden/identity` called `bitbetter/identity`. You may now simply edit your bitwarden docker-compose.yml to utilize the modified image. +This will create a new self-signed certificate in the `.keys` directory one does not already exist and then create a modified version of the official `bitwarden/api` called `bitbetter/api` and a modified version of the `bitwarden/identity` called `bitbetter/identity`. You may **now simply edit your bitwarden docker-compose.yml to utilize the modified image**. Edit your `/path/to/bwdata/docker/docker-compose.yml`. @@ -61,10 +61,10 @@ You'll also want to edit the `/path/to/bwdata/scripts/run.sh` file. In the `func > Replace `dockerComposePull`
with `#dockerComposePull` -You can now start or restart Bitwarden as normal and the modified api will be used. It is now ready to accept self-issued licenses. +You can now start or restart Bitwarden as normal and the modified api will be used. **It is now ready to accept self-issued licenses.** --- -**Note: Manually generating Certificate & Key** +### Note: Manually generating Certificate & Key If you wish to generate your self-signed cert & key manually, you can run the following commands. @@ -74,7 +74,7 @@ openssl x509 -inform DER -in cert.cert -out cert.pem openssl pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -passin pass:test -passout pass:test ``` -Note that the password here must be `test`.[1](#f1) +> Note that the password here must be `test`.[1](#f1) --- @@ -82,22 +82,41 @@ Note that the password here must be `test`.[1](#f1) There is a tool included in the directory `src/licenseGen/` that will generate new individual and organization licenses. These licenses will be accepted by the modified Bitwarden because they will be signed by the certificate you generated in earlier steps. -First, from the `BitBetter/src/licenseGen` directory, build the license generator.[2](#f2) +First, from the `BitBetter` directory, **build the license generator**.[2](#f2) ```bash ./build.sh ``` -Now, from the `BitBetter/src/licenseGen` directory, you can run the tool to generate licenses. +In order to run the tool and generate a license you'll need to get a **user's GUID** in order to generate an **invididual license** or the server's **install ID** to generate an **Organization license**. These can be retrieved most easily through the Bitwarden [Admin Portal](https://help.bitwarden.com/article/admin-portal/). -You'll need to get a user's GUID in order to generate an invididual license and the server's install ID to generate an Organization license. These can be retrieved most easily through the Bitwarden [Admin Portal](https://help.bitwarden.com/article/admin-portal/). +If you generated your keys in the default `BitBetter/.keys` directory, you can **simply run the license gen in interactive mode** from the `Bitbetter` directory and **follow the prompts to generate your license**. ```bash -./run.sh ~/BitBetter/.keys/cert.pfx user "Name" "EMail" "User-GUID" -./run.sh ~/BitBetter/.keys/cert.pfx org "Name" "EMail" "Install-ID used to install the server" +./src/licenseGen/run.sh interactive ``` -The license generator will spit out a JSON-formatted license which can then be used within the Bitwarden web front-end to license your user or org! +**The license generator will spit out a JSON-formatted license which can then be used within the Bitwarden web front-end to license your user or org!** + +--- + +### Note: Alternative Ways to Generate License + +If you wish to run the license gen from a directory aside from the root `BitBetter` one, you'll have to provide the absolute path to your cert.pfx. + +```bash +./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx interactive +``` + +Additional, instead of interactive mode, you can also pass the parameters directly to the command as follows. + +```bash +./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx user "Name" "EMail" "User-GUID" +./src/licenseGen/run.sh /Absolute/Path/To/BitBetter/.keys/cert.pfx org "Name" "EMail" "Install-ID used to install the server" +``` + +--- + # FAQ: Questions (you might have?) @@ -121,4 +140,4 @@ I'm still in the testing/evaluating phase. If I am hosting the server/data, let 1 If you wish to change this you'll need to change the value that `src/licenseGen/Program.cs` uses for it's `GenerateUserLicense` and `GenerateOrgLicense` calls, but this is really unnecessary as this certificate does not represent any type of security issue. -2This tool build ontop of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script. \ No newline at end of file +2This tool build ontop of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script. diff --git a/ReadMeInstall b/ReadMeInstall deleted file mode 100644 index 49265ed..0000000 --- a/ReadMeInstall +++ /dev/null @@ -1,95 +0,0 @@ -These are the commands I used (and a few of my notes) for a minimal Debian Stretch install.. -Software Selection - "SSH server" and 'standard system utilities" only - - - -sudo apt-get update -sudo apt-get install vim vim-doc vim-scripts wget curl git -sudo apt-get dist-upgrade -sudo reboot - ----The next few lines I setup my server authentication and other misc profile settings you can skip until sudo apt-get - -ssh-keygen - -echo "ssh-rsa AA...1Q== " >> .ssh/authorized_keys - -echo ":set mouse=" >> .vimrc -echo "set nocompatible" >> .vimrc - -vi .bashrc - -sudo visudo ---- tom ALL=(ALL) NOPASSWD:ALL - -sudo vi /etc/ssh/sshd_config - Enable 'PermitRootLogin prohibit-password' and 'PasswordAuthentication no' - -sudo apt-get update -sudo apt install apt-transport-https ca-certificates curl gnupg2 software-properties-common -curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - -sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" -sudo apt update -apt-cache policy docker-ce -sudo apt install docker-ce -sudo systemctl status docker -sudo usermod -aG docker ${USER} -exit - -id -nG -docker version -docker info -docker run hello-world - -sudo curl -L --fail https://github.com/docker/compose/releases/download/1.23.1/run.sh -o /usr/local/bin/docker-compose -sudo chmod +x /usr/local/bin/docker-compose - -wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg -sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/ -wget -q https://packages.microsoft.com/config/debian/9/prod.list -sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list -sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg -sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list - -curl -s -o bitwarden.sh https://raw.githubusercontent.com/bitwarden/core/master/scripts/bitwarden.sh && sudo chmod u+x bitwarden.sh -./bitwarden.sh install - -sudo apt-get update -sudo apt-get install dotnet-sdk-2.1 - -git clone https://github.com/online-stuff/BitBetter.git - -cd BitBetter/src/licenseGen/ -dotnet add package Newtonsoft.Json --version 11.0.0 - -cd ~/BitBetter/src/bitBetter -dotnet add package Newtonsoft.Json --version 11.0.0 - -cd ~/BitBetter/.keys -rm * - -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 pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem -passin pass:test -passout pass:test - - -cd ~/BitBetter -./build.sh - -cd src/licenseGen/ -./build.sh - -cd ~ - -vi ~/bwdata/docker/docker-compose.yml - Change image: bitwarden/api:1.26.0 to image: bitbetter/api and image: bitwarden/identity:x.xx.x to image: bitbetter/identity -vi ~/bwdata/env/global.override.env - Enter mail__smtp relay settings -vi ~/bwdata/scripts/run.sh - function restart() { dockerComposePull to #dockerComposePull - -./bitwarden start - -----Server should be up and running.. Create a user account - -cd ~/BitBetter/src/licenseGen/ -./build.sh -./run.sh ~/BitBetter/.keys/cert.pfx user "Name" "EMail" "User-GUID" - Get User-GUID from the admin portal of the BitWarden server - -./run.sh ~/BitBetter/.keys/cert.pfx org "Name" "EMail" "Install-ID used to install the server from bitwarden.com/host" diff --git a/src/licenseGen/Program.cs b/src/licenseGen/Program.cs index 8e0e667..fc1823a 100644 --- a/src/licenseGen/Program.cs +++ b/src/licenseGen/Program.cs @@ -33,6 +33,121 @@ namespace bitwardenSelfLicensor certExists() && coreExists(); } + app.Command("interactive", config => + { + string buff="", licensetype="", name="", email="", businessname=""; + + bool valid_guid = false, valid_installid = false; + Guid guid, installid; + + config.OnExecute(() => + { + if (!verifyTopOptions()) + { + if (!coreExists()) config.Error.WriteLine($"Cant find core dll at: {coreDll.Value()}"); + if (!certExists()) config.Error.WriteLine($"Cant find certificate at: {cert.Value()}"); + + config.ShowHelp(); + return 1; + } + + WriteLine("Interactive license mode..."); + + while (licensetype == "") + { + WriteLine("What would you like to generate, a [u]ser license or an [o]rg license?"); + buff = Console.ReadLine(); + + if(buff == "u") + { + licensetype = "user"; + WriteLineOver("Okay, we will generate a user license."); + + while (valid_guid == false) + { + 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))valid_guid = true; + else WriteLineOver("The user-guid provided does not appear to be valid."); + } + } + else if (buff == "o") + { + licensetype = "org"; + WriteLineOver("Okay, we will generate an organization license."); + + while (valid_installid == false) + { + 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)) valid_installid = true; + else WriteLineOver("The install-id provided does not appear to be valid."); + } + + while (businessname == "") + { + WriteLineOver("Please enter a business name, default is BitBetter. [Business Name]:"); + buff = Console.ReadLine(); + if (buff == "") businessname = "BitBetter"; + else if (checkBusinessName(buff)) businessname = buff; + } + } + else + { + WriteLineOver("Unrecognized option \'" + buff + "\'. "); + } + } + + while (name == "") + { + WriteLineOver("Please provide the username this license will be registered to. [username]:"); + buff = Console.ReadLine(); + if ( checkUsername(buff) ) name = buff; + } + + while (email == "") + { + WriteLineOver("Please provide the email address for the user " + name + ". [email]"); + buff = Console.ReadLine(); + if ( checkEmail(buff) ) email = buff; + } + + + if (licensetype == "user") + { + WriteLineOver("Confirm creation of \"user\" license for username: \"" + name + "\", email: \"" + email + "\", User-GUID: \"" + guid + "\"? Y/n"); + buff = Console.ReadLine(); + if ( buff == "" || buff == "y" || buff == "Y" ) + { + GenerateUserLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, guid, null); + } + else + { + WriteLineOver("Exiting..."); + return 0; + } + } + else if (licensetype == "org") + { + WriteLineOver("Confirm creation of \"organization\" license for business name: \"" + businessname + "\", username: \"" + name + "\", email: \"" + email + "\", Install-ID: \"" + installid + "\"? Y/n"); + buff = Console.ReadLine(); + if ( buff == "" || buff == "y" || buff == "Y" ) + { + GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name, email, installid, businessname, null); + } + else + { + WriteLineOver("Exiting..."); + return 0; + } + } + + return 0; + }); + }); + app.Command("user", config => { var name = config.Argument("Name", "your name"); @@ -82,6 +197,7 @@ namespace bitwardenSelfLicensor var name = config.Argument("Name", "your name"); var email = config.Argument("Email", "your email"); var installId = config.Argument("InstallId", "your installation id (GUID)"); + var businessName = config.Argument("BusinessName", "name For the organization (optional)"); var key = config.Argument("Key", "your key id (optional)"); var help = config.HelpOption("--help | -h | -?"); @@ -118,7 +234,7 @@ namespace bitwardenSelfLicensor return 1; } - GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, installationId, key.Value); + GenerateOrgLicense(new X509Certificate2(cert.Value(), "test"), coreDll.Value(), name.Value, email.Value, installationId, businessName.Value, key.Value); return 0; }); @@ -143,6 +259,49 @@ namespace bitwardenSelfLicensor } } + // checkUsername Checks that the username is a valid username + static bool checkUsername(string s) + { + if ( string.IsNullOrWhiteSpace(s) ) { + WriteLineOver("The username provided doesn't appear to be valid.\n"); + return false; + } + return true; // TODO: Actually validate + } + + // checkBusinessName Checks that the Business Name is a valid username + static bool checkBusinessName(string s) + { + if ( string.IsNullOrWhiteSpace(s) ) { + WriteLineOver("The Business Name provided doesn't appear to be valid.\n"); + return false; + } + return true; // TODO: Actually validate + } + + // checkEmail Checks that the email address is a valid email address + static bool checkEmail(string s) + { + if ( string.IsNullOrWhiteSpace(s) ) { + WriteLineOver("The email provided doesn't appear to be valid.\n"); + return false; + } + return true; // TODO: Actually validate + } + + // WriteLineOver Writes a new line to console over last line. + static void WriteLineOver(string s) + { + Console.SetCursorPosition(0, Console.CursorTop -1); + Console.WriteLine(s); + } + + // WriteLine This wrapper is just here so that console writes all look similar. + static void WriteLine(string s) + { + Console.WriteLine(s); + } + static void GenerateUserLicense(X509Certificate2 cert, string corePath, string userName, string email, Guid userId, string key) { @@ -176,7 +335,7 @@ namespace bitwardenSelfLicensor } static void GenerateOrgLicense(X509Certificate2 cert, string corePath, - string userName, string email, Guid instalId, string key) + string userName, string email, Guid instalId, string businessName, string key) { var core = AssemblyLoadContext.Default.LoadFromAssemblyPath(corePath); @@ -194,7 +353,7 @@ namespace bitwardenSelfLicensor set("Id", Guid.NewGuid()); set("Name", userName); set("BillingEmail", email); - set("BusinessName", "BitBetter"); + set("BusinessName", string.IsNullOrWhiteSpace(businessName) ? "BitBetter" : businessName); set("Enabled", true); set("Plan", "Custom"); set("PlanType", (byte)6); diff --git a/src/licenseGen/licenseGen.csproj b/src/licenseGen/licenseGen.csproj index a24ae32..73abe54 100644 --- a/src/licenseGen/licenseGen.csproj +++ b/src/licenseGen/licenseGen.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/licenseGen/run.sh b/src/licenseGen/run.sh index 3174ade..2219640 100755 --- a/src/licenseGen/run.sh +++ b/src/licenseGen/run.sh @@ -2,11 +2,17 @@ script_dir=`cd $(dirname $0); pwd` +# Grab the absolute path to the default pfx location +cert_path=`cd ./.keys; ls -d -1 $PWD/cert.pfx` + if [ "$#" -lt "1" ]; then echo "USAGE: $0 [License Gen args...]" exit 1 +elif [ "$#" -ge "2" ]; then + # If a cert path is provided manually, override the default + cert_path=$1 + shift fi -cert_path=$1 -shift + docker run -it -v "$cert_path:/cert.pfx" bitbetter/licensegen "$@"