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.
This commit is contained in:
Jeff Alyanak 2019-06-05 14:19:39 -04:00 committed by GitHub
parent 15371c362c
commit 6c8789cd67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 205 additions and 116 deletions

View File

@ -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`<br>with `#dockerComposePull`
You can now start or restart Bitwarden as normal and the modified api will be used. <b>It is now ready to accept self-issued licenses.</b>
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`.<sup>[1](#f1)</sup>
> Note that the password here must be `test`.<sup>[1](#f1)</sup>
---
@ -82,22 +82,41 @@ Note that the password here must be `test`.<sup>[1](#f1)</sup>
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.<sup>[2](#f2)</sup>
First, from the `BitBetter` directory, **build the license generator**.<sup>[2](#f2)</sup>
```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 <b>GUID</b> in order to generate an <b>invididual license</b> and the server's <b>install ID</b> to generate an <b>Organization license</b>. 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
```
<b>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!</b>
**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
<a name="#f1"><sup>1</sup></a> 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.
<a name="#f2"><sup>2</sup></a>This tool build ontop of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script.
<a name="#f2"><sup>2</sup></a>This tool build ontop of the `bitbetter/api` container image so make sure you've built that above using the root `./build.sh` script.

View File

@ -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"

View File

@ -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);

View File

@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>

View File

@ -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 <ABSOLUTE PATH TO CERT.PFX> [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 "$@"