diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b0d3b1fd..2f7b5adfd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -403,11 +403,12 @@ jobs: - name: Restore run: dotnet tool restore - - name: Make Docker stub + - name: Make Docker stubs if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' run: | + # Set proper image based on branch if [[ "${{ github.ref }}" == "rc" ]]; then SETUP_IMAGE="bitwarden/setup:rc" elif [[ "${{ github.ref }}" == "hotfix-rc" ]]; then @@ -417,32 +418,65 @@ jobs: fi STUB_OUTPUT=$(pwd)/docker-stub - docker run -i --rm --name setup -v $STUB_OUTPUT:/bitwarden $SETUP_IMAGE \ - dotnet Setup.dll -stub 1 -install 1 -domain bitwarden.example.com -os lin + + # Run setup + docker run -i --rm --name setup -v $STUB_OUTPUT/US:/bitwarden $SETUP_IMAGE \ + dotnet Setup.dll -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region US + docker run -i --rm --name setup -v $STUB_OUTPUT/EU:/bitwarden $SETUP_IMAGE \ + dotnet Setup.dll -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region EU + sudo chown -R $(whoami):$(whoami) $STUB_OUTPUT - rm -rf $STUB_OUTPUT/letsencrypt - rm $STUB_OUTPUT/env/uid.env $STUB_OUTPUT/config.yml - touch $STUB_OUTPUT/env/uid.env - cd docker-stub; zip -r ../docker-stub.zip *; cd .. - - name: Make Docker stub checksum + Remove extra directories and files + rm -rf $STUB_OUTPUT/US/letsencrypt + rm -rf $STUB_OUTPUT/EU/letsencrypt + rm $STUB_OUTPUT/US/env/uid.env $STUB_OUTPUT/US/config.yml + rm $STUB_OUTPUT/EU/env/uid.env $STUB_OUTPUT/EU/config.yml + + # Create uid environment files + touch $STUB_OUTPUT/US/env/uid.env + touch $STUB_OUTPUT/EU/env/uid.env + + # Zip up the Docker stub files + cd docker-stub/US; zip -r ../../docker-stub-US.zip *; cd ../.. + cd docker-stub/EU; zip -r ../../docker-stub-EU.zip *; cd ../.. + + - name: Make Docker stub checksums if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' - run: sha256sum docker-stub.zip > docker-stub-sha256.txt + run: | + sha256sum docker-stub-US.zip > docker-stub-US-sha256.txt + sha256sum docker-stub-EU.zip > docker-stub-EU-sha256.txt - - name: Upload Docker stub artifact + - name: Upload Docker stub US artifact if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: - name: docker-stub.zip - path: docker-stub.zip + name: docker-stub-US.zip + path: docker-stub-US.zip if-no-files-found: error - - name: Upload Docker stub checksum artifact + - name: Upload Docker stub EU artifact if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: - name: docker-stub-sha256.txt - path: docker-stub-sha256.txt + name: docker-stub-EU.zip + path: docker-stub-EU.zip + if-no-files-found: error + + - name: Upload Docker stub US checksum artifact + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + with: + name: docker-stub-US-sha256.txt + path: docker-stub-US-sha256.txt + if-no-files-found: error + + - name: Upload Docker stub EU checksum artifact + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc' + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + with: + name: docker-stub-EU-sha256.txt + path: docker-stub-EU-sha256.txt if-no-files-found: error - name: Build Swagger diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4cf8a0a7..8b17d4daa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -338,34 +338,40 @@ jobs: - setup - deploy steps: - - name: Download latest Release docker-stub + - name: Download latest Release Docker Stubs if: ${{ github.event.inputs.release_type != 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710 with: workflow: build.yml workflow_conclusion: success branch: ${{ needs.setup.outputs.branch-name }} - artifacts: "docker-stub.zip, - docker-stub-sha256.txt, + artifacts: "docker-stub-US.zip, + docker-stub-US-sha256.txt, + docker-stub-EU.zip, + docker-stub-EU-sha256.txt, swagger.json" - - name: Download latest Release docker-stub + - name: Download latest Release Docker Stubs if: ${{ github.event.inputs.release_type == 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@74f4ac01c9abe0a7331c9a5de822a558fd4a4710 with: workflow: build.yml workflow_conclusion: success branch: master - artifacts: "docker-stub.zip, - docker-stub-sha256.txt, + artifacts: "docker-stub-US.zip, + docker-stub-US-sha256.txt, + docker-stub-EU.zip, + docker-stub-EU-sha256.txt, swagger.json" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 with: - artifacts: "docker-stub.zip, - docker-stub-sha256.txt, + artifacts: "docker-stub-US.zip, + docker-stub-US-sha256.txt, + docker-stub-EU.zip, + docker-stub-EU-sha256.txt, swagger.json" commit: ${{ github.sha }} tag: "v${{ needs.setup.outputs.release_version }}" diff --git a/util/Setup/Context.cs b/util/Setup/Context.cs index 251c61f62..ca9087b5d 100644 --- a/util/Setup/Context.cs +++ b/util/Setup/Context.cs @@ -1,4 +1,5 @@ -using YamlDotNet.Serialization; +using Bit.Setup.Enums; +using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; namespace Bit.Setup; @@ -183,6 +184,7 @@ public class Context { public Guid InstallationId { get; set; } public string InstallationKey { get; set; } + public CloudRegion CloudRegion { get; set; } public bool DiffieHellman { get; set; } public bool Trusted { get; set; } public bool SelfSignedCert { get; set; } diff --git a/util/Setup/Enums/CloudRegion.cs b/util/Setup/Enums/CloudRegion.cs new file mode 100644 index 000000000..6c5f57281 --- /dev/null +++ b/util/Setup/Enums/CloudRegion.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Bit.Setup.Enums; + +public enum CloudRegion +{ + [Display(Name = "US")] + US = 0, + [Display(Name = "EU")] + EU = 1, +} diff --git a/util/Setup/EnvironmentFileBuilder.cs b/util/Setup/EnvironmentFileBuilder.cs index 5babc6ca8..a57013a6d 100644 --- a/util/Setup/EnvironmentFileBuilder.cs +++ b/util/Setup/EnvironmentFileBuilder.cs @@ -73,7 +73,7 @@ public class EnvironmentFileBuilder _globalOverrideValues = new Dictionary { ["globalSettings__baseServiceUri__vault"] = _context.Config.Url, - ["globalSettings__baseServiceUri__cloudVaultRegion"] = "US", + ["globalSettings__baseServiceUri__cloudRegion"] = _context.Install?.CloudRegion.ToString(), ["globalSettings__sqlServer__connectionString"] = $"\"{dbConnectionString.Replace("\"", "\\\"")}\"", ["globalSettings__identityServer__certificatePassword"] = _context.Install?.IdentityCertPassword, ["globalSettings__internalIdentityKey"] = _context.Stub ? "RANDOM_IDENTITY_KEY" : diff --git a/util/Setup/Helpers.cs b/util/Setup/Helpers.cs index ea7351b98..d5c0417de 100644 --- a/util/Setup/Helpers.cs +++ b/util/Setup/Helpers.cs @@ -148,7 +148,7 @@ public static class Helpers Console.Write(prompt); if (prompt.EndsWith("?")) { - Console.Write(" (y/n)"); + Console.Write(" (y/N)"); } Console.Write(": "); var input = Console.ReadLine(); @@ -222,4 +222,13 @@ public static class Helpers Console.WriteLine(); } } + + public static void WriteError(string errorMessage) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.Write("(!) "); + Console.ResetColor(); + Console.Write(errorMessage); + Console.WriteLine(); + } } diff --git a/util/Setup/Program.cs b/util/Setup/Program.cs index cd95ee174..e6eae0749 100644 --- a/util/Setup/Program.cs +++ b/util/Setup/Program.cs @@ -1,6 +1,7 @@ using System.Globalization; using System.Net.Http.Json; using Bit.Migrator; +using Bit.Setup.Enums; namespace Bit.Setup; @@ -196,6 +197,7 @@ public class Program { var installationId = string.Empty; var installationKey = string.Empty; + CloudRegion cloudRegion; if (_context.Parameters.ContainsKey("install-id")) { @@ -203,7 +205,13 @@ public class Program } else { - installationId = Helpers.ReadInput("Enter your installation id (get at https://bitwarden.com/host)"); + var prompt = "Enter your installation id (get at https://bitwarden.com/host)"; + installationId = Helpers.ReadInput(prompt); + while (string.IsNullOrEmpty(installationId)) + { + Helpers.WriteError("Invalid input for installation id. Please try again."); + installationId = Helpers.ReadInput(prompt); + } } if (!Guid.TryParse(installationId.Trim(), out var installationidGuid)) @@ -218,26 +226,61 @@ public class Program } else { - installationKey = Helpers.ReadInput("Enter your installation key"); + var prompt = "Enter your installation key"; + installationKey = Helpers.ReadInput(prompt); + while (string.IsNullOrEmpty(installationKey)) + { + Helpers.WriteError("Invalid input for installation key. Please try again."); + installationKey = Helpers.ReadInput(prompt); + } + } + + if (_context.Parameters.ContainsKey("cloud-region")) + { + Enum.TryParse(_context.Parameters["cloud-region"], out cloudRegion); + } + else + { + var prompt = "Enter your region (US/EU) [US]"; + var region = Helpers.ReadInput(prompt); + if (string.IsNullOrEmpty(region)) region = "US"; + + while (!Enum.TryParse(region, out cloudRegion)) + { + Helpers.WriteError("Invalid input for region. Please try again."); + region = Helpers.ReadInput(prompt); + if (string.IsNullOrEmpty(region)) region = "US"; + } } _context.Install.InstallationId = installationidGuid; _context.Install.InstallationKey = installationKey; + _context.Install.CloudRegion = cloudRegion; try { - var response = new HttpClient().GetAsync("https://api.bitwarden.com/installations/" + - _context.Install.InstallationId).GetAwaiter().GetResult(); + string url; + switch (cloudRegion) + { + case CloudRegion.EU: + url = "https://api.bitwarden.eu/installations/"; + break; + case CloudRegion.US: + default: + url = "https://api.bitwarden.com/installations/"; + break; + } + var response = new HttpClient().GetAsync(url + _context.Install.InstallationId).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (response.StatusCode == System.Net.HttpStatusCode.NotFound) { - Console.WriteLine("Invalid installation id."); + Console.WriteLine($"Invalid installation id for {cloudRegion.ToString()} region."); } else { - Console.WriteLine("Unable to validate installation id."); + Console.WriteLine($"Unable to validate installation id for {cloudRegion.ToString()} region."); } return false; @@ -246,7 +289,7 @@ public class Program var result = response.Content.ReadFromJsonAsync().GetAwaiter().GetResult(); if (!result.Enabled) { - Console.WriteLine("Installation id has been disabled."); + Console.WriteLine($"Installation id has been disabled in the {cloudRegion.ToString()} region."); return false; } @@ -254,7 +297,7 @@ public class Program } catch { - Console.WriteLine("Unable to validate installation id. Problem contacting Bitwarden server."); + Console.WriteLine($"Unable to validate installation id. Problem contacting Bitwarden {cloudRegion.ToString()} server."); return false; } }