Exemplo n.º 1
0
        /// <summary>
        /// Revoke certificate
        /// </summary>
        private async Task RevokeCertificate()
        {
            var renewal = await _input.ChooseFromList("Which certificate would you like to revoke?",
                                                      _renewalStore.Renewals,
                                                      x => Choice.Create(x),
                                                      "Back");

            if (renewal != null)
            {
                if (await _input.PromptYesNo($"Are you sure you want to revoke {renewal}? This should only be done in case of a security breach.", false))
                {
                    using var scope = _scopeBuilder.Execution(_container, renewal, RunLevel.Unattended);
                    var cs = scope.Resolve <ICertificateService>();
                    try
                    {
                        await cs.RevokeCertificate(renewal);

                        renewal.History.Add(new RenewResult("Certificate revoked"));
                    }
                    catch (Exception ex)
                    {
                        _exceptionHandler.HandleException(ex);
                    }
                }
            }
        }
Exemplo n.º 2
0
 /// <summary>
 /// Shared code for command line and renewal manager
 /// </summary>
 /// <param name="renewals"></param>
 /// <returns></returns>
 internal async Task RevokeCertificates(IEnumerable<Renewal> renewals)
 {
     foreach (var renewal in renewals)
     {
         using var scope = _scopeBuilder.Execution(_container, renewal, RunLevel.Unattended);
         var cs = scope.Resolve<ICertificateService>();
         try
         {
             await cs.RevokeCertificate(renewal);
             renewal.History.Add(new RenewResult("Certificate(s) revoked"));
         }
         catch (Exception ex)
         {
             _exceptionHandler.HandleException(ex);
         }
     }
 }
Exemplo n.º 3
0
        /// <summary>
        /// Determine if the renewal should be executes
        /// </summary>
        /// <param name="renewal"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        public async Task <RenewResult> HandleRenewal(Renewal renewal, RunLevel runLevel)
        {
            _input.CreateSpace();
            _log.Reset();
            using var ts = _scopeBuilder.Target(_container, renewal, runLevel);
            using var es = _scopeBuilder.Execution(ts, renewal, runLevel);
            // Generate the target
            var targetPlugin = es.Resolve <ITargetPlugin>();

            var(disabled, disabledReason) = targetPlugin.Disabled;
            if (disabled)
            {
                return(new RenewResult($"Target plugin is not available. {disabledReason}"));
            }
            var target = await targetPlugin.Generate();

            if (target is INull)
            {
                return(new RenewResult($"Target plugin did not generate a target"));
            }
            if (!target.IsValid(_log))
            {
                return(new RenewResult($"Target plugin generated an invalid target"));
            }

            // Check if our validation plugin is (still) up to the task
            var validationPlugin = es.Resolve <IValidationPluginOptionsFactory>();

            if (!validationPlugin.CanValidate(target))
            {
                return(new RenewResult($"Validation plugin is unable to validate the target. A wildcard host was introduced into a HTTP validated renewal."));
            }

            // Create one or more orders based on the target
            var orderPlugin = es.Resolve <IOrderPlugin>();
            var orders      = orderPlugin.Split(renewal, target);

            if (orders == null || orders.Count() == 0)
            {
                return(new RenewResult("Order plugin failed to create order(s)"));
            }
            _log.Verbose("Targeted convert into {n} order(s)", orders.Count());

            // Check if renewal is needed
            if (!runLevel.HasFlag(RunLevel.ForceRenew) && !renewal.Updated)
            {
                _log.Verbose("Checking {renewal}", renewal.LastFriendlyName);
                if (!renewal.IsDue())
                {
                    var cs    = es.Resolve <ICertificateService>();
                    var abort = true;
                    foreach (var order in orders)
                    {
                        var cache = cs.CachedInfo(order);
                        if (cache == null && !renewal.New)
                        {
                            _log.Information(LogType.All, "Renewal for {renewal} running prematurely due to detected target change", renewal.LastFriendlyName);
                            abort = false;
                            break;
                        }
                    }
                    if (abort)
                    {
                        _log.Information("Renewal for {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate());
                        return(new RenewResult()
                        {
                            Abort = true
                        });
                    }
                }
                else if (!renewal.New)
                {
                    _log.Information(LogType.All, "Renewing certificate for {renewal}", renewal.LastFriendlyName);
                }
            }
            else if (runLevel.HasFlag(RunLevel.ForceRenew))
            {
                _log.Information(LogType.All, "Force renewing certificate for {renewal}", renewal.LastFriendlyName);
            }

            // If at this point we haven't retured already with an error/abort
            // actually execute the renewal
            var result = await ExecuteRenewal(es, orders.ToList(), runLevel);

            // Configure task scheduler
            if (result.Success && !result.Abort)
            {
                if ((renewal.New || renewal.Updated) && !_args.NoTaskScheduler)
                {
                    if (runLevel.HasFlag(RunLevel.Test) && !await _input.PromptYesNo($"[--test] Do you want to automatically renew with these settings?", true))
                    {
                        // Early out for test runs
                        result.Abort = true;
                        return(result);
                    }
                    else
                    {
                        // Make sure the Task Scheduler is configured
                        await es.Resolve <TaskSchedulerService>().EnsureTaskScheduler(runLevel, false);
                    }
                }
            }
            return(result);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Renewal management mode
        /// </summary>
        /// <returns></returns>
        internal async Task ManageRenewals()
        {
            IEnumerable <Renewal> originalSelection = _renewalStore.Renewals.OrderBy(x => x.LastFriendlyName);
            var selectedRenewals = originalSelection;
            var quit             = false;
            var displayAll       = false;

            do
            {
                var all                  = selectedRenewals.Count() == originalSelection.Count();
                var none                 = selectedRenewals.Count() == 0;
                var totalLabel           = originalSelection.Count() != 1 ? "renewals" : "renewal";
                var renewalSelectedLabel = selectedRenewals.Count() != 1 ? "renewals" : "renewal";
                var selectionLabel       =
                    all ? selectedRenewals.Count() == 1 ? "the renewal" : "*all* renewals" :
                    none ? "no renewals" :
                    $"{selectedRenewals.Count()} of {originalSelection.Count()} {totalLabel}";

                _input.Show(null,
                            "Welcome to the renewal manager. Actions selected in the menu below will " +
                            "be applied to the following list of renewals. You may filter the list to target " +
                            "your action at a more specific set of renewals, or sort it to make it easier to " +
                            "find what you're looking for.",
                            true);

                var displayRenewals    = selectedRenewals;
                var displayLimited     = !displayAll && selectedRenewals.Count() >= _settings.UI.PageSize;
                var displayHidden      = 0;
                var displayHiddenLabel = "";
                if (displayLimited)
                {
                    displayRenewals    = displayRenewals.Take(_settings.UI.PageSize - 1);
                    displayHidden      = selectedRenewals.Count() - displayRenewals.Count();
                    displayHiddenLabel = displayHidden != 1 ? "renewals" : "renewal";
                }
                var choices = displayRenewals.Select(x => Choice.Create <Renewal?>(x,
                                                                                   description: x.ToString(_input),
                                                                                   color: x.History.LastOrDefault()?.Success ?? false ?
                                                                                   x.IsDue() ?
                                                                                   ConsoleColor.DarkYellow :
                                                                                   ConsoleColor.Green :
                                                                                   ConsoleColor.Red)).ToList();
                if (displayLimited)
                {
                    choices.Add(Choice.Create <Renewal?>(null,
                                                         command: "More",
                                                         description: $"{displayHidden} additional {displayHiddenLabel} selected but currently not displayed"));
                }
                await _input.WritePagedList(choices);

                displayAll = false;

                var options = new List <Choice <Func <Task> > >();
                if (displayLimited)
                {
                    options.Add(
                        Choice.Create <Func <Task> >(
                            () => { displayAll = true; return(Task.CompletedTask); },
                            "List all selected renewals", "A"));
                }

                if (selectedRenewals.Count() > 1)
                {
                    options.Add(
                        Choice.Create <Func <Task> >(
                            async() => selectedRenewals = await FilterRenewalsMenu(selectedRenewals),
                            all ? "Apply filter" : "Apply additional filter", "F",
                            @disabled: (selectedRenewals.Count() < 2, "Not enough renewals to filter.")));
                    options.Add(
                        Choice.Create <Func <Task> >(
                            async() => selectedRenewals = await SortRenewalsMenu(selectedRenewals),
                            "Sort renewals", "S",
                            @disabled: (selectedRenewals.Count() < 2, "Not enough renewals to sort.")));
                }
                if (!all)
                {
                    options.Add(
                        Choice.Create <Func <Task> >(
                            () => { selectedRenewals = originalSelection; return(Task.CompletedTask); },
                            "Reset sorting and filtering", "X",
                            @disabled: (all, "No filters have been applied yet.")));
                }
                options.Add(
                    Choice.Create <Func <Task> >(
                        async() => {
                    foreach (var renewal in selectedRenewals)
                    {
                        var index = selectedRenewals.ToList().IndexOf(renewal) + 1;
                        _log.Information("Details for renewal {n}/{m}", index, selectedRenewals.Count());
                        await ShowRenewal(renewal);
                        var cont = false;
                        if (index != selectedRenewals.Count())
                        {
                            cont = await _input.Wait("Press <Enter> to continue or <Esc> to abort");
                            if (!cont)
                            {
                                break;
                            }
                        }
                        else
                        {
                            await _input.Wait();
                        }
                    }
                },
                        $"Show details for {selectionLabel}", "D",
                        @disabled: (none, "No renewals selected.")));
                options.Add(
                    Choice.Create <Func <Task> >(
                        async() => {
                    WarnAboutRenewalArguments();
                    foreach (var renewal in selectedRenewals)
                    {
                        var runLevel = RunLevel.Interactive | RunLevel.ForceRenew;
                        if (_args.Force)
                        {
                            runLevel |= RunLevel.IgnoreCache;
                        }
                        await ProcessRenewal(renewal, runLevel);
                    }
                },
                        $"Run {selectionLabel}", "R",
                        @disabled: (none, "No renewals selected.")));
                options.Add(
                    Choice.Create <Func <Task> >(
                        async() => selectedRenewals = await Analyze(selectedRenewals),
                        $"Analyze duplicates for {selectionLabel}", "A",
                        @disabled: (none, "No renewals selected.")));
                options.Add(
                    Choice.Create <Func <Task> >(
                        async() => {
                    var confirm = await _input.PromptYesNo($"Are you sure you want to cancel {selectedRenewals.Count()} currently selected {renewalSelectedLabel}?", false);
                    if (confirm)
                    {
                        foreach (var renewal in selectedRenewals)
                        {
                            _renewalStore.Cancel(renewal);
                        }
                        ;
                        originalSelection = _renewalStore.Renewals.OrderBy(x => x.LastFriendlyName);
                        selectedRenewals  = originalSelection;
                    }
                },
                        $"Cancel {selectionLabel}", "C",
                        @disabled: (none, "No renewals selected.")));
                options.Add(
                    Choice.Create <Func <Task> >(
                        async() => {
                    var confirm = await _input.PromptYesNo($"Are you sure you want to revoke the most recently issued certificate for {selectedRenewals.Count()} currently selected {renewalSelectedLabel}? This should only be done in case of a (suspected) security breach. Cancel the {renewalSelectedLabel} if you simply don't need the certificates anymore.", false);
                    if (confirm)
                    {
                        foreach (var renewal in selectedRenewals)
                        {
                            using var scope = _scopeBuilder.Execution(_container, renewal, RunLevel.Interactive);
                            var cs          = scope.Resolve <ICertificateService>();
                            try
                            {
                                await cs.RevokeCertificate(renewal);
                                renewal.History.Add(new RenewResult("Certificate revoked"));
                            }
                            catch (Exception ex)
                            {
                                _exceptionHandler.HandleException(ex);
                            }
                        }
                        ;
                    }
                },
                        $"Revoke certificate for {selectionLabel}", "V",
                        @disabled: (none, "No renewals selected.")));
                options.Add(
                    Choice.Create <Func <Task> >(
                        () => { quit = true; return(Task.CompletedTask); },
                        "Back", "Q",
                        @default: originalSelection.Count() == 0));

                if (selectedRenewals.Count() > 1)
                {
                    _input.Show(null, $"Currently selected {selectedRenewals.Count()} of {originalSelection.Count()} {totalLabel}", true);
                }
                var chosen = await _input.ChooseFromMenu(
                    "Choose an action or type numbers to select renewals",
                    options,
                    (string unexpected) =>
                    Choice.Create <Func <Task> >(
                        async() => selectedRenewals = await FilterRenewalsById(selectedRenewals, unexpected)));

                await chosen.Invoke();
            }while (!quit);
        }
Exemplo n.º 5
0
        public async Task <RenewResult> Renew(Renewal renewal, RunLevel runLevel)
        {
            using var ts = _scopeBuilder.Target(_container, renewal, runLevel);
            using var es = _scopeBuilder.Execution(ts, renewal, runLevel);
            // Generate the target
            var targetPlugin = es.Resolve <ITargetPlugin>();

            if (targetPlugin.Disabled)
            {
                throw new Exception($"Target plugin is not available to the current user, try running as administrator");
            }
            var target = await targetPlugin.Generate();

            if (target == null)
            {
                throw new Exception($"Target plugin did not generate a target");
            }
            if (!target.IsValid(_log))
            {
                throw new Exception($"Target plugin generated an invalid target");
            }

            // Check if our validation plugin is (still) up to the task
            var validationPlugin = es.Resolve <IValidationPluginOptionsFactory>();

            if (!validationPlugin.CanValidate(target))
            {
                throw new Exception($"Validation plugin is unable to validate the target. A wildcard host was introduced into a HTTP validated renewal.");
            }

            // Check if renewal is needed
            if (!runLevel.HasFlag(RunLevel.ForceRenew) && !renewal.Updated)
            {
                _log.Verbose("Checking {renewal}", renewal.LastFriendlyName);
                if (!renewal.IsDue())
                {
                    var cs    = es.Resolve <ICertificateService>();
                    var cache = cs.CachedInfo(renewal);
                    if (cache != null && cache.Match(target))
                    {
                        _log.Information(LogType.All, "Renewal for {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate());
                        return(null);
                    }
                    else if (!renewal.New)
                    {
                        _log.Information(LogType.All, "Renewal for {renewal} running prematurely due to detected target change", renewal.LastFriendlyName);
                    }
                }
                else if (!renewal.New)
                {
                    _log.Information(LogType.All, "Renewing certificate for {renewal}", renewal.LastFriendlyName);
                }
            }
            else if (runLevel.HasFlag(RunLevel.ForceRenew))
            {
                _log.Information(LogType.All, "Force renewing certificate for {renewal}", renewal.LastFriendlyName);
            }

            // Create the order
            var client      = es.Resolve <AcmeClient>();
            var identifiers = target.GetHosts(false);
            var order       = await client.CreateOrder(identifiers);

            // Check if the order is valid
            if (order.Payload.Status != AcmeClient.OrderReady &&
                order.Payload.Status != AcmeClient.OrderPending)
            {
                return(OnRenewFail(new Challenge()
                {
                    Error = order.Payload.Error
                }));
            }

            // Answer the challenges
            foreach (var authUrl in order.Payload.Authorizations)
            {
                // Get authorization details
                var authorization = await client.GetAuthorizationDetails(authUrl);

                // Find a targetPart that matches the challenge
                var targetPart = target.Parts.
                                 FirstOrDefault(tp => tp.GetHosts(false).
                                                Any(h => authorization.Identifier.Value == h.Replace("*.", "")));
                if (targetPart == null)
                {
                    return(OnRenewFail(new Challenge()
                    {
                        Error = "Unable to match challenge to target"
                    }));
                }

                // Run the validation plugin
                var challenge = await Authorize(es, runLevel, renewal.ValidationPluginOptions, targetPart, authorization);

                if (challenge.Status != AcmeClient.AuthorizationValid)
                {
                    return(OnRenewFail(challenge));
                }
            }
            return(await OnValidationSuccess(es, renewal, target, order, runLevel));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Determine if the renewal should be executes
        /// </summary>
        /// <param name="renewal"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        public async Task <RenewResult> HandleRenewal(Renewal renewal, RunLevel runLevel)
        {
            _input.CreateSpace();
            _log.Reset();
            using var ts = _scopeBuilder.Target(_container, renewal, runLevel);
            using var es = _scopeBuilder.Execution(ts, renewal, runLevel);

            // Generate the target
            var targetPlugin = es.Resolve <ITargetPlugin>();

            var(disabled, disabledReason) = targetPlugin.Disabled;
            if (disabled)
            {
                return(new RenewResult($"Source plugin is not available. {disabledReason}"));
            }
            var target = await targetPlugin.Generate();

            if (target is INull)
            {
                return(new RenewResult($"Source plugin did not generate source"));
            }

            // Create one or more orders based on the target
            var orderPlugin = es.Resolve <IOrderPlugin>();
            var orders      = orderPlugin.Split(renewal, target);

            if (orders == null || !orders.Any())
            {
                return(new RenewResult("Order plugin failed to create order(s)"));
            }
            _log.Verbose("Source converted into {n} order(s)", orders.Count());
            foreach (var order in orders)
            {
                if (!order.Target.IsValid(_log))
                {
                    return(new RenewResult($"Source plugin generated invalid source"));
                }
            }

            /// Start to check the renewal
            var result = await HandleOrders(es, renewal, orders.ToList(), runLevel);

            // Configure task scheduler
            var setupTaskScheduler = _args.SetupTaskScheduler;

            if (!setupTaskScheduler && !_args.NoTaskScheduler)
            {
                setupTaskScheduler = result.Success == true && !result.Abort && (renewal.New || renewal.Updated);
            }
            if (setupTaskScheduler && runLevel.HasFlag(RunLevel.Test))
            {
                setupTaskScheduler = await _input.PromptYesNo($"[--test] Do you want to automatically renew with these settings?", true);

                if (!setupTaskScheduler)
                {
                    result.Abort = true;
                }
            }
            if (setupTaskScheduler)
            {
                var taskLevel = runLevel;
                if (_args.SetupTaskScheduler)
                {
                    taskLevel |= RunLevel.ForceRenew;
                }
                await es.Resolve <TaskSchedulerService>().EnsureTaskScheduler(runLevel);
            }
            return(result);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Determine if the renewal should be executes
        /// </summary>
        /// <param name="renewal"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        public async Task <RenewResult> HandleRenewal(Renewal renewal, RunLevel runLevel)
        {
            _input.CreateSpace();
            _log.Reset();
            using var ts = _scopeBuilder.Target(_container, renewal, runLevel);
            using var es = _scopeBuilder.Execution(ts, renewal, runLevel);
            // Generate the target
            var targetPlugin = es.Resolve <ITargetPlugin>();

            var(disabled, disabledReason) = targetPlugin.Disabled;
            if (disabled)
            {
                return(new RenewResult($"Source plugin is not available. {disabledReason}"));
            }
            var target = await targetPlugin.Generate();

            if (target is INull)
            {
                return(new RenewResult($"Source plugin did not generate source"));
            }
            if (!target.IsValid(_log))
            {
                return(new RenewResult($"Source plugin generated invalid source"));
            }

            // Check if our validation plugin is (still) up to the task
            var validationPlugin = es.Resolve <IValidationPluginOptionsFactory>();

            if (!validationPlugin.CanValidate(target))
            {
                return(new RenewResult($"Validation plugin is unable to validate the source. A wildcard host was introduced into a HTTP validated renewal."));
            }

            // Create one or more orders based on the target
            var orderPlugin = es.Resolve <IOrderPlugin>();
            var orders      = orderPlugin.Split(renewal, target);

            if (orders == null || !orders.Any())
            {
                return(new RenewResult("Order plugin failed to create order(s)"));
            }
            _log.Verbose("Targeted convert into {n} order(s)", orders.Count());

            // Test if this renewal should run right now
            if (!ShouldRun(es, orders, renewal, runLevel))
            {
                return(new RenewResult()
                {
                    Abort = true
                });
            }

            // If at this point we haven't retured already with an error/abort
            // actually execute the renewal
            var preScript    = _settings.Execution?.DefaultPreExecutionScript;
            var scriptClient = es.Resolve <ScriptClient>();

            if (!string.IsNullOrWhiteSpace(preScript))
            {
                await scriptClient.RunScript(preScript, $"{renewal.Id}");
            }
            var result = await ExecuteRenewal(es, orders.ToList(), runLevel);

            var postScript = _settings.Execution?.DefaultPostExecutionScript;

            if (!string.IsNullOrWhiteSpace(postScript))
            {
                await scriptClient.RunScript(postScript, $"{renewal.Id}");
            }

            // Configure task scheduler
            var setupTaskScheduler = _args.SetupTaskScheduler;

            if (!setupTaskScheduler && !_args.NoTaskScheduler)
            {
                setupTaskScheduler = result.Success && !result.Abort && (renewal.New || renewal.Updated);
            }
            if (setupTaskScheduler && runLevel.HasFlag(RunLevel.Test))
            {
                setupTaskScheduler = await _input.PromptYesNo($"[--test] Do you want to automatically renew with these settings?", true);

                if (!setupTaskScheduler)
                {
                    result.Abort = true;
                }
            }
            if (setupTaskScheduler)
            {
                var taskLevel = runLevel;
                if (_args.SetupTaskScheduler)
                {
                    taskLevel |= RunLevel.ForceRenew;
                }
                await es.Resolve <TaskSchedulerService>().EnsureTaskScheduler(runLevel);
            }
            return(result);
        }