Beispiel #1
0
        /// <summary>
        /// Run the renewal
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="orders"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        private async Task <RenewResult> ExecuteRenewal(ILifetimeScope execute, List <Order> orders, RunLevel runLevel)
        {
            var result = new RenewResult();

            foreach (var order in orders)
            {
                _log.Verbose("Handle order {n}/{m}: {friendly}",
                             orders.IndexOf(order) + 1,
                             orders.Count,
                             order.FriendlyNamePart ?? "Main");

                // Create the order details
                var orderManager = execute.Resolve <OrderManager>();
                order.Details = await orderManager.GetOrCreate(order, runLevel);

                // Create the execution context
                var context = new ExecutionContext(execute, order, runLevel, result);

                // Authorize the order (validation)
                await AuthorizeOrder(context);

                if (context.Result.Success)
                {
                    // Execute final steps (CSR, store, install)
                    await ExecuteOrder(context);
                }
            }
            return(result);
        }
Beispiel #2
0
 public ExecutionContext(ILifetimeScope scope, Order order, RunLevel runLevel, RenewResult result)
 {
     Scope    = scope;
     Order    = order;
     RunLevel = runLevel;
     Result   = result;
 }
Beispiel #3
0
        /// <summary>
        /// Run the renewal
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="orders"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        private async Task <RenewResult> ExecuteRenewal(ILifetimeScope execute, List <Order> orders, RunLevel runLevel)
        {
            var result = new RenewResult();

            foreach (var order in orders)
            {
                _log.Verbose("Handle order {n}/{m}: {friendly}",
                             orders.IndexOf(order) + 1,
                             orders.Count,
                             order.FriendlyNamePart ?? "Main");

                // Create the execution context
                var context = new ExecutionContext(execute, order, runLevel, result);
                await ExecuteOrder(context, runLevel);
            }
            return(result);
        }
Beispiel #4
0
 /// <summary>
 /// Move errors from a validation context up to the renewal result
 /// </summary>
 /// <param name="from"></param>
 /// <param name="to"></param>
 /// <param name="prefix"></param>
 private void TransferErrors(ValidationContext from, RenewResult to)
 {
     from.ErrorMessages.ForEach(e => to.AddErrorMessage($"[{from.Label}] {e}", from.Success != true));
     from.ErrorMessages.Clear();
 }
Beispiel #5
0
        /// <summary>
        /// Steps to take on succesful (re)authorization
        /// </summary>
        /// <param name="target"></param>
        private RenewResult OnRenewSuccess(ILifetimeScope renewalScope, Renewal renewal, Target target, OrderDetails order, RunLevel runLevel)
        {
            RenewResult result = null;

            try
            {
                var certificateService = renewalScope.Resolve <CertificateService>();
                var storePlugin        = renewalScope.Resolve <IStorePlugin>();
                var csrPlugin          = renewalScope.Resolve <ICsrPlugin>();
                var oldCertificate     = renewal.Certificate(storePlugin);
                var newCertificate     = certificateService.RequestCertificate(csrPlugin, renewal, target, order);

                // Test if a new certificate has been generated
                if (newCertificate == null)
                {
                    return(new RenewResult("No certificate generated"));
                }
                else
                {
                    result = new RenewResult(newCertificate);
                }

                // Early escape for testing validation only
                if (renewal.New && runLevel.HasFlag(RunLevel.Test) && !_input.PromptYesNo($"[--test] Do you want to install the certificate?"))
                {
                    return(new RenewResult("User aborted"));
                }

                try
                {
                    // Check if the newly requested certificate is already in the store,
                    // which might be the case due to the cache mechanism built into the
                    // RequestCertificate function
                    var storedCertificate = storePlugin.FindByThumbprint(newCertificate.Certificate.Thumbprint);
                    if (storedCertificate != null)
                    {
                        // Copy relevant properties
                        _log.Warning("Certificate with thumbprint {thumbprint} is already in the store", newCertificate.Certificate.Thumbprint);
                        newCertificate.Store = storedCertificate.Store;
                    }
                    else
                    {
                        // Save to store
                        storePlugin.Save(newCertificate);
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to store certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Store failed: {ex.Message}";
                    return(result);
                }

                // Run installation plugin(s)
                try
                {
                    var steps = renewal.InstallationPluginOptions.Count();
                    for (var i = 0; i < steps; i++)
                    {
                        var installOptions = renewal.InstallationPluginOptions[i];
                        var installPlugin  = (IInstallationPlugin)renewalScope.Resolve(installOptions.Instance);
                        if (!(installPlugin is INull))
                        {
                            if (steps > 1)
                            {
                                _log.Information("Installation step {n}/{m}: {name}...", i + 1, steps, installOptions.Name);
                            }
                            else
                            {
                                _log.Information("Installing with {name}...", installOptions.Name);
                            }
                            installPlugin.Install(newCertificate, oldCertificate);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to install certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Install failed: {ex.Message}";
                }

                // Delete the old certificate if not forbidden, found and not re-used
                if (!renewal.StorePluginOptions.KeepExisting &&
                    oldCertificate != null &&
                    newCertificate.Certificate.Thumbprint != oldCertificate.Certificate.Thumbprint)
                {
                    try
                    {
                        storePlugin.Delete(oldCertificate);
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Unable to delete previous certificate");
                        //result.Success = false; // not a show-stopper, consider the renewal a success
                        result.ErrorMessage = $"Delete failed: {ex.Message}";
                    }
                }

                if (renewal.New && !_arguments.NoTaskScheduler)
                {
                    if (runLevel.HasFlag(RunLevel.Test) && !_input.PromptYesNo($"[--test] Do you want to automatically renew this certificate?"))
                    {
                        // Early out for test runs
                        return(new RenewResult("User aborted"));
                    }
                    else
                    {
                        // Make sure the Task Scheduler is configured
                        renewalScope.Resolve <TaskSchedulerService>().EnsureTaskScheduler();
                    }
                }

                return(result);
            }
            catch (Exception ex)
            {
                HandleException(ex);

                // Result might still contain the Thumbprint of the certificate
                // that was requested and (partially? installed, which might help
                // with debugging
                if (result == null)
                {
                    result = new RenewResult(ex.Message);
                }
                else
                {
                    result.Success      = false;
                    result.ErrorMessage = ex.Message;
                }
            }

            return(result);
        }
Beispiel #6
0
        /// <summary>
        /// Steps to take on succesful (re)authorization
        /// </summary>
        /// <param name="target"></param>
        private static RenewResult OnRenewSuccess(ILifetimeScope renewalScope, ScheduledRenewal renewal)
        {
            RenewResult result = null;

            try
            {
                var certificateService = renewalScope.Resolve <CertificateService>();
                var storePlugin        = renewalScope.Resolve <IStorePlugin>();
                var oldCertificate     = renewal.Certificate(storePlugin);
                var newCertificate     = certificateService.RequestCertificate(renewal.Binding);

                // Test if a new certificate has been generated
                if (newCertificate == null)
                {
                    return(new RenewResult(new Exception("No certificate generated")));
                }
                else
                {
                    result = new RenewResult(newCertificate);
                }

                // Early escape for testing validation only
                if (_options.Test &&
                    renewal.New &&
                    !_input.PromptYesNo($"[--test] Do you want to install the certificate?"))
                {
                    return(result);
                }

                try
                {
                    // Check if the newly requested certificate is already in the store,
                    // which might be the case due to the cache mechanism built into the
                    // RequestCertificate function
                    var storedCertificate = storePlugin.FindByThumbprint(newCertificate.Certificate.Thumbprint);
                    if (storedCertificate != null)
                    {
                        // Copy relevant properties
                        _log.Warning("Certificate with thumbprint {thumbprint} is already in the store", newCertificate.Certificate.Thumbprint);
                        newCertificate.Store = storedCertificate.Store;
                    }
                    else
                    {
                        // Save to store
                        storePlugin.Save(newCertificate);
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to store certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Store failed: {ex.Message}";
                    return(result);
                }

                // Run installation plugin(s)
                try
                {
                    var installFactories = renewalScope.Resolve <List <IInstallationPluginFactory> >();
                    var steps            = installFactories.Count();
                    for (var i = 0; i < steps; i++)
                    {
                        var installFactory = installFactories[i];
                        if (!(installFactory is INull))
                        {
                            var installInstance = (IInstallationPlugin)renewalScope.Resolve(installFactory.Instance);
                            if (steps > 1)
                            {
                                _log.Information("Installation step {n}/{m}: {name}...", i + 1, steps, installFactory.Name);
                            }
                            else
                            {
                                _log.Information("Installing with {name}...", installFactory.Name);
                            }
                            installInstance.Install(newCertificate, oldCertificate);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to install certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Install failed: {ex.Message}";
                }

                // Delete the old certificate if not forbidden, found and not re-used
                if ((!renewal.KeepExisting ?? false) &&
                    oldCertificate != null &&
                    newCertificate.Certificate.Thumbprint != oldCertificate.Certificate.Thumbprint)
                {
                    try
                    {
                        storePlugin.Delete(oldCertificate);
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Unable to delete previous certificate");
                        //result.Success = false; // not a show-stopper, consider the renewal a success
                        result.ErrorMessage = $"Delete failed: {ex.Message}";
                    }
                }

                // Add or update renewal
                if ((renewal.New || renewal.Updated) &&
                    !_options.NoTaskScheduler &&
                    (!_options.Test ||
                     _input.PromptYesNo($"[--test] Do you want to automatically renew this certificate?")))
                {
                    var taskScheduler = renewalScope.Resolve <TaskSchedulerService>();
                    taskScheduler.EnsureTaskScheduler();
                    _renewalService.Save(renewal, result);
                }

                // Save renewal to store
                return(result);
            }
            catch (Exception ex)
            {
                // Result might still contain the Thumbprint of the certificate
                // that was requested and (partially? installed, which might help
                // with debugging
                HandleException(ex);
                if (result == null)
                {
                    result = new RenewResult(ex);
                }
                else
                {
                    result.Success      = false;
                    result.ErrorMessage = ex.Message;
                }
            }
            return(result);
        }
Beispiel #7
0
        /// <summary>
        /// Steps to take on succesful (re)authorization
        /// </summary>
        /// <param name="target"></param>
        private RenewResult OnValidationSuccess(ILifetimeScope renewalScope, Renewal renewal, Target target, OrderDetails order, RunLevel runLevel)
        {
            RenewResult result = null;

            try
            {
                var certificateService = renewalScope.Resolve <ICertificateService>();
                var csrPlugin          = target.CsrBytes == null?renewalScope.Resolve <ICsrPlugin>() : null;

                var oldCertificate = certificateService.CachedInfo(renewal);
                var newCertificate = certificateService.RequestCertificate(csrPlugin, runLevel, renewal, target, order);

                // Test if a new certificate has been generated
                if (newCertificate == null)
                {
                    return(new RenewResult("No certificate generated"));
                }
                else
                {
                    result = new RenewResult(newCertificate);
                }

                // Early escape for testing validation only
                if (renewal.New &&
                    runLevel.HasFlag(RunLevel.Test) &&
                    !_input.PromptYesNo($"[--test] Do you want to install the certificate?", true))
                {
                    return(new RenewResult("User aborted"));
                }

                // Run store plugin(s)
                var storePluginOptions = new List <StorePluginOptions>();
                var storePlugins       = new List <IStorePlugin>();
                try
                {
                    var steps = renewal.StorePluginOptions.Count();
                    for (var i = 0; i < steps; i++)
                    {
                        var storeOptions = renewal.StorePluginOptions[i];
                        var storePlugin  = (IStorePlugin)renewalScope.Resolve(storeOptions.Instance);
                        if (!(storePlugin is INull))
                        {
                            if (steps > 1)
                            {
                                _log.Information("Store step {n}/{m}: {name}...", i + 1, steps, storeOptions.Name);
                            }
                            else
                            {
                                _log.Information("Store with {name}...", storeOptions.Name);
                            }
                            storePlugin.Save(newCertificate);
                            storePlugins.Add(storePlugin);
                            storePluginOptions.Add(storeOptions);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to store certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Store failed: {ex.Message}";
                    return(result);
                }

                // Run installation plugin(s)
                try
                {
                    var steps = renewal.InstallationPluginOptions.Count();
                    for (var i = 0; i < steps; i++)
                    {
                        var installOptions = renewal.InstallationPluginOptions[i];
                        var installPlugin  = (IInstallationPlugin)renewalScope.Resolve(installOptions.Instance);
                        if (!(installPlugin is INull))
                        {
                            if (steps > 1)
                            {
                                _log.Information("Installation step {n}/{m}: {name}...", i + 1, steps, installOptions.Name);
                            }
                            else
                            {
                                _log.Information("Installing with {name}...", installOptions.Name);
                            }
                            installPlugin.Install(storePlugins, newCertificate, oldCertificate);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Unable to install certificate");
                    result.Success      = false;
                    result.ErrorMessage = $"Install failed: {ex.Message}";
                }

                // Delete the old certificate if not forbidden, found and not re-used
                for (var i = 0; i < storePluginOptions.Count; i++)
                {
                    if (!storePluginOptions[i].KeepExisting &&
                        oldCertificate != null &&
                        newCertificate.Certificate.Thumbprint != oldCertificate.Certificate.Thumbprint)
                    {
                        try
                        {
                            storePlugins[i].Delete(oldCertificate);
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Unable to delete previous certificate");
                            //result.Success = false; // not a show-stopper, consider the renewal a success
                            result.ErrorMessage = $"Delete failed: {ex.Message}";
                        }
                    }
                }

                if (renewal.New && !_args.NoTaskScheduler)
                {
                    if (runLevel.HasFlag(RunLevel.Test) &&
                        !_input.PromptYesNo($"[--test] Do you want to automatically renew this certificate?", true))
                    {
                        // Early out for test runs
                        return(new RenewResult("User aborted"));
                    }
                    else
                    {
                        // Make sure the Task Scheduler is configured
                        renewalScope.Resolve <TaskSchedulerService>().EnsureTaskScheduler(runLevel);
                    }
                }

                return(result);
            }
            catch (Exception ex)
            {
                HandleException(ex);
                while (ex.InnerException != null)
                {
                    ex = ex.InnerException;
                }

                // Result might still contain the Thumbprint of the certificate
                // that was requested and (partially? installed, which might help
                // with debugging
                if (result == null)
                {
                    result = new RenewResult(ex.Message);
                }
                else
                {
                    result.Success      = false;
                    result.ErrorMessage = ex.Message;
                }
            }

            return(result);
        }
Beispiel #8
0
        /// <summary>
        /// Run the renewal
        /// </summary>
        /// <param name="execute"></param>
        /// <param name="orders"></param>
        /// <param name="runLevel"></param>
        /// <returns></returns>
        private async Task <RenewResult> HandleOrders(ILifetimeScope execute, Renewal renewal, List <Order> orders, RunLevel runLevel)
        {
            // Build context
            var result        = new RenewResult();
            var orderContexts = orders.Select(order => new OrderContext(execute, order, runLevel, result)).ToList();

            // Check if renewal is needed at the root level
            var mainDue = !ShouldRunRenewal(renewal, runLevel);

            // Check individual orders
            foreach (var o in orderContexts)
            {
                o.ShouldRun = runLevel.HasFlag(RunLevel.ForceRenew) || _dueDate.ShouldRun(o.Order);
            }

            if (!mainDue)
            {
                // If renewal is not needed at the root level
                // it may be needed at the order level due to
                // change in target. Here we check this.
                if (!orderContexts.Any(x => x.ShouldRun))
                {
                    return(Abort(renewal));
                }
            }

            // Only process orders that are due. In the normal
            // case when using static due dates this will be all
            // the orders. But when using the random due dates,
            // this could only be a part of them.
            if (!runLevel.HasFlag(RunLevel.IgnoreCache) && !renewal.New && !renewal.Updated)
            {
                orderContexts = orderContexts.Where(x => x.ShouldRun).ToList();
            }
            if (!orderContexts.Any())
            {
                _log.Debug("None of the orders are currently due to run", orderContexts.Count, orders.Count);
                return(Abort(renewal));
            }
            if (!renewal.New && !runLevel.HasFlag(RunLevel.ForceRenew))
            {
                _log.Information(LogType.All, "Renewing {renewal}", renewal.LastFriendlyName);
            }
            if (orders.Count > orderContexts.Count)
            {
                _log.Information("{n} of {m} orders are due to run", orderContexts.Count, orders.Count);
            }

            // If at this point we haven't retured already with an error/abort
            // actually execute the renewal

            // Run the pre-execution script, e.g. to re-configure
            // local firewall rules, since now it's (almost) sure
            // that we're going to do something. Actually we may
            // still be able to read all certificates from cache,
            // but that's the exception rather than the rule.
            var preScript    = _settings.Execution?.DefaultPreExecutionScript;
            var scriptClient = execute.Resolve <ScriptClient>();

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

            // Get the certificates from cache or server
            await ExecuteOrders(orderContexts, runLevel);

            // Handle all the store/install steps
            await ProcessOrders(orderContexts);

            // Run the post-execution script. Note that this is different
            // from the script installation plugin, which is handled
            // in the previous step. This is only meant to undo any
            // (firewall?) changes made by the pre-execution script.
            var postScript = _settings.Execution?.DefaultPostExecutionScript;

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

            // Return final result
            return(result);
        }