/// <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());
         }
     }
 }
示例#2
0
        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));
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#5
0
        /// <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);
        }
示例#7
0
        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);
        }
示例#8
0
        /// <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);
        }
示例#9
0
        /// <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);
            }
        }
示例#10
0
 /// <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);
        }
示例#12
0
        /// <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);
        }
示例#13
0
        /// <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);
            }
        }
示例#14
0
        /// <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");
            }
        }
示例#15
0
        /// <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);
        }
示例#19
0
 /// <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);
     }
 }
示例#20
0
 /// <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);
 }
示例#21
0
 /// <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);
     }
 }
示例#22
0
        /// <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());
                }
            }
        }
示例#23
0
 /// <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);
     }
 }
示例#24
0
 /// <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);
     }
 }
示例#25
0
 /// <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 
示例#26
0
 /// <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();
     });
 }
示例#27
0
        /// <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");
            }
        }
示例#28
0
        /// <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());
        }
示例#29
0
 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);
 }
示例#30
0
        /// <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();
            }));
        }