2017-08-19 00:22:25 +02:00
|
|
|
|
using DbUp;
|
2017-08-19 15:33:14 +02:00
|
|
|
|
using Newtonsoft.Json;
|
2017-08-19 00:22:25 +02:00
|
|
|
|
using System;
|
2017-08-07 22:31:00 +02:00
|
|
|
|
using System.Collections.Generic;
|
2017-08-21 15:57:09 +02:00
|
|
|
|
using System.Data.SqlClient;
|
2017-08-19 15:33:14 +02:00
|
|
|
|
using System.Net.Http;
|
2017-08-19 00:22:25 +02:00
|
|
|
|
using System.Reflection;
|
2017-08-07 22:31:00 +02:00
|
|
|
|
|
2017-09-08 17:45:20 +02:00
|
|
|
|
namespace Bit.Setup
|
2017-08-07 22:31:00 +02:00
|
|
|
|
{
|
|
|
|
|
public class Program
|
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
private static Context _context;
|
2017-08-07 22:31:00 +02:00
|
|
|
|
|
|
|
|
|
public static void Main(string[] args)
|
|
|
|
|
{
|
2018-03-30 15:23:33 +02:00
|
|
|
|
Console.WriteLine();
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context = new Context
|
|
|
|
|
{
|
|
|
|
|
Args = args
|
|
|
|
|
};
|
|
|
|
|
ParseParameters();
|
2018-03-30 15:23:33 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("os"))
|
2017-11-06 23:28:02 +01:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.HostOS = _context.Parameters["os"];
|
2017-11-06 23:28:02 +01:00
|
|
|
|
}
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("corev"))
|
2017-11-07 16:54:00 +01:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.CoreVersion = _context.Parameters["corev"];
|
2017-11-07 16:54:00 +01:00
|
|
|
|
}
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("webv"))
|
2017-11-07 16:54:00 +01:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.WebVersion = _context.Parameters["webv"];
|
2017-11-07 16:54:00 +01:00
|
|
|
|
}
|
2017-11-06 23:28:02 +01:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("install"))
|
2017-08-19 00:22:25 +02:00
|
|
|
|
{
|
|
|
|
|
Install();
|
|
|
|
|
}
|
2018-08-30 17:35:44 +02:00
|
|
|
|
else if(_context.Parameters.ContainsKey("update"))
|
2017-08-19 00:22:25 +02:00
|
|
|
|
{
|
|
|
|
|
Update();
|
|
|
|
|
}
|
2018-08-30 17:35:44 +02:00
|
|
|
|
else if(_context.Parameters.ContainsKey("printenv"))
|
2017-08-24 17:16:01 +02:00
|
|
|
|
{
|
|
|
|
|
PrintEnvironment();
|
|
|
|
|
}
|
2017-08-19 00:22:25 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("No top-level command detected. Exiting...");
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-07 22:31:00 +02:00
|
|
|
|
|
2017-08-19 00:22:25 +02:00
|
|
|
|
private static void Install()
|
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("letsencrypt"))
|
2017-08-08 20:35:31 +02:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.Config.SslManagedLetsEncrypt =
|
|
|
|
|
_context.Parameters["letsencrypt"].ToLowerInvariant() == "y";
|
2018-03-29 03:18:10 +02:00
|
|
|
|
}
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("domain"))
|
2018-03-29 03:18:10 +02:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.Install.Domain = _context.Parameters["domain"].ToLowerInvariant();
|
2017-11-20 13:53:38 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(!ValidateInstallation())
|
2018-03-29 03:18:10 +02:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-06 23:28:02 +01:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var certBuilder = new CertBuilder(_context);
|
|
|
|
|
certBuilder.BuildForInstall();
|
2018-08-30 22:09:18 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
// Set the URL
|
|
|
|
|
_context.Config.Url = string.Format("http{0}://{1}",
|
|
|
|
|
_context.Config.Ssl ? "s" : string.Empty, _context.Install.Domain);
|
2017-08-11 14:57:31 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var nginxBuilder = new NginxConfigBuilder(_context);
|
2017-11-08 04:35:36 +01:00
|
|
|
|
nginxBuilder.BuildForInstaller();
|
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var environmentFileBuilder = new EnvironmentFileBuilder(_context);
|
2017-11-07 04:55:15 +01:00
|
|
|
|
environmentFileBuilder.BuildForInstaller();
|
2017-10-24 04:45:59 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var appIdBuilder = new AppIdBuilder(_context);
|
2017-10-24 04:45:59 +02:00
|
|
|
|
appIdBuilder.Build();
|
2017-11-06 23:28:02 +01:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var dockerComposeBuilder = new DockerComposeBuilder(_context);
|
|
|
|
|
dockerComposeBuilder.BuildForInstaller();
|
|
|
|
|
|
|
|
|
|
_context.SaveConfiguration();
|
2018-08-31 15:16:01 +02:00
|
|
|
|
|
|
|
|
|
Console.WriteLine("\nInstallation complete");
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("\nIf you need to make additional configuration changes, you can modify\n" +
|
2018-09-17 21:18:49 +02:00
|
|
|
|
"the settings in `{0}` and then run:\n{1}",
|
2018-08-30 22:09:18 +02:00
|
|
|
|
_context.HostOS == "win" ? ".\\bwdata\\config.yml" : "./bwdata/config.yml",
|
|
|
|
|
_context.HostOS == "win" ? "`.\\bitwarden.ps1 -rebuild` or `.\\bitwarden.ps1 -update`" :
|
|
|
|
|
"`./bitwarden.sh rebuild` or `./bitwarden.sh update`");
|
2018-08-31 15:16:01 +02:00
|
|
|
|
|
|
|
|
|
Console.WriteLine("\nNext steps, run:");
|
|
|
|
|
if(_context.HostOS == "win")
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("`.\\bitwarden.ps1 -start` and then `.\\bitwarden.ps1 -updatedb`");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("`./bitwarden.sh start` and then `./bitwarden.sh updatedb`");
|
|
|
|
|
}
|
|
|
|
|
Console.WriteLine(string.Empty);
|
2017-08-07 22:31:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-19 00:22:25 +02:00
|
|
|
|
private static void Update()
|
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.Parameters.ContainsKey("db"))
|
2017-08-19 00:22:25 +02:00
|
|
|
|
{
|
|
|
|
|
MigrateDatabase();
|
|
|
|
|
}
|
2017-10-24 04:45:59 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RebuildConfigs();
|
|
|
|
|
}
|
2017-08-19 00:22:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-24 17:16:01 +02:00
|
|
|
|
private static void PrintEnvironment()
|
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.LoadConfiguration();
|
2018-02-27 20:16:19 +01:00
|
|
|
|
Console.WriteLine("\nBitwarden is up and running!");
|
2017-08-24 17:35:16 +02:00
|
|
|
|
Console.WriteLine("===================================================");
|
2018-08-30 17:35:44 +02:00
|
|
|
|
Console.WriteLine("\nvisit {0}", _context.Config.Url);
|
2017-11-09 02:54:39 +01:00
|
|
|
|
Console.Write("to update, run ");
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(_context.HostOS == "win")
|
2017-08-24 17:16:01 +02:00
|
|
|
|
{
|
2018-08-30 22:09:18 +02:00
|
|
|
|
Console.Write("`.\\bitwarden.ps1 -updateself` and then `.\\bitwarden.ps1 -update`");
|
2017-08-24 17:16:01 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-08-30 22:09:18 +02:00
|
|
|
|
Console.Write("`./bitwarden.sh updateself` and then `./bitwarden.sh update`");
|
2017-08-24 17:16:01 +02:00
|
|
|
|
}
|
2017-08-24 17:35:16 +02:00
|
|
|
|
Console.WriteLine("\n");
|
2017-08-24 17:16:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 21:12:28 +02:00
|
|
|
|
private static void MigrateDatabase(int attempt = 1)
|
2017-08-19 00:22:25 +02:00
|
|
|
|
{
|
2018-03-27 21:12:28 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Migrating database.");
|
2017-08-19 00:22:25 +02:00
|
|
|
|
|
2018-10-18 17:41:49 +02:00
|
|
|
|
var dbPass = Helpers.GetValueFromEnvFile("mssql", "SA_PASSWORD");
|
2018-03-27 21:12:28 +02:00
|
|
|
|
var masterConnectionString = Helpers.MakeSqlConnectionString(
|
|
|
|
|
"mssql", "master", "sa", dbPass ?? string.Empty);
|
|
|
|
|
var vaultConnectionString = Helpers.MakeSqlConnectionString(
|
|
|
|
|
"mssql", "vault", "sa", dbPass ?? string.Empty);
|
2017-08-21 15:57:09 +02:00
|
|
|
|
|
2018-03-27 21:12:28 +02:00
|
|
|
|
using(var connection = new SqlConnection(masterConnectionString))
|
|
|
|
|
{
|
|
|
|
|
var command = new SqlCommand(
|
|
|
|
|
"IF ((SELECT COUNT(1) FROM sys.databases WHERE [name] = 'vault') = 0) " +
|
|
|
|
|
"CREATE DATABASE [vault];", connection);
|
|
|
|
|
command.Connection.Open();
|
|
|
|
|
command.ExecuteNonQuery();
|
2018-08-09 22:57:15 +02:00
|
|
|
|
command.CommandText = "IF ((SELECT DATABASEPROPERTYEX([name], 'IsAutoClose') " +
|
|
|
|
|
"FROM sys.databases WHERE [name] = 'vault') = 1) " +
|
|
|
|
|
"ALTER DATABASE [vault] SET AUTO_CLOSE OFF;";
|
|
|
|
|
command.ExecuteNonQuery();
|
2018-03-27 21:12:28 +02:00
|
|
|
|
}
|
2017-08-21 15:57:09 +02:00
|
|
|
|
|
2018-03-27 21:12:28 +02:00
|
|
|
|
var upgrader = DeployChanges.To
|
|
|
|
|
.SqlDatabase(vaultConnectionString)
|
|
|
|
|
.JournalToSqlTable("dbo", "Migration")
|
|
|
|
|
.WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(),
|
|
|
|
|
s => s.Contains($".DbScripts.") && !s.Contains(".Archive."))
|
2018-07-19 22:22:30 +02:00
|
|
|
|
.WithTransaction()
|
2018-03-27 21:12:28 +02:00
|
|
|
|
.WithExecutionTimeout(new TimeSpan(0, 5, 0))
|
|
|
|
|
.LogToConsole()
|
|
|
|
|
.Build();
|
2017-08-19 00:22:25 +02:00
|
|
|
|
|
2018-03-27 21:12:28 +02:00
|
|
|
|
var result = upgrader.PerformUpgrade();
|
|
|
|
|
if(result.Successful)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Migration successful.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Migration failed.");
|
|
|
|
|
}
|
2017-08-19 00:22:25 +02:00
|
|
|
|
}
|
2018-03-27 21:12:28 +02:00
|
|
|
|
catch(SqlException e)
|
2017-08-19 00:22:25 +02:00
|
|
|
|
{
|
2018-07-19 22:01:54 +02:00
|
|
|
|
if(e.Message.Contains("Server is in script upgrade mode") && attempt < 10)
|
2018-03-27 21:12:28 +02:00
|
|
|
|
{
|
|
|
|
|
var nextAttempt = attempt + 1;
|
|
|
|
|
Console.WriteLine("Database is in script upgrade mode. " +
|
|
|
|
|
"Trying again (attempt #{0})...", nextAttempt);
|
2018-06-01 04:11:08 +02:00
|
|
|
|
System.Threading.Thread.Sleep(20000);
|
2018-03-27 21:12:28 +02:00
|
|
|
|
MigrateDatabase(nextAttempt);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw e;
|
2017-08-19 00:22:25 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-19 15:33:14 +02:00
|
|
|
|
private static bool ValidateInstallation()
|
|
|
|
|
{
|
2018-03-30 15:40:14 +02:00
|
|
|
|
var installationId = Helpers.ReadInput("Enter your installation id (get at https://bitwarden.com/host)");
|
2018-03-30 15:23:33 +02:00
|
|
|
|
if(!Guid.TryParse(installationId.Trim(), out var installationidGuid))
|
2017-08-19 15:33:14 +02:00
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Invalid installation id.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.Install.InstallationId = installationidGuid;
|
|
|
|
|
_context.Install.InstallationKey = Helpers.ReadInput("Enter your installation key");
|
2017-08-19 15:33:14 +02:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var response = new HttpClient().GetAsync("https://api.bitwarden.com/installations/" +
|
|
|
|
|
_context.Install.InstallationId).GetAwaiter().GetResult();
|
2017-08-19 15:33:14 +02:00
|
|
|
|
|
|
|
|
|
if(!response.IsSuccessStatusCode)
|
|
|
|
|
{
|
|
|
|
|
if(response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Invalid installation id.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Unable to validate installation id.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var resultString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
|
|
|
|
var result = JsonConvert.DeserializeObject<dynamic>(resultString);
|
|
|
|
|
if(!(bool)result.Enabled)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Installation id has been disabled.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2018-02-27 20:16:19 +01:00
|
|
|
|
Console.WriteLine("Unable to validate installation id. Problem contacting Bitwarden server.");
|
2017-08-19 15:33:14 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-24 04:45:59 +02:00
|
|
|
|
private static void RebuildConfigs()
|
2017-08-07 22:31:00 +02:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.LoadConfiguration();
|
2017-11-07 04:55:15 +01:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var environmentFileBuilder = new EnvironmentFileBuilder(_context);
|
|
|
|
|
environmentFileBuilder.BuildForUpdater();
|
2017-08-07 22:31:00 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var nginxBuilder = new NginxConfigBuilder(_context);
|
2017-10-24 04:45:59 +02:00
|
|
|
|
nginxBuilder.BuildForUpdater();
|
2017-08-07 22:31:00 +02:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var appIdBuilder = new AppIdBuilder(_context);
|
2017-10-24 04:45:59 +02:00
|
|
|
|
appIdBuilder.Build();
|
2017-11-06 23:28:02 +01:00
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
var dockerComposeBuilder = new DockerComposeBuilder(_context);
|
2017-11-06 23:28:02 +01:00
|
|
|
|
dockerComposeBuilder.BuildForUpdater();
|
2018-08-30 17:35:44 +02:00
|
|
|
|
|
|
|
|
|
_context.SaveConfiguration();
|
2018-08-31 04:30:31 +02:00
|
|
|
|
Console.WriteLine(string.Empty);
|
2017-08-08 18:29:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
private static void ParseParameters()
|
2017-08-07 22:31:00 +02:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.Parameters = new Dictionary<string, string>();
|
|
|
|
|
for(var i = 0; i < _context.Args.Length; i = i + 2)
|
2017-08-07 22:31:00 +02:00
|
|
|
|
{
|
2018-08-30 17:35:44 +02:00
|
|
|
|
if(!_context.Args[i].StartsWith("-"))
|
2017-08-07 22:31:00 +02:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 17:35:44 +02:00
|
|
|
|
_context.Parameters.Add(_context.Args[i].Substring(1), _context.Args[i + 1]);
|
2017-08-07 22:31:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|