public Credentials prompt(Host bookmark, string title, string reason, LoginOptions options)
        {
            Credentials credentials = new VaultCredentials();

            credentials.setSaved(options.save());
            AsyncDelegate d = delegate
            {
                View                = ObjectFactory.GetInstance <IPasswordPromptView>();
                View.Title          = title;
                View.Reason         = reason;
                View.OkButtonText   = LocaleFactory.localizedString("Continue", "Credentials");
                View.IconView       = IconCache.Instance.IconForName(options.icon(), 64);
                View.SavePassword   = options.save();
                View.ValidateInput += ValidateInputEventHandler;
                if (DialogResult.Cancel == View.ShowDialog(_browser.View))
                {
                    throw new LoginCanceledException();
                }
                credentials.setPassword(View.InputText);
                credentials.setSaved(View.SavePassword);
            };

            _browser.Invoke(d);
            return(credentials);
        }
Esempio n. 2
0
        /// <summary>
        /// Implements the service as a <see cref="Task"/>.
        /// </summary>
        /// <returns>The <see cref="Task"/>.</returns>
        private static async Task RunAsync()
        {
            // Load the settings.
            //
            // Initialize the proxy manager settings to their default values
            // if they don't already exist.

            if (!await consul.KV.Exists(hivemqMaintainSecondsKey))
            {
                log.LogInfo($"Persisting setting [{hivemqMaintainSecondsKey}=60.0]");
                await consul.KV.PutDouble(hivemqMaintainSecondsKey, 60);
            }

            if (!await consul.KV.Exists(logPurgeSecondsKey))
            {
                log.LogInfo($"Persisting setting [{logPurgeSecondsKey}=300.0]");
                await consul.KV.PutDouble(logPurgeSecondsKey, 300);
            }

            if (!await consul.KV.Exists(managerTopologySecondsKey))
            {
                log.LogInfo($"Persisting setting [{managerTopologySecondsKey}=300.0]");
                await consul.KV.PutDouble(managerTopologySecondsKey, 1800);
            }

            if (!await consul.KV.Exists(proxyUpdateSecondsKey))
            {
                log.LogInfo($"Persisting setting [{proxyUpdateSecondsKey}=60.0]");
                await consul.KV.PutDouble(proxyUpdateSecondsKey, 60);
            }

            if (!await consul.KV.Exists(secretPurgeSecondsKey))
            {
                log.LogInfo($"Persisting setting [{secretPurgeSecondsKey}=300.0]");
                await consul.KV.PutDouble(secretPurgeSecondsKey, 300);
            }

            if (!await consul.KV.Exists(swarmPollSecondsKey))
            {
                log.LogInfo($"Persisting setting [{swarmPollSecondsKey}=30.0]");
                await consul.KV.PutDouble(swarmPollSecondsKey, 30.0);
            }

            if (!await consul.KV.Exists(vaultUnsealSecondsKey))
            {
                log.LogInfo($"Persisting setting [{vaultUnsealSecondsKey}=30.0]");
                await consul.KV.PutDouble(vaultUnsealSecondsKey, 30.0);
            }

            hivemqMantainInterval   = TimeSpan.FromSeconds(await consul.KV.GetDouble(hivemqMaintainSecondsKey));
            logPurgerInterval       = TimeSpan.FromSeconds(await consul.KV.GetDouble(logPurgeSecondsKey));
            managerTopologyInterval = TimeSpan.FromSeconds(await consul.KV.GetDouble(managerTopologySecondsKey));
            proxyUpdateInterval     = TimeSpan.FromSeconds(await consul.KV.GetDouble(proxyUpdateSecondsKey));
            secretPurgeInterval     = TimeSpan.FromSeconds(await consul.KV.GetDouble(secretPurgeSecondsKey));
            swarmPollInterval       = TimeSpan.FromSeconds(await consul.KV.GetDouble(swarmPollSecondsKey));
            vaultUnsealInterval     = TimeSpan.FromSeconds(await consul.KV.GetDouble(vaultUnsealSecondsKey));

            log.LogInfo(() => $"Using setting [{hivemqMaintainSecondsKey}={hivemqMantainInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{logPurgeSecondsKey}={logPurgerInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{managerTopologySecondsKey}={managerTopologyInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{proxyUpdateSecondsKey}={proxyUpdateInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{secretPurgeSecondsKey}={secretPurgeInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{swarmPollSecondsKey}={swarmPollInterval.TotalSeconds}]");
            log.LogInfo(() => $"Using setting [{vaultUnsealSecondsKey}={vaultUnsealInterval.TotalSeconds}]");

            // Parse the Vault credentials from the [neon-hive-manager-vaultkeys]
            // secret, if it exists.

            var vaultCredentialsJson = HiveHelper.GetSecret("neon-hive-manager-vaultkeys");

            if (string.IsNullOrWhiteSpace(vaultCredentialsJson))
            {
                log.LogInfo(() => "Vault AUTO-UNSEAL is DISABLED because [neon-hive-manager-vaultkeys] Docker secret is not specified.");
            }
            else
            {
                try
                {
                    vaultCredentials = NeonHelper.JsonDeserialize <VaultCredentials>(vaultCredentialsJson);

                    log.LogInfo(() => "Vault AUTO-UNSEAL is ENABLED.");
                }
                catch (Exception e)
                {
                    log.LogError("Vault AUTO-UNSEAL is DISABLED because the [neon-hive-manager-vaultkeys] Docker secret could not be parsed.", e);
                }
            }

            // We're going to need this later.

            vaultUris = await GetVaultUrisAsync();

            // Launch the sub-tasks.  These will run until the service is terminated.

            var tasks = new List <Task>();

            // Start a task that handles HiveMQ related activities like ensuring that
            // the [sysadmin] account has full permissions for all virtual hosts.

            tasks.Add(HiveMQMaintainerAsync());

            // Start a task that checks for Elasticsearch [logstash] and [metricbeat] indexes
            // that are older than the number of retention days.

            tasks.Add(LogPurgerAsync());

            // Start a task that periodically checks for changes to the set of hive managers
            // (e.g. if a manager is added or removed).  This task will cause the service to exit
            // so it can be restarted automatically by Docker to respond to the change.

            tasks.Add(ManagerWatcherAsync());

            // Start a task that checks for old [neon-secret-retriever-*] service instances
            // as well as old persisted secrets and removes them.

            tasks.Add(SecretPurgerAsync());

            // Start a task that polls current hive state to update the hive definition in Consul, etc.

            tasks.Add(SwarmPollerAsync());

            // Start a task that periodically notifies the [neon-proxy-manager] service
            // that it should proactively rebuild the proxy configurations.

            tasks.Add(ProxyUpdaterAsync());

            // We need to start a vault poller for the Vault instance running on each manager
            // node.  We're going to construct the direct Vault URIs by querying Docker for
            // the current hive nodes and looking for the managers.

            foreach (var uri in vaultUris)
            {
                tasks.Add(VaultUnsealerAsync(uri));
            }

            // Wait for all tasks to exit cleanly for a normal shutdown.

            await NeonHelper.WaitAllAsync(tasks);
        }
Esempio n. 3
0
        /// <inheritdoc/>
        public override void Run(CommandLine commandLine)
        {
            // Split the command line on "--".

            var split = commandLine.Split(SplitItem);

            var leftCommandLine  = split.Left;
            var rightCommandLine = split.Right;

            // Basic initialization.

            if (leftCommandLine.HasHelpOption)
            {
                Console.WriteLine(usage);
                Program.Exit(0);
            }

            // Initialize the hive.

            var hiveLogin = Program.ConnectHive();

            hive             = new HiveProxy(hiveLogin);
            vaultCredentials = hiveLogin.VaultCredentials;

            if (rightCommandLine == null)
            {
                Console.WriteLine("*** ERROR: The [--] command separator is required.");
                Console.WriteLine();
                Console.WriteLine(usage);
                Program.Exit(1);
            }

            // Determine which node we're going to target.

            SshProxy <NodeDefinition> node;
            string          nodeName = leftCommandLine.GetOption("--node", null);
            CommandBundle   bundle;
            CommandResponse response;
            bool            failed = false;

            if (!string.IsNullOrEmpty(nodeName))
            {
                node = hive.GetNode(nodeName);
            }
            else
            {
                node = hive.GetReachableManager();
            }

            var command = rightCommandLine.Arguments.FirstOrDefault();;

            switch (command)
            {
            case "init":
            case "rekey":
            case "server":
            case "ssh":

                Console.Error.WriteLine($"*** ERROR: [neon vault {command}] is not supported.");
                Program.Exit(1);
                break;

            case "seal":

                // Just run the command on the target node if one was specified.

                if (nodeName != null)
                {
                    ExecuteOnNode(node, rightCommandLine);
                    return;
                }

                // We need to seal the Vault instance on every manager node unless a
                // specific node was requsted via [--node].
                //
                // Note also that it's not possible to seal a node that's in standby
                // mode so we'll restart the Vault container instead.

                Console.WriteLine();

                if (!string.IsNullOrEmpty(nodeName))
                {
                    response = node.SudoCommand($"vault-direct status");

                    if (response.ExitCode != 0)
                    {
                        Console.WriteLine($"[{node.Name}] is already sealed");
                    }
                    else
                    {
                        var vaultStatus = new VaultStatus(response.OutputText);

                        if (vaultStatus.HAMode == "standby")
                        {
                            Console.WriteLine($"[{node.Name}] restarting to seal standby node...");

                            response = node.SudoCommand($"systemctl restart vault");

                            if (response.ExitCode == 0)
                            {
                                Console.WriteLine($"[{node.Name}] sealed");
                            }
                            else
                            {
                                Console.WriteLine($"[{node.Name}] restart failed");
                            }
                        }
                        else
                        {
                            Console.WriteLine($"[{node.Name}] sealing...");

                            response = node.SudoCommand($"export VAULT_TOKEN={vaultCredentials.RootToken} && vault-direct operator seal", RunOptions.Redact);

                            if (response.ExitCode == 0)
                            {
                                Console.WriteLine($"[{node.Name}] sealed");
                            }
                            else
                            {
                                Console.WriteLine($"[{node.Name}] seal failed");
                            }
                        }
                    }

                    failed = response.ExitCode != 0;
                }
                else
                {
                    foreach (var manager in hive.Managers)
                    {
                        try
                        {
                            response = manager.SudoCommand($"vault-direct status");
                        }
                        catch (SshOperationTimeoutException)
                        {
                            Console.WriteLine($"[{manager.Name}] ** unavailable **");
                            continue;
                        }

                        var vaultStatus = new VaultStatus(response.OutputText);

                        if (response.ExitCode != 0)
                        {
                            Console.WriteLine($"[{manager.Name}] is already sealed");
                        }
                        else
                        {
                            response = manager.SudoCommand($"vault-direct operator seal");

                            if (vaultStatus.HAMode == "standby")
                            {
                                Console.WriteLine($"[{manager.Name}] restarting to seal standby node...");

                                response = manager.SudoCommand($"systemctl restart vault");

                                if (response.ExitCode == 0)
                                {
                                    Console.WriteLine($"[{manager.Name}] restart/seal [standby]");
                                }
                                else
                                {
                                    Console.WriteLine($"[{manager.Name}] restart/seal failed [standby]");
                                }
                            }
                            else
                            {
                                Console.WriteLine($"[{manager.Name}] sealing...");

                                response = manager.SudoCommand($"export VAULT_TOKEN={vaultCredentials.RootToken} && vault-direct operator seal", RunOptions.Redact);

                                if (response.ExitCode == 0)
                                {
                                    Console.WriteLine($"[{manager.Name}] sealed");
                                }
                                else
                                {
                                    failed = true;
                                    Console.WriteLine($"[{manager.Name}] seal failed");
                                }
                            }
                        }
                    }

                    if (!failed)
                    {
                        // Disable auto unseal until the operator explicitly unseals Vault again.

                        hive.Consul.Client.KV.PutBool($"{HiveConst.GlobalKey}/{HiveGlobals.UserDisableAutoUnseal}", true).Wait();
                    }

                    Program.Exit(failed ? 1 : 0);
                }
                break;

            case "status":

                // Just run the command on the target node if one was specified.

                if (nodeName != null)
                {
                    ExecuteOnNode(node, rightCommandLine);
                    return;
                }

                // We need to obtain the status from the Vault instance on every manager node unless a
                // specific node was requsted via [--node].

                Console.WriteLine();

                if (!string.IsNullOrEmpty(nodeName))
                {
                    response = node.SudoCommand("vault-direct status");

                    Console.WriteLine(response.AllText);
                    Program.Exit(response.ExitCode);
                }
                else
                {
                    var allSealed = true;

                    foreach (var manager in hive.Managers)
                    {
                        try
                        {
                            response = manager.SudoCommand("vault-direct status");
                        }
                        catch (SshOperationTimeoutException)
                        {
                            Console.WriteLine($"[{manager.Name}] ** unavailable **");
                            continue;
                        }

                        var vaultStatus = new VaultStatus(response.OutputText);

                        if (response.ExitCode == 0)
                        {
                            allSealed = false;

                            var status = vaultStatus.HAMode;

                            if (status == "active")
                            {
                                status += "  <-- LEADER";
                            }

                            Console.WriteLine($"[{manager.Name}] unsealed {status}");
                        }
                        else if (response.ExitCode == 2)
                        {
                            Console.WriteLine($"[{manager.Name}] sealed");
                        }
                        else
                        {
                            failed = true;
                            Console.WriteLine($"[{manager.Name}] error getting status");
                        }
                    }

                    if (allSealed)
                    {
                        Program.Exit(2);
                    }
                    else
                    {
                        Program.Exit(failed ? 1 : 0);
                    }
                }
                break;

            case "unseal":

                // Just run the command on the target node if one was specified.

                if (nodeName != null)
                {
                    ExecuteOnNode(node, rightCommandLine);
                    return;
                }

                // We need to unseal the Vault instance on every manager node unless a
                // specific node was requsted via [--node].

                Console.WriteLine();

                if (!string.IsNullOrEmpty(nodeName))
                {
                    // Verify that the instance isn't already unsealed.

                    response = node.SudoCommand($"vault-direct status");

                    if (response.ExitCode == 2)
                    {
                        Console.WriteLine($"[{node.Name}] unsealing...");
                    }
                    else if (response.ExitCode == 0)
                    {
                        Console.WriteLine($"[{node.Name}] is already unsealed");
                        break;
                    }
                    else
                    {
                        Console.WriteLine($"[{node.Name}] unseal failed");
                        Program.Exit(response.ExitCode);
                    }

                    // Note that we're passing the [-reset] option to ensure that
                    // any keys from previous attempts have been cleared.

                    node.SudoCommand($"vault-direct operator unseal -reset");

                    foreach (var key in vaultCredentials.UnsealKeys)
                    {
                        response = node.SudoCommand($"vault-direct operator unseal {key}", RunOptions.None);

                        if (response.ExitCode != 0)
                        {
                            Console.WriteLine($"[{node.Name}] unseal failed");
                            Program.Exit(1);
                        }
                    }

                    Console.WriteLine($"[{node.Name}] unsealed");
                }
                else
                {
                    var commandFailed = false;

                    foreach (var manager in hive.Managers)
                    {
                        // Verify that the instance isn't already unsealed.

                        try
                        {
                            response = manager.SudoCommand($"vault-direct status");
                        }
                        catch (SshOperationTimeoutException)
                        {
                            Console.WriteLine($"[{manager.Name}] ** unavailable **");
                            continue;
                        }

                        if (response.ExitCode == 2)
                        {
                            Console.WriteLine($"[{manager.Name}] unsealing...");
                        }
                        else if (response.ExitCode == 0)
                        {
                            Console.WriteLine($"[{manager.Name}] is already unsealed");
                            continue;
                        }
                        else
                        {
                            Console.WriteLine($"[{manager.Name}] unseal failed");
                            continue;
                        }

                        // Note that we're passing the [-reset] option to ensure that
                        // any keys from previous attempts have been cleared.

                        manager.SudoCommand($"vault-direct operator unseal -reset");

                        foreach (var key in vaultCredentials.UnsealKeys)
                        {
                            response = manager.SudoCommand($"vault-direct operator unseal {key}", RunOptions.None);

                            if (response.ExitCode != 0)
                            {
                                failed        = true;
                                commandFailed = true;

                                Console.WriteLine($"[{manager.Name}] unseal failed");
                            }
                        }

                        if (!failed)
                        {
                            Console.WriteLine($"[{manager.Name}] unsealed");
                        }
                    }

                    if (!commandFailed)
                    {
                        // Reenable auto unseal.

                        hive.Consul.Client.KV.PutBool($"{HiveConst.GlobalKey}/{HiveGlobals.UserDisableAutoUnseal}", false).Wait();
                    }

                    Program.Exit(commandFailed ? 1 : 0);
                }
                break;

            case "write":

            {
                // We need handle any [key=@file] arguments specially by including them
                // in a command bundle as data files.

                var files         = new List <CommandFile>();
                var commandString = rightCommandLine.ToString();

                foreach (var dataArg in rightCommandLine.Arguments.Skip(2))
                {
                    var fields = dataArg.Split(new char[] { '=' }, 2);

                    if (fields.Length == 2 && fields[1].StartsWith("@"))
                    {
                        var fileName      = fields[1].Substring(1);
                        var localFileName = $"{files.Count}.data";

                        files.Add(
                            new CommandFile()
                            {
                                Path = localFileName,
                                Data = File.ReadAllBytes(fileName)
                            });

                        commandString = commandString.Replace($"@{fileName}", $"@{localFileName}");
                    }
                }

                bundle = new CommandBundle($"export VAULT_TOKEN={vaultCredentials.RootToken} && {remoteVaultPath} {commandString}");

                foreach (var file in files)
                {
                    bundle.Add(file);
                }

                response = node.SudoCommand(bundle, RunOptions.IgnoreRemotePath | RunOptions.Redact);

                Console.WriteLine(response.AllText);
                Program.Exit(response.ExitCode);
            }
            break;

            case "policy-write":

                // The last command line item is either:
                //
                //      * A "-", indicating that the content should come from standard input.
                //      * A file name prefixed by "@"
                //      * A string holding JSON or HCL

                if (rightCommandLine.Items.Length < 2)
                {
                    response = node.SudoCommand($"export VAULT_TOKEN={vaultCredentials.RootToken} && {remoteVaultPath} {rightCommandLine}", RunOptions.IgnoreRemotePath | RunOptions.Redact);

                    Console.WriteLine(response.AllText);
                    Program.Exit(response.ExitCode);
                }

                var lastItem   = rightCommandLine.Items.Last();
                var policyText = string.Empty;

                if (lastItem == "-")
                {
                    policyText = NeonHelper.ReadStandardInputText();
                }
                else if (lastItem.StartsWith("@"))
                {
                    policyText = File.ReadAllText(lastItem.Substring(1), Encoding.UTF8);
                }
                else
                {
                    policyText = lastItem;
                }

                // We're going to upload a text file holding the policy text and
                // then run a script piping the policy file into the Vault command passed,
                // with the last item replaced by a "-".

                bundle = new CommandBundle("./set-vault-policy.sh.sh");

                var sbScript = new StringBuilder();

                sbScript.AppendLine($"export VAULT_TOKEN={vaultCredentials.RootToken}");
                sbScript.Append($"cat policy | {remoteVaultPath}");

                for (int i = 0; i < rightCommandLine.Items.Length - 1; i++)
                {
                    sbScript.Append(' ');
                    sbScript.Append(rightCommandLine.Items[i]);
                }

                sbScript.AppendLine(" -");

                bundle.AddFile("set-vault-policy.sh", sbScript.ToString(), isExecutable: true);
                bundle.AddFile("policy", policyText);

                response = node.SudoCommand(bundle, RunOptions.IgnoreRemotePath | RunOptions.Redact);

                Console.WriteLine(response.AllText);
                Program.Exit(response.ExitCode);
                break;

            default:

                ExecuteOnNode(node, rightCommandLine);
                break;
            }
        }