/// <summary> /// Allow user to choose a ValidationPlugin /// </summary> /// <returns></returns> public override IValidationPluginOptionsFactory GetValidationPlugin(ILifetimeScope scope, Target target) { if (_runLevel.HasFlag(RunLevel.Advanced)) { var ret = _input.ChooseFromList( "How would you like to validate this certificate?", _plugins.ValidationPluginFactories(scope). Where(x => !(x is INull)). Where(x => x.CanValidate(target)). OrderBy(x => x.ChallengeType + x.Description), x => Choice.Create(x, description: $"[{x.ChallengeType}] {x.Description}"), true); return(ret ?? new NullValidationFactory()); } else { var ret = scope.Resolve <SelfHostingOptionsFactory>(); if (ret.CanValidate(target)) { return(ret); } else { _log.Error("The default validation plugin cannot be " + "used for this target. Most likely this is because " + "you have included a wildcard identifier (*.example.com), " + "which requires DNS validation. Choose another plugin " + "from the advanced menu ('M')."); return(new NullValidationFactory()); } } }
private async Task <CertificateInfo?> GetFromServer(ExecutionContext context, RunLevel runLevel) { // Place the order var certificateService = context.Scope.Resolve <ICertificateService>(); var orderManager = context.Scope.Resolve <OrderManager>(); context.Order.KeyPath = context.Order.Renewal.CsrPluginOptions?.ReusePrivateKey == true ? certificateService.ReuseKeyPath(context.Order) : null; context.Order.Details = await orderManager.GetOrCreate(context.Order, runLevel); // Sanity checks if (context.Order.Details == null) { context.Result.AddErrorMessage($"Unable to create order"); return(null); } if (context.Order.Details.Payload.Status == AcmeClient.OrderInvalid) { context.Result.AddErrorMessage($"Created order was invalid"); return(null); } // Generate the CSR plugin var csrPlugin = context.Target.CsrBytes == null?context.Scope.Resolve <ICsrPlugin>() : null; if (csrPlugin != null) { var(disabled, disabledReason) = csrPlugin.Disabled; if (disabled) { context.Result.AddErrorMessage($"CSR plugin is not available. {disabledReason}"); return(null); } } // Run validations var orderValid = context.Order.Details.Payload.Status == AcmeClient.OrderValid || context.Order.Details.Payload.Status == AcmeClient.OrderReady; if (!orderValid || runLevel.HasFlag(RunLevel.Test) || runLevel.HasFlag(RunLevel.IgnoreCache)) { await _validator.AuthorizeOrder(context, orderValid); if (!context.Result.Success) { return(null); } } // Request the certificate return(await certificateService.RequestCertificate(csrPlugin, context.RunLevel, context.Order)); }
public override IISWebOptions Aquire(Target target, IArgumentsService arguments, IInputService inputService, RunLevel runLevel) { var args = arguments.GetArguments <IISWebArguments>(); var ret = new IISWebOptions(args); var ask = true; if (target.IIS) { if (runLevel.HasFlag(RunLevel.Advanced)) { ask = inputService.PromptYesNo("Use different site for installation?"); } else { ask = false; } } if (ask) { var chosen = inputService.ChooseFromList("Choose site to create new bindings", _iisClient.WebSites, x => new Choice <long>(x.Id) { Description = x.Name, Command = x.Id.ToString() }, false); ret.SiteId = chosen; } return(ret); }
public override IISSitesOptions Aquire(IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { var ret = new IISSitesOptions(); var sites = _helper.GetSites(optionsService.MainArguments.HideHttps, true).Where(x => x.Hidden == false).ToList(); inputService.WritePagedList(sites.Select(x => Choice.Create(x, $"{x.Name} ({x.Hosts.Count()} bindings)", x.Id.ToString())).ToList()); var sanInput = inputService.RequestString("Enter a comma separated list of SiteIds or 'S' for all sites").ToLower().Trim(); sites = ProcessSiteIds(ret, sites, sanInput); if (sites == null) { return(null); } var hosts = sites.SelectMany(x => x.Hosts).Distinct().OrderBy(x => x); inputService.WritePagedList(hosts.Select(x => Choice.Create(x, ""))); ret.ExcludeBindings = inputService.RequestString("Press enter to include all listed hosts, or type a comma-separated lists of exclusions").ParseCsv(); if (runLevel.HasFlag(RunLevel.Advanced)) { ret.CommonName = inputService.ChooseFromList( "Select common name", hosts.Except(ret.ExcludeBindings ?? new List <string>()), (x) => new Choice <string>(x), true); } return(ret); }
/// <summary> /// Get a certificate from the cache /// </summary> /// <param name="context"></param> /// <param name="runLevel"></param> /// <returns></returns> private CertificateInfo?GetFromCache(ExecutionContext context, RunLevel runLevel) { var certificateService = context.Scope.Resolve <ICertificateService>(); var cachedCertificate = certificateService.CachedInfo(context.Order); if (cachedCertificate == null || cachedCertificate.CacheFile == null) { return(null); } if (cachedCertificate.CacheFile.LastWriteTime < DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1)) { return(null); } if (runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Warning( "Cached certificate available on disk but not used due to --{switch} switch.", nameof(MainArguments.Force).ToLower()); return(null); } _log.Warning( "Using cached certificate for {friendlyName}. To force a new request of the " + "certificate within {days} days, run with the --{switch} switch.", context.Order.FriendlyNameIntermediate, _settings.Cache.ReuseDays, nameof(MainArguments.Force).ToLower()); return(cachedCertificate); }
public override IISSiteOptions Aquire(IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { var ret = new IISSiteOptions(); var chosen = inputService.ChooseFromList("Choose site", _helper.GetSites(optionsService.MainArguments.HideHttps, true).Where(x => x.Hidden == false), x => new Choice <IISSiteHelper.IISSiteOption>(x) { Description = x.Name }, true); if (chosen != null) { ret.SiteId = chosen.Id; ret.FriendlyNameSuggestion = $"Site-{chosen.Id}"; // Exclude bindings inputService.WritePagedList(chosen.Hosts.Select(x => Choice.Create(x, ""))); ret.ExcludeBindings = inputService.RequestString("Press enter to include all listed hosts, or type a comma-separated lists of exclusions").ParseCsv(); if (runLevel.HasFlag(RunLevel.Advanced)) { ret.CommonName = inputService.ChooseFromList( "Select common name", chosen.Hosts.Except(ret.ExcludeBindings ?? new List <string>()), x => new Choice <string>(x), true); } return(ret); } return(null); }
public bool AquireAdvancedOptions(IInputService input, IEnumerable <string> chosen, RunLevel runLevel, IIISSiteOptions ret) { if (runLevel.HasFlag(RunLevel.Advanced)) { // Exclude bindings input.WritePagedList(chosen.Select(x => Choice.Create(x, ""))); ret.ExcludeBindings = input.RequestString("Press enter to include all listed hosts, or type a comma-separated lists of exclusions").ParseCsv(); } var remaining = chosen.Except(ret.ExcludeBindings ?? new List <string>()); if (remaining.Count() == 0) { _log.Error("No bindings remain"); return(false); } // Set common name if (remaining.Count() > 1) { ret.CommonName = input.ChooseFromList( "Select primary domain (common name)", remaining, x => Choice.Create(x), "Default"); } return(true); }
/// <summary> /// Get a certificate from the cache /// </summary> /// <param name="context"></param> /// <param name="runLevel"></param> /// <returns></returns> private CertificateInfo?GetFromCache(OrderContext context, RunLevel runLevel) { var cachedCertificate = _certificateService.CachedInfo(context.Order); if (cachedCertificate == null || cachedCertificate.CacheFile == null) { return(null); } if (cachedCertificate.CacheFile.LastWriteTime < DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1)) { return(null); } if (runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Warning( "Cached certificate available but not used due to --{switch} switch.", nameof(MainArguments.Force).ToLower()); return(null); } _log.Warning( "Using cache for {friendlyName}. To get a new certificate " + "within {days} days, run with --{switch}.", context.Order.FriendlyNameIntermediate, _settings.Cache.ReuseDays, nameof(MainArguments.Force).ToLower()); return(cachedCertificate); }
/// <summary> /// If renewal is already Scheduled, replace it with the new options /// </summary> /// <param name="target"></param> /// <returns></returns> private Renewal CreateRenewal(Renewal temp, RunLevel runLevel) { var renewal = _renewalService.FindByFriendlyName(temp.LastFriendlyName).FirstOrDefault(); if (renewal == null) { return(temp); } var overwrite = false; if (runLevel.HasFlag(RunLevel.Interactive)) { overwrite = _input.PromptYesNo($"Renewal with FriendlyName {temp.LastFriendlyName} already exists, overwrite?", true); } else { overwrite = true; } if (overwrite) { _log.Warning("Overwriting previously created renewal"); renewal.Updated = true; renewal.TargetPluginOptions = temp.TargetPluginOptions; renewal.CsrPluginOptions = temp.CsrPluginOptions; renewal.StorePluginOptions = temp.StorePluginOptions; renewal.ValidationPluginOptions = temp.ValidationPluginOptions; renewal.InstallationPluginOptions = temp.InstallationPluginOptions; return(renewal); } else { return(temp); } }
/// <summary> /// Remove renewal from the list of scheduled items /// </summary> private void CancelRenewal(RunLevel runLevel) { if (runLevel.HasFlag(RunLevel.Unattended)) { var friendlyName = _arguments.TryGetRequiredArgument(nameof(MainArguments.FriendlyName), _args.FriendlyName); foreach (var r in _renewalService.FindByFriendlyName(friendlyName)) { _renewalService.Cancel(r); } } else { var renewal = _input.ChooseFromList("Which renewal would you like to cancel?", _renewalService.Renewals, x => Choice.Create(x), "Back"); if (renewal != null) { if (_input.PromptYesNo($"Are you sure you want to cancel the renewal for {renewal}", false)) { _renewalService.Cancel(renewal); } } } }
public override async Task <IISWebOptions> Aquire(Target target, IInputService inputService, RunLevel runLevel) { var args = _arguments.GetArguments <IISWebArguments>(); var ret = new IISWebOptions(args); var ask = true; if (target.IIS) { if (runLevel.HasFlag(RunLevel.Advanced)) { ask = await inputService.PromptYesNo("Use different site for installation?", false); } else { ask = false; } } if (ask) { var chosen = await inputService.ChooseRequired("Choose site to create new bindings", _iisClient.WebSites, x => Choice.Create(x.Id, x.Name, x.Id.ToString())); ret.SiteId = chosen; } return(ret); }
/// <summary> /// Generic method to select a list of plugins /// </summary> /// <typeparam name="TOptions"></typeparam> /// <typeparam name="TOptionsFactory"></typeparam> /// <param name="name"></param> /// <param name="scope"></param> /// <param name="runLevel"></param> /// <param name="next"></param> /// <param name="default"></param> /// <param name="aquire"></param> /// <returns></returns> internal async Task <List <TOptions>?> SetupPlugins <TOptions, TOptionsFactory>( string name, ILifetimeScope scope, RunLevel runLevel, Func <IResolver, IEnumerable <TOptionsFactory>, Task <TOptionsFactory?> > next, Func <TOptionsFactory, Task <TOptions?> > @default, Func <TOptionsFactory, Task <TOptions?> > aquire) where TOptionsFactory : IPluginOptionsFactory where TOptions : class { var resolver = scope.Resolve <IResolver>(); var ret = new List <TOptions>(); var factories = new List <TOptionsFactory>(); try { while (true) { var factory = await next(resolver, factories); if (factory == null) { _exceptionHandler.HandleException(message: $"{name} plugin could not be selected"); return(null); } TOptions?options; try { options = runLevel.HasFlag(RunLevel.Unattended) ? await @default(factory) : await aquire(factory); } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"{name} plugin {factory.Name} aborted or failed"); return(null); } if (options == null) { _exceptionHandler.HandleException(message: $"{name} plugin {factory.Name} was unable to generate options"); return(null); } var isNull = factory is INull; if (!isNull || factories.Count == 0) { ret.Add(options); factories.Add(factory); } if (isNull) { break; } } } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Invalid selection of {name} plugins"); } return(ret); }
/// <summary> /// Decide to (re)create scheduled task or not /// </summary> /// <param name="runLevel"></param> /// <returns></returns> public async System.Threading.Tasks.Task EnsureTaskScheduler(RunLevel runLevel) { var existingTask = ExistingTask; var create = existingTask == null; if (existingTask != null) { var healthy = IsHealthy(existingTask); if (!healthy) { if (runLevel.HasFlag(RunLevel.Interactive)) { create = await _input.PromptYesNo($"Do you want to replace the existing task?", false); } else { _log.Error("Proceeding with unhealthy scheduled task, automatic renewals may not work until this is addressed"); } } } if (create) { await CreateTaskScheduler(runLevel); } }
/// <summary> /// Handle http challenge /// </summary> public override void PrepareChallenge() { Refresh(); WriteAuthorizationFile(); WriteWebConfig(); _log.Information("Answer should now be browsable at {answerUri}", _challenge.HttpResourceUrl); if (_runLevel.HasFlag(RunLevel.Test) && _renewal.New) { if (_input.PromptYesNo("[--test] Try in default browser?", false)) { Process.Start(_challenge.HttpResourceUrl); _input.Wait(); } } string foundValue = null; try { var value = WarmupSite(); if (Equals(value, _challenge.HttpResourceValue)) { _log.Information("Preliminary validation looks good, but ACME will be more thorough..."); } else { _log.Warning("Preliminary validation failed, found {value} instead of {expected}", foundValue ?? "(null)", _challenge.HttpResourceValue); } } catch (Exception ex) { _log.Error(ex, "Preliminary validation failed"); } }
/// <summary> /// Encrypt/Decrypt all machine-dependent information /// </summary> private void Encrypt(RunLevel runLevel) { bool userApproved = !runLevel.HasFlag(RunLevel.Interactive); bool encryptConfig = Properties.Settings.Default.EncryptConfig; var settings = _container.Resolve <ISettingsService>(); if (!userApproved) { _input.Show(null, "To move your installation of win-acme to another machine, you will want " + "to copy the data directory's files to the new machine. However, if you use the Encrypted Configuration option, your renewal " + "files contain protected data that is dependent on your local machine. You can " + "use this tools to temporarily unprotect your data before moving from the old machine. " + "The renewal files includes passwords for your certificates, other passwords/keys, and a key used " + "for signing requests for new certificates."); _input.Show(null, "To remove machine-dependent protections, use the following steps.", true); _input.Show(null, " 1. On your old machine, set the EncryptConfig setting to false"); _input.Show(null, " 2. Run this option; all protected values will be unprotected."); _input.Show(null, " 3. Copy your data files to the new machine."); _input.Show(null, " 4. On the new machine, set the EncryptConfig setting to true"); _input.Show(null, " 5. Run this option; all unprotected values will be saved with protection"); _input.Show(null, $"Data directory: {settings.ConfigPath}", true); _input.Show(null, $"Config directory: {Environment.CurrentDirectory}\\settings.config"); _input.Show(null, $"Current EncryptConfig setting: {encryptConfig}"); userApproved = _input.PromptYesNo($"Save all renewal files {(encryptConfig ? "with" : "without")} encryption?", false); } if (userApproved) { _log.Information("Updating files in: {settings}", settings.ConfigPath); _renewalService.Encrypt(); //re-saves all renewals, forcing re-write of all protected strings decorated with [jsonConverter(typeOf(protectedStringConverter())] var acmeClient = _container.Resolve <AcmeClient>(); acmeClient.EncryptSigner(); //re-writes the signer file _log.Information("Your files are re-saved with encryption turned {onoff}", encryptConfig? "on":"off"); } }
public override async Task <FileSystemOptions?> Aquire(Target target, IInputService inputService, RunLevel runLevel) { // Choose alternative site for validation var ret = new FileSystemOptions(await BaseAquire(target, inputService)); if (target.IIS && _iisClient.HasWebSites && string.IsNullOrEmpty(ret.Path) && runLevel.HasFlag(RunLevel.Advanced)) { var siteId = await ValidationSite().GetValue(); if (siteId != null || await inputService.PromptYesNo("Use different site for validation?", false)) { var site = await inputService.ChooseOptional("Validation site, must receive requests for all hosts on port 80", _iisClient.Sites.Where(x => x.Type == IISSiteType.Web), x => Choice.Create <IIISSite?>(x, x.Name, x.Id.ToString(), @default: x.Id == siteId), "Automatic (target site)"); if (site != null) { ret.Path = site.Path; ret.SiteId = site.Id; } } } return(ret); }
public override async Task <CertificateStoreOptions?> Aquire(IInputService inputService, RunLevel runLevel) { var ret = await Default(); if (ret != null && string.IsNullOrEmpty(ret.StoreName) && runLevel.HasFlag(RunLevel.Advanced)) { var currentDefault = CertificateStore.DefaultStore(_settingsService, _iisClient); var choices = new List <Choice <string?> >(); if (_iisClient.Version.Major > 8) { choices.Add(Choice.Create <string?>( "WebHosting", description: "[WebHosting] - Dedicated store for IIS")); } choices.Add(Choice.Create <string?>( "My", description: "[My] - General computer store (for Exchange/RDS)")); choices.Add(Choice.Create <string?>( null, description: $"[Default] - Use global default, currently {currentDefault}", @default: true)); var choice = await inputService.ChooseFromMenu( "Choose store to use, or type the name of another unlisted store", choices, other => Choice.Create <string?>(other)); // final save ret.StoreName = string.IsNullOrWhiteSpace(choice) ? null : choice; } return(ret); }
/// <summary> /// Get settings in interactive mode /// </summary> /// <param name="input"></param> /// <param name="runLevel"></param> /// <returns></returns> public override async Task <IISOptions?> Aquire(IInputService input, RunLevel runLevel) { var allSites = _iisHelper.GetSites(true).Where(x => x.Hosts.Any()).ToList(); if (!allSites.Any()) { _log.Error($"No sites with host bindings have been configured in IIS. " + $"Add one in the IIS Manager or choose the plugin '{ManualOptions.DescriptionText}' " + $"instead."); return(null); } var visibleSites = allSites.Where(x => !_arguments.MainArguments.HideHttps || x.Https == false).ToList(); if (!visibleSites.Any()) { _log.Error("No sites with host bindings remain after applying the --{hidehttps} filter. " + "It looks like all your websites are already configured for https!", "hidehttps"); return(null); } // Remove sites with only wildcard bindings because they cannot be validated in simple mode if (!runLevel.HasFlag(RunLevel.Advanced)) { visibleSites = visibleSites.Where(x => x.Hosts.Any(h => !h.StartsWith("*"))).ToList(); if (!visibleSites.Any()) { _log.Error("No sites with host bindings remain after discarding wildcard domains. To " + "create certificates including wildcards, please use the 'Full options' mode, as " + "this requires DNS validation."); return(null); } } // Repeat the process until the user is happy with their settings do { var allBindings = _iisHelper.GetBindings(); var visibleBindings = allBindings.Where(x => !_arguments.MainArguments.HideHttps || x.Https == false).ToList(); var ret = await TryAquireSettings(input, allBindings, visibleBindings, allSites, visibleSites, runLevel); if (ret != null) { var filtered = _iisHelper.FilterBindings(allBindings, ret); await ListBindings(input, filtered, ret.CommonName); if (await input.PromptYesNo("Continue with this selection?", true)) { return(ret); } } if (!await input.PromptYesNo("Restart?", true)) { return(null); } }while (true); }
/// <summary> /// Handle failure notification /// </summary> /// <param name="runLevel"></param> /// <param name="renewal"></param> private void NotifyFailure(RunLevel runLevel, Renewal renewal) { // Do not send emails when running interactively _log.Error("Renewal for {friendlyName} failed, will retry on next run", renewal.LastFriendlyName); if (runLevel.HasFlag(RunLevel.Unattended)) { _email.Send("Error processing certificate renewal", $"Renewal for {renewal.LastFriendlyName} failed, will retry on next run.", MailPriority.High); } }
/// <summary> /// Test if a renewal is needed /// </summary> /// <param name="renewal"></param> /// <param name="runLevel"></param> /// <returns></returns> internal bool ShouldRunRenewal(Renewal renewal, RunLevel runLevel) { if (renewal.New) { return(true); } if (!runLevel.HasFlag(RunLevel.ForceRenew) && !renewal.Updated) { _log.Verbose("Checking {renewal}", renewal.LastFriendlyName); if (!_dueDate.ShouldRun(renewal)) { return(false); } } else if (runLevel.HasFlag(RunLevel.ForceRenew)) { _log.Information(LogType.All, "Force renewing {renewal}", renewal.LastFriendlyName); } return(true); }
/// <summary> /// Handle failure notification /// </summary> /// <param name="runLevel"></param> /// <param name="renewal"></param> internal void NotifyFailure(RunLevel runLevel, Renewal renewal, string errorMessage) { // Do not send emails when running interactively _log.Error("Renewal for {friendlyName} failed, will retry on next run", renewal.LastFriendlyName); if (runLevel.HasFlag(RunLevel.Unattended)) { _email.Send("Error processing certificate renewal", $"<p>Renewal for <b>{renewal.LastFriendlyName}</b> failed with error <b>{errorMessage}</b>, will retry on next run.</p> {NotificationInformation(renewal)}", MailPriority.High); } }
/// <summary> /// Allow user to choose a ValidationPlugin /// </summary> /// <returns></returns> public override IValidationPluginOptionsFactory GetValidationPlugin(ILifetimeScope scope, Target target) { if (_runLevel.HasFlag(RunLevel.Advanced)) { // List options for generating new certificates _input.Show(null, "The ACME server will need to verify that you are the owner of the domain names that you are requesting" + " the certificate for. This happens both during initial setup *and* for every future renewal. There are two main methods of doing so: " + "answering specific http requests (http-01) or create specific dns records (dns-01). For wildcard domains the latter is the only option. " + "Various additional plugins are available from https://github.com/PKISharp/win-acme/.", true); var ret = _input.ChooseFromList( "How would you like prove ownership for the domain(s) in the certificate?", _plugins.ValidationPluginFactories(scope). Where(x => !(x is INull)). Where(x => x.CanValidate(target)). OrderByDescending(x => x.ChallengeType). ThenBy(x => x.Order). ThenBy(x => x.Description), x => Choice.Create(x, description: $"[{x.ChallengeType}] {x.Description}", @default: x is SelfHostingOptionsFactory), "Abort"); return(ret ?? new NullValidationFactory()); } else { var ret = scope.Resolve <SelfHostingOptionsFactory>(); if (ret.CanValidate(target)) { return(ret); } else { _log.Error("The default validation plugin cannot be " + "used for this target. Most likely this is because " + "you have included a wildcard identifier (*.example.com), " + "which requires DNS validation. Choose another plugin " + "from the advanced menu ('M')."); return(new NullValidationFactory()); } } }
/// <summary> /// Handle success notification /// </summary> /// <param name="runLevel"></param> /// <param name="renewal"></param> internal void NotifySuccess(RunLevel runLevel, Renewal renewal) { // Do not send emails when running interactively _log.Information(LogType.All, "Renewal for {friendlyName} succeeded", renewal.LastFriendlyName); if (runLevel.HasFlag(RunLevel.Unattended) && _settings.Notification.EmailOnSuccess) { _email.Send( "Certificate renewal completed", $"<p>Certificate <b>{renewal.LastFriendlyName}</b> succesfully renewed.</p> {NotificationInformation(renewal)}", MessagePriority.NonUrgent); } }
/// <summary> /// Handle success notification /// </summary> /// <param name="runLevel"></param> /// <param name="renewal"></param> internal void NotifySuccess(RunLevel runLevel, Renewal renewal) { // Do not send emails when running interactively _log.Information(true, "Renewal for {friendlyName} succeeded", renewal.LastFriendlyName); if (runLevel.HasFlag(RunLevel.Unattended) && Properties.Settings.Default.EmailOnSuccess) { _email.Send( "Certificate renewal completed", $"<p>Certificate <b>{renewal.LastFriendlyName}</b> succesfully renewed.</p> {NotificationInformation(renewal)}", MailPriority.Low); } }
/// <summary> /// Handle failure notification /// </summary> /// <param name="runLevel"></param> /// <param name="renewal"></param> internal void NotifyFailure(RunLevel runLevel, Renewal renewal, List <string> errorMessage) { // Do not send emails when running interactively _log.Error("Renewal for {friendlyName} failed, will retry on next run", renewal.LastFriendlyName); if (errorMessage.Count == 0) { errorMessage.Add("No specific error reason provided."); } if (runLevel.HasFlag(RunLevel.Unattended)) { _email.Send("Error processing certificate renewal", @$ "<p>Renewal for <b>{renewal.LastFriendlyName}</b> failed with error(s) <ul><li>{string.Join(" < / li > < li > ", errorMessage)}</li></ul> will retry
/// <summary> /// For revocation and configuration /// </summary> /// <param name="main"></param> /// <param name="runLevel"></param> /// <returns></returns> public ILifetimeScope Configuration(ILifetimeScope main, Renewal renewal, RunLevel runLevel) { var resolver = runLevel.HasFlag(RunLevel.Interactive) ? main.Resolve<InteractiveResolver>(new TypedParameter(typeof(RunLevel), runLevel)) : (IResolver)main.Resolve<UnattendedResolver>(); return main.BeginLifetimeScope(builder => { builder.Register(c => runLevel).As<RunLevel>(); builder.Register(c => resolver).As<IResolver>(); builder.Register(c => resolver.GetTargetPlugin(main).Result).As<ITargetPluginOptionsFactory>().SingleInstance(); builder.Register(c => resolver.GetCsrPlugin(main).Result).As<ICsrPluginOptionsFactory>().SingleInstance(); }); }
/// <summary> /// Handle http challenge /// </summary> public async override Task PrepareChallenge(ValidationContext context, Http01ChallengeValidationDetails challenge) { // Should always have a value, confirmed by RenewalExecutor // check only to satifiy the compiler if (context.TargetPart != null) { Refresh(context.TargetPart); } await WriteAuthorizationFile(challenge); await WriteWebConfig(challenge); _log.Information("Answer should now be browsable at {answerUri}", challenge.HttpResourceUrl); if (_runLevel.HasFlag(RunLevel.Test) && _renewal.New) { if (await _input.PromptYesNo("[--test] Try in default browser?", false)) { Process.Start(new ProcessStartInfo { FileName = challenge.HttpResourceUrl, UseShellExecute = true }); await _input.Wait(); } } string?foundValue = null; try { var value = await WarmupSite(challenge); if (Equals(value, challenge.HttpResourceValue)) { _log.Information("Preliminary validation looks good, but the ACME server will be more thorough"); } else { _log.Warning("Preliminary validation failed, the server answered '{value}' instead of '{expected}'. The ACME server might have a different perspective", foundValue ?? "(null)", challenge.HttpResourceValue); } } catch (HttpRequestException hrex) { _log.Warning("Preliminary validation failed because '{hrex}'", hrex.Message); } catch (Exception ex) { _log.Error(ex, "Preliminary validation failed"); } }
/// <summary> /// Allow user to choose a TargetPlugin /// </summary> /// <returns></returns> public override async Task <ITargetPluginOptionsFactory?> GetTargetPlugin(ILifetimeScope scope) { var options = _plugins.TargetPluginFactories(scope). Where(x => !x.Hidden). OrderBy(x => x.Order). ThenBy(x => x.Description); var defaultType = typeof(IISOptionsFactory); if (!options.OfType <IISOptionsFactory>().Any(x => !x.Disabled.Item1)) { defaultType = typeof(ManualOptionsFactory); } if (!_runLevel.HasFlag(RunLevel.Advanced)) { return((ITargetPluginOptionsFactory)scope.Resolve(defaultType)); } // List options for generating new certificates _input.Show(null, "Please specify how the list of domain names that will be included in the certificate " + "should be determined. If you choose for one of the \"all bindings\" options, the list will automatically be " + "updated for future renewals to reflect the bindings at that time.", true); var ret = await _input.ChooseOptional( "How shall we determine the domain(s) to include in the certificate?", options, x => Choice.Create <ITargetPluginOptionsFactory?>( x, description: x.Description, @default: x.GetType() == defaultType, disabled: x.Disabled.Item1, disabledReason: x.Disabled.Item2), "Abort"); return(ret ?? new NullTargetFactory()); }
private bool ShouldRun(ILifetimeScope target, IEnumerable <Order> orders, Renewal renewal, RunLevel runLevel) { // Check if renewal is needed if (!runLevel.HasFlag(RunLevel.ForceRenew) && !renewal.Updated) { _log.Verbose("Checking {renewal}", renewal.LastFriendlyName); if (!renewal.IsDue()) { var cs = target.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 {renewal} running prematurely due to source change", renewal.LastFriendlyName); abort = false; break; } } if (abort) { _log.Information("Renewal {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate()); return(false); } } else if (!renewal.New) { _log.Information(LogType.All, "Renewing certificate {renewal}", renewal.LastFriendlyName); } } else if (runLevel.HasFlag(RunLevel.ForceRenew)) { _log.Information(LogType.All, "Force renewing certificate {renewal}", renewal.LastFriendlyName); } return(true); }
/// <summary> /// For configuration and renewal /// </summary> /// <param name="main"></param> /// <param name="renewal"></param> /// <param name="runLevel"></param> /// <returns></returns> public ILifetimeScope Target(ILifetimeScope main, Renewal renewal, RunLevel runLevel) { var resolver = runLevel.HasFlag(RunLevel.Interactive) ? main.Resolve <InteractiveResolver>(new TypedParameter(typeof(RunLevel), runLevel)) : (IResolver)main.Resolve <UnattendedResolver>(); return(main.BeginLifetimeScope(builder => { builder.RegisterInstance(renewal.TargetPluginOptions).As(renewal.TargetPluginOptions.GetType()); builder.RegisterInstance(renewal.TargetPluginOptions).As(renewal.TargetPluginOptions.GetType().BaseType); builder.RegisterType(renewal.TargetPluginOptions.Instance).As <ITargetPlugin>().SingleInstance(); builder.Register(c => c.Resolve <ITargetPlugin>().Generate().Result).As <Target>().SingleInstance(); builder.Register(c => resolver.GetValidationPlugin(main, c.Resolve <Target>()).Result).As <IValidationPluginOptionsFactory>().SingleInstance(); })); }