/// <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); } } } }
/// <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); } } }
/// <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); }
/// <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); }
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)); }
/// <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); }
/// <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); }