Example #1
0
        /// <summary>
        /// Will the selected validation plugin be able to validate the
        /// authorisation?
        /// </summary>
        /// <param name="authorization"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private bool IsValid(AuthorizationContext context, ValidationPluginOptions options)
        {
            var(plugin, match) = GetInstances(context.Order.ExecutionScope, options);
            if (plugin == null || match == null)
            {
                _log.Warning("Validation plugin {name} not found or not created", options.Name);
                return(false);
            }
            var(disabled, disabledReason) = plugin.Disabled;
            if (disabled)
            {
                _log.Warning("Validation plugin {name} is not available. {disabledReason}", options.Name, disabledReason);
                return(false);
            }

            var identifier  = Identifier.Parse(context.Authorization.Identifier);
            var dummyTarget = new Target(identifier);

            if (!match.CanValidate(dummyTarget))
            {
                _log.Warning("Validation plugin {name} cannot validate identifier {identifier}", options.Name, identifier.Value);
                return(false);
            }
            if (!context.Authorization.Challenges.Any(x => x.Type == options.ChallengeType))
            {
                _log.Warning("No challenge of type {options.ChallengeType} available", options.Name, identifier.Value);
                return(false);
            }
            return(true);
        }
Example #2
0
 /// <summary>
 /// Validation
 /// </summary>
 /// <param name="execution"></param>
 /// <param name="options"></param>
 /// <param name="target"></param>
 /// <param name="identifier"></param>
 /// <returns></returns>
 public ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options)
 {
     return execution.BeginLifetimeScope(builder =>
     {
         builder.RegisterType<HttpValidationParameters>();
         builder.RegisterType(options.Instance).
             As<IValidationPlugin>().
             SingleInstance();
     });
 }
Example #3
0
 public ValidationContextParameters(
     AuthorizationContext authorization,
     TargetPart targetPart,
     ValidationPluginOptions options)
 {
     TargetPart    = targetPart;
     OrderContext  = authorization.Order;
     Authorization = authorization.Authorization;
     Options       = options;
 }
Example #4
0
 /// <summary>
 /// Validation
 /// </summary>
 /// <param name="execution"></param>
 /// <param name="options"></param>
 /// <param name="target"></param>
 /// <param name="identifier"></param>
 /// <returns></returns>
 public ILifetimeScope Validation(ILifetimeScope execution, ValidationPluginOptions options, TargetPart target, string identifier)
 {
     return(execution.BeginLifetimeScope(builder =>
     {
         builder.RegisterType <HttpValidationParameters>().
         WithParameters(new[] {
             new TypedParameter(typeof(string), identifier),
             new TypedParameter(typeof(TargetPart), target)
         });
         builder.RegisterType(options.Instance).
         WithParameters(new[] {
             new TypedParameter(typeof(string), identifier),
         }).
         As <IValidationPlugin>().
         SingleInstance();
     }));
 }
Example #5
0
        /// <summary>
        /// Setup a new scheduled renewal
        /// </summary>
        /// <param name="runLevel"></param>
        private void SetupRenewal(RunLevel runLevel)
        {
            if (_args.Test)
            {
                runLevel |= RunLevel.Test;
            }
            if (_args.Force)
            {
                runLevel |= RunLevel.IgnoreCache;
            }
            _log.Information(LogType.All, "Running in mode: {runLevel}", runLevel);
            var tempRenewal = Renewal.Create(_args.Id, _passwordGenerator);

            using (var configScope = _scopeBuilder.Configuration(_container, tempRenewal, runLevel))
            {
                // Choose target plugin
                var targetPluginOptionsFactory = configScope.Resolve <ITargetPluginOptionsFactory>();
                if (targetPluginOptionsFactory is INull)
                {
                    HandleException(message: $"No target plugin could be selected");
                    return;
                }
                var targetPluginOptions = runLevel.HasFlag(RunLevel.Unattended) ?
                                          targetPluginOptionsFactory.Default(_arguments) :
                                          targetPluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                if (targetPluginOptions == null)
                {
                    HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} aborted or failed");
                    return;
                }
                tempRenewal.TargetPluginOptions = targetPluginOptions;

                // Generate Target and validation plugin choice
                Target initialTarget = null;
                IValidationPluginOptionsFactory validationPluginOptionsFactory = null;
                using (var targetScope = _scopeBuilder.Target(_container, tempRenewal, runLevel))
                {
                    initialTarget = targetScope.Resolve <Target>();
                    if (initialTarget == null)
                    {
                        HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} was unable to generate a target");
                        return;
                    }
                    if (!initialTarget.IsValid(_log))
                    {
                        HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} generated an invalid target");
                        return;
                    }
                    _log.Information("Target generated using plugin {name}: {target}", targetPluginOptions.Name, initialTarget);

                    // Choose FriendlyName
                    if (runLevel.HasFlag(RunLevel.Advanced) &&
                        runLevel.HasFlag(RunLevel.Interactive) &&
                        string.IsNullOrEmpty(_args.FriendlyName))
                    {
                        var alt = _input.RequestString($"Suggested FriendlyName is '{initialTarget.FriendlyName}', press enter to accept or type an alternative");
                        if (!string.IsNullOrEmpty(alt))
                        {
                            tempRenewal.FriendlyName = alt;
                        }
                    }
                    tempRenewal.LastFriendlyName = initialTarget.FriendlyName;

                    // Choose validation plugin
                    validationPluginOptionsFactory = targetScope.Resolve <IValidationPluginOptionsFactory>();
                    if (validationPluginOptionsFactory is INull)
                    {
                        HandleException(message: $"No validation plugin could be selected");
                        return;
                    }
                }

                // Configure validation
                try
                {
                    ValidationPluginOptions validationOptions = null;
                    if (runLevel.HasFlag(RunLevel.Unattended))
                    {
                        validationOptions = validationPluginOptionsFactory.Default(initialTarget, _arguments);
                    }
                    else
                    {
                        validationOptions = validationPluginOptionsFactory.Aquire(initialTarget, _arguments, _input, runLevel);
                    }
                    if (validationOptions == null)
                    {
                        HandleException(message: $"Validation plugin {validationPluginOptionsFactory.Name} was unable to generate options");
                        return;
                    }
                    tempRenewal.ValidationPluginOptions = validationOptions;
                }
                catch (Exception ex)
                {
                    HandleException(ex, $"Validation plugin {validationPluginOptionsFactory.Name} aborted or failed");
                    return;
                }

                // Choose CSR plugin
                if (initialTarget.CsrBytes == null)
                {
                    var csrPluginOptionsFactory = configScope.Resolve <ICsrPluginOptionsFactory>();
                    if (csrPluginOptionsFactory is INull)
                    {
                        HandleException(message: $"No CSR plugin could be selected");
                        return;
                    }

                    // Configure CSR
                    try
                    {
                        CsrPluginOptions csrOptions = null;
                        if (runLevel.HasFlag(RunLevel.Unattended))
                        {
                            csrOptions = csrPluginOptionsFactory.Default(_arguments);
                        }
                        else
                        {
                            csrOptions = csrPluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                        }
                        if (csrOptions == null)
                        {
                            HandleException(message: $"CSR plugin {csrPluginOptionsFactory.Name} was unable to generate options");
                            return;
                        }
                        tempRenewal.CsrPluginOptions = csrOptions;
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, $"CSR plugin {csrPluginOptionsFactory.Name} aborted or failed");
                        return;
                    }
                }

                // Choose and configure store plugins
                var resolver = configScope.Resolve <IResolver>();
                var storePluginOptionsFactories = new List <IStorePluginOptionsFactory>();
                try
                {
                    while (true)
                    {
                        var storePluginOptionsFactory = resolver.GetStorePlugin(configScope, storePluginOptionsFactories);
                        if (storePluginOptionsFactory == null)
                        {
                            HandleException(message: $"Store could not be selected");
                        }
                        if (storePluginOptionsFactory is NullStoreOptionsFactory)
                        {
                            if (storePluginOptionsFactories.Count == 0)
                            {
                                throw new Exception();
                            }
                            break;
                        }
                        StorePluginOptions storeOptions;
                        try
                        {
                            if (runLevel.HasFlag(RunLevel.Unattended))
                            {
                                storeOptions = storePluginOptionsFactory.Default(_arguments);
                            }
                            else
                            {
                                storeOptions = storePluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                            }
                        }
                        catch (Exception ex)
                        {
                            HandleException(ex, $"Store plugin {storePluginOptionsFactory.Name} aborted or failed");
                            return;
                        }
                        if (storeOptions == null)
                        {
                            HandleException(message: $"Store plugin {storePluginOptionsFactory.Name} was unable to generate options");
                            return;
                        }
                        tempRenewal.StorePluginOptions.Add(storeOptions);
                        storePluginOptionsFactories.Add(storePluginOptionsFactory);
                    }
                }
                catch (Exception ex)
                {
                    HandleException(ex, "Invalid selection of store plugins");
                    return;
                }

                // Choose and configure installation plugins
                var installationPluginFactories = new List <IInstallationPluginOptionsFactory>();
                try
                {
                    while (true)
                    {
                        var installationPluginFactory = resolver.GetInstallationPlugin(configScope,
                                                                                       tempRenewal.StorePluginOptions.Select(x => x.Instance),
                                                                                       installationPluginFactories);

                        if (installationPluginFactory == null)
                        {
                            HandleException(message: $"Installation plugin could not be selected");
                        }
                        InstallationPluginOptions installOptions;
                        try
                        {
                            if (runLevel.HasFlag(RunLevel.Unattended))
                            {
                                installOptions = installationPluginFactory.Default(initialTarget, _arguments);
                            }
                            else
                            {
                                installOptions = installationPluginFactory.Aquire(initialTarget, _arguments, _input, runLevel);
                            }
                        }
                        catch (Exception ex)
                        {
                            HandleException(ex, $"Installation plugin {installationPluginFactory.Name} aborted or failed");
                            return;
                        }
                        if (installOptions == null)
                        {
                            HandleException(message: $"Installation plugin {installationPluginFactory.Name} was unable to generate options");
                            return;
                        }
                        if (installationPluginFactory is NullInstallationOptionsFactory)
                        {
                            if (installationPluginFactories.Count == 0)
                            {
                                tempRenewal.InstallationPluginOptions.Add(installOptions);
                                installationPluginFactories.Add(installationPluginFactory);
                            }
                            break;
                        }
                        tempRenewal.InstallationPluginOptions.Add(installOptions);
                        installationPluginFactories.Add(installationPluginFactory);
                    }
                }
                catch (Exception ex)
                {
                    HandleException(ex, "Invalid selection of installation plugins");
                    return;
                }

                // Try to run for the first time
                var renewal = CreateRenewal(tempRenewal, runLevel);
                var result  = Renew(renewal, runLevel);
                if (!result.Success)
                {
                    HandleException(message: $"Create certificate failed: {result.ErrorMessage}");
                }
                else
                {
                    _renewalService.Save(renewal, result);
                }
            }
        }
Example #6
0
        /// <summary>
        /// Get information needed to construct a validation context (shared between serial and parallel mode)
        /// </summary>
        /// <param name="context"></param>
        /// <param name="authorizationUri"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private async Task <ValidationContextParameters?> GetValidationContextParameters(ExecutionContext context, string authorizationUri, ValidationPluginOptions options, bool orderValid)
        {
            // Get authorization challenge details from server
            var client        = context.Scope.Resolve <AcmeClient>();
            var authorization = default(Authorization);

            try
            {
                authorization = await client.GetAuthorizationDetails(authorizationUri);
            }
            catch
            {
                context.Result.AddErrorMessage($"Unable to get authorization details from {authorizationUri}", !orderValid);
                return(null);
            }

            // Find a targetPart that matches the challenge
            // TODO: this might cause a bug when there are two
            // identifiers of different types with the same value
            // though I cannot imagine at this point how that
            // would happen (ips are not valid domains, domains
            // are not valid emails, etc.)
            var targetPart = context.Target.Parts.
                             FirstOrDefault(tp => tp.GetIdentifiers(false).
                                            Any(h => authorization.Identifier.Value == h.Value.Replace("*.", "")));

            if (targetPart == null)
            {
                context.Result.AddErrorMessage($"Unable to match challenge {authorization.Identifier.Value} to target", !orderValid);
                return(null);
            }

            return(new ValidationContextParameters(authorization, targetPart, options.ChallengeType, options.Name, orderValid));
        }
Example #7
0
        /// <summary>
        /// Get information needed to construct a validation context (shared between serial and parallel mode)
        /// </summary>
        /// <param name="context"></param>
        /// <param name="authorizationUri"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private async Task <ValidationContextParameters?> GetValidationContextParameters(ExecutionContext context, string authorizationUri, ValidationPluginOptions options, bool orderValid)
        {
            // Get authorization challenge details from server
            var client        = context.Scope.Resolve <AcmeClient>();
            var authorization = default(Authorization);

            try
            {
                authorization = await client.GetAuthorizationDetails(authorizationUri);
            }
            catch
            {
                context.Result.AddErrorMessage($"Unable to get authorization details from {authorizationUri}", !orderValid);
                return(null);
            }

            // Find a targetPart that matches the challenge
            var targetPart = context.Target.Parts.
                             FirstOrDefault(tp => tp.GetHosts(false).
                                            Any(h => authorization.Identifier.Value == h.Replace("*.", "")));

            if (targetPart == null)
            {
                context.Result.AddErrorMessage($"Unable to match challenge {authorization.Identifier.Value} to target", !orderValid);
                return(null);
            }

            return(new ValidationContextParameters(authorization, targetPart, options.ChallengeType, options.Name, orderValid));
        }
Example #8
0
        /// <summary>
        /// Make sure we have authorization for every host in target
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private Challenge Authorize(ILifetimeScope execute, RunLevel runLevel, OrderDetails order, ValidationPluginOptions options, TargetPart targetPart, Authorization authorization)
        {
            var invalid = new Challenge {
                Status = _authorizationInvalid
            };
            var valid = new Challenge {
                Status = _authorizationValid
            };
            var client     = execute.Resolve <AcmeClient>();
            var identifier = authorization.Identifier.Value;

            try
            {
                _log.Information("Authorize identifier: {identifier}", identifier);
                if (authorization.Status == _authorizationValid && !runLevel.HasFlag(RunLevel.Test))
                {
                    _log.Information("Cached authorization result: {Status}", authorization.Status);
                    return(valid);
                }
                else
                {
                    using (var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier))
                    {
                        IValidationPlugin validationPlugin = null;
                        try
                        {
                            validationPlugin = validation.Resolve <IValidationPlugin>();
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Error resolving validation plugin");
                        }
                        if (validationPlugin == null)
                        {
                            _log.Error("Validation plugin not found or not created.");
                            return(invalid);
                        }
                        var challenge = authorization.Challenges.FirstOrDefault(c => c.Type == options.ChallengeType);
                        if (challenge == null)
                        {
                            _log.Error("Expected challenge type {type} not available for {identifier}.",
                                       options.ChallengeType,
                                       authorization.Identifier.Value);
                            return(invalid);
                        }

                        if (challenge.Status == _authorizationValid && !runLevel.HasFlag(RunLevel.Test))
                        {
                            _log.Information("{dnsIdentifier} already validated by {challengeType} validation ({name})",
                                             authorization.Identifier.Value,
                                             options.ChallengeType,
                                             options.Name);
                            return(valid);
                        }

                        _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})",
                                         identifier,
                                         options.ChallengeType,
                                         options.Name);
                        try
                        {
                            var details = client.DecodeChallengeValidation(authorization, challenge);
                            validationPlugin.PrepareChallenge(details);
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "Error preparing for challenge answer");
                            return(invalid);
                        }

                        _log.Debug("Submitting challenge answer");
                        challenge = client.AnswerChallenge(challenge);

                        // Have to loop to wait for server to stop being pending
                        var tries    = 0;
                        var maxTries = 4;
                        while (challenge.Status == _authorizationPending)
                        {
                            _log.Debug("Refreshing authorization");
                            Thread.Sleep(2000); // this has to be here to give ACME server a chance to think
                            challenge = client.GetChallengeDetails(challenge.Url);
                            tries    += 1;
                            if (tries > maxTries)
                            {
                                _log.Error("Authorization timed out");
                                return(invalid);
                            }
                        }

                        if (challenge.Status != _authorizationValid)
                        {
                            _log.Error("Authorization result: {Status}", challenge.Status);
                            return(invalid);
                        }
                        else
                        {
                            _log.Information("Authorization result: {Status}", challenge.Status);
                            return(valid);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _log.Error("Error authorizing {renewal}", targetPart);
                HandleException(ex);
                return(invalid);
            }
        }
Example #9
0
        /// <summary>
        /// Setup a new scheduled renewal
        /// </summary>
        /// <param name="runLevel"></param>
        private void CreateNewCertificate(RunLevel runLevel)
        {
            if (_args.Test)
            {
                runLevel |= RunLevel.Test;
            }
            _log.Information(true, "Running in mode: {runLevel}", runLevel);
            var tempRenewal = Renewal.Create(_passwordGenerator);

            using (var configScope = _scopeBuilder.Configuration(_container, tempRenewal, runLevel))
            {
                // Choose target plugin
                var targetPluginOptionsFactory = configScope.Resolve <ITargetPluginOptionsFactory>();
                if (targetPluginOptionsFactory is INull)
                {
                    HandleException(message: $"No target plugin could be selected");
                    return;
                }
                var targetPluginOptions = runLevel.HasFlag(RunLevel.Unattended) ?
                                          targetPluginOptionsFactory.Default(_arguments) :
                                          targetPluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                if (targetPluginOptions == null)
                {
                    HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} aborted or failed");
                    return;
                }
                tempRenewal.TargetPluginOptions = targetPluginOptions;

                // Generate Target and validation plugin choice
                Target initialTarget = null;
                IValidationPluginOptionsFactory validationPluginOptionsFactory = null;
                using (var targetScope = _scopeBuilder.Target(_container, tempRenewal, runLevel))
                {
                    initialTarget = targetScope.Resolve <Target>();
                    if (initialTarget == null)
                    {
                        HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} was unable to generate a target");
                        return;
                    }
                    if (!initialTarget.IsValid(_log))
                    {
                        HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} generated an invalid target");
                        return;
                    }
                    _log.Information("Target generated using plugin {name}: {target}", targetPluginOptions.Name, initialTarget);

                    // Choose FriendlyName
                    if (runLevel.HasFlag(RunLevel.Advanced) &&
                        runLevel.HasFlag(RunLevel.Interactive) &&
                        string.IsNullOrEmpty(_args.FriendlyName))
                    {
                        var alt = _input.RequestString($"Suggested FriendlyName is '{initialTarget.FriendlyName}', press enter to accept or type an alternative");
                        if (!string.IsNullOrEmpty(alt))
                        {
                            tempRenewal.FriendlyName = alt;
                        }
                    }
                    tempRenewal.LastFriendlyName = initialTarget.FriendlyName;

                    // Choose validation plugin
                    validationPluginOptionsFactory = targetScope.Resolve <IValidationPluginOptionsFactory>();
                    if (validationPluginOptionsFactory is INull)
                    {
                        HandleException(message: $"No validation plugin could be selected");
                        return;
                    }
                }

                // Configure validation
                try
                {
                    ValidationPluginOptions validationOptions = null;
                    if (runLevel.HasFlag(RunLevel.Unattended))
                    {
                        validationOptions = validationPluginOptionsFactory.Default(initialTarget, _arguments);
                    }
                    else
                    {
                        validationOptions = validationPluginOptionsFactory.Aquire(initialTarget, _arguments, _input, runLevel);
                    }
                    if (validationOptions == null)
                    {
                        HandleException(message: $"Validation plugin {validationPluginOptionsFactory.Name} was unable to generate options");
                        return;
                    }
                    tempRenewal.ValidationPluginOptions = validationOptions;
                }
                catch (Exception ex)
                {
                    HandleException(ex, $"Validation plugin {validationPluginOptionsFactory.Name} aborted or failed");
                    return;
                }

                // Choose CSR plugin
                var csrPluginOptionsFactory = configScope.Resolve <ICsrPluginOptionsFactory>();
                if (csrPluginOptionsFactory is INull)
                {
                    HandleException(message: $"No CSR plugin could be selected");
                    return;
                }

                // Configure CSR
                try
                {
                    CsrPluginOptions csrOptions = null;
                    if (runLevel.HasFlag(RunLevel.Unattended))
                    {
                        csrOptions = csrPluginOptionsFactory.Default(_arguments);
                    }
                    else
                    {
                        csrOptions = csrPluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                    }
                    if (csrOptions == null)
                    {
                        HandleException(message: $"CSR plugin {csrPluginOptionsFactory.Name} was unable to generate options");
                        return;
                    }
                    tempRenewal.CsrPluginOptions = csrOptions;
                }
                catch (Exception ex)
                {
                    HandleException(ex, $"CSR plugin {csrPluginOptionsFactory.Name} aborted or failed");
                    return;
                }

                // Choose store plugin
                var storePluginOptionsFactory = configScope.Resolve <IStorePluginOptionsFactory>();
                if (storePluginOptionsFactory is INull)
                {
                    HandleException(message: $"No store plugin could be selected");
                    return;
                }

                // Configure storage
                try
                {
                    StorePluginOptions storeOptions = null;
                    if (runLevel.HasFlag(RunLevel.Unattended))
                    {
                        storeOptions = storePluginOptionsFactory.Default(_arguments);
                    }
                    else
                    {
                        storeOptions = storePluginOptionsFactory.Aquire(_arguments, _input, runLevel);
                    }
                    if (storeOptions == null)
                    {
                        HandleException(message: $"Store plugin {storePluginOptionsFactory.Name} was unable to generate options");
                        return;
                    }
                    tempRenewal.StorePluginOptions = storeOptions;
                }
                catch (Exception ex)
                {
                    HandleException(ex, $"Store plugin {storePluginOptionsFactory.Name} aborted or failed");
                    return;
                }

                // Choose and configure installation plugins
                try
                {
                    var installationPluginOptionsFactories =
                        configScope.Resolve <List <IInstallationPluginOptionsFactory> >(
                            new TypedParameter(typeof(IStorePlugin), storePluginOptionsFactory.Name)
                            );
                    if (installationPluginOptionsFactories.Count() == 0)
                    {
                        // User cancelled, otherwise we would at least have the Null-installer
                        HandleException(message: $"User aborted");
                        return;
                    }
                    foreach (var installationPluginOptionsFactory in installationPluginOptionsFactories)
                    {
                        InstallationPluginOptions installOptions;
                        try
                        {
                            if (runLevel.HasFlag(RunLevel.Unattended))
                            {
                                installOptions = installationPluginOptionsFactory.Default(initialTarget, _arguments);
                            }
                            else
                            {
                                installOptions = installationPluginOptionsFactory.Aquire(initialTarget, _arguments, _input, runLevel);
                            }
                        }
                        catch (Exception ex)
                        {
                            HandleException(ex, $"Install plugin {installationPluginOptionsFactory.Name} aborted or failed");
                            return;
                        }
                        if (installOptions == null)
                        {
                            HandleException(message: $"Installation plugin {installationPluginOptionsFactory.Name} was unable to generate options");
                            return;
                        }
                        tempRenewal.InstallationPluginOptions.Add(installOptions);
                    }
                }
                catch (Exception ex)
                {
                    HandleException(ex, "Invalid selection of installation plugins");
                    return;
                }

                // Try to run for the first time
                var renewal = CreateRenewal(tempRenewal, runLevel);
                var result  = Renew(renewal, runLevel);
                if (!result.Success)
                {
                    HandleException(message: $"Create certificate failed: {result.ErrorMessage}");
                }
                else
                {
                    _renewalService.Save(renewal, result);
                }
            }
        }
Example #10
0
        /// <summary>
        /// Get instances of IValidationPlugin and IValidationPluginOptionsFactory
        /// based on an instance of ValidationPluginOptions.
        /// TODO: more cache here
        /// </summary>
        /// <param name="scope"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private (IValidationPlugin?, IValidationPluginOptionsFactory?) GetInstances(ILifetimeScope scope, ValidationPluginOptions options)
        {
            var validationScope = _scopeBuilder.Validation(scope, options);

            try
            {
                var validationPlugin = validationScope.Resolve <IValidationPlugin>();
                var pluginService    = scope.Resolve <IPluginService>();
                var match            = pluginService.
                                       GetFactories <IValidationPluginOptionsFactory>(scope).
                                       FirstOrDefault(vp => vp.OptionsType.PluginId() == options.Plugin);
                return(validationPlugin, match);
            }
            catch (Exception ex)
            {
                _log.Error(ex, $"Unable to resolve plugin {options.Name}");
                return(null, null);
            }
        }
Example #11
0
        /// <summary>
        /// Make sure we have authorization for every host in target
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private async Task <Challenge> Authorize(
            ILifetimeScope execute, RunLevel runLevel,
            ValidationPluginOptions options, TargetPart targetPart,
            Authorization authorization)
        {
            var invalid = new Challenge {
                Status = AcmeClient.AuthorizationInvalid
            };
            var valid = new Challenge {
                Status = AcmeClient.AuthorizationValid
            };
            var client     = execute.Resolve <AcmeClient>();
            var identifier = authorization.Identifier.Value;

            try
            {
                _log.Information("Authorize identifier: {identifier}", identifier);
                if (authorization.Status == AcmeClient.AuthorizationValid &&
                    !runLevel.HasFlag(RunLevel.Test) &&
                    !runLevel.HasFlag(RunLevel.IgnoreCache))
                {
                    _log.Information("Cached authorization result: {Status}", authorization.Status);
                    return(valid);
                }
                else
                {
                    using var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier);
                    IValidationPlugin validationPlugin = null;
                    try
                    {
                        validationPlugin = validation.Resolve <IValidationPlugin>();
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Error resolving validation plugin");
                    }
                    if (validationPlugin == null)
                    {
                        _log.Error("Validation plugin not found or not created.");
                        return(invalid);
                    }
                    if (validationPlugin.Disabled)
                    {
                        _log.Error("Validation plugin is not available to the current user, try running as administrator.");
                        return(invalid);
                    }
                    var challenge = authorization.Challenges.FirstOrDefault(c => c.Type == options.ChallengeType);
                    if (challenge == null)
                    {
                        _log.Error("Expected challenge type {type} not available for {identifier}.",
                                   options.ChallengeType,
                                   authorization.Identifier.Value);
                        return(invalid);
                    }

                    if (challenge.Status == AcmeClient.AuthorizationValid &&
                        !runLevel.HasFlag(RunLevel.Test) &&
                        !runLevel.HasFlag(RunLevel.IgnoreCache))
                    {
                        _log.Information("{dnsIdentifier} already validated by {challengeType} validation ({name})",
                                         authorization.Identifier.Value,
                                         options.ChallengeType,
                                         options.Name);
                        return(valid);
                    }

                    _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})",
                                     identifier,
                                     options.ChallengeType,
                                     options.Name);
                    try
                    {
                        var details = client.DecodeChallengeValidation(authorization, challenge);
                        await validationPlugin.PrepareChallenge(details);
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Error preparing for challenge answer");
                        return(invalid);
                    }

                    _log.Debug("Submitting challenge answer");
                    challenge = await client.AnswerChallenge(challenge);

                    if (challenge.Status != AcmeClient.AuthorizationValid)
                    {
                        if (challenge.Error != null)
                        {
                            _log.Error(challenge.Error.ToString());
                        }
                        _log.Error("Authorization result: {Status}", challenge.Status);
                        return(invalid);
                    }
                    else
                    {
                        _log.Information("Authorization result: {Status}", challenge.Status);
                        return(valid);
                    }
                }
            }
            catch (Exception ex)
            {
                _log.Error("Error authorizing {renewal}", targetPart);
                _exceptionHandler.HandleException(ex);
                return(invalid);
            }
        }
Example #12
0
        /// <summary>
        /// Make sure we have authorization for every host in target
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private async Task <Challenge> Authorize(
            ILifetimeScope execute, RunLevel runLevel,
            ValidationPluginOptions options, TargetPart targetPart,
            Authorization authorization)
        {
            var invalid = new Challenge {
                Status = AcmeClient.AuthorizationInvalid
            };
            var valid = new Challenge {
                Status = AcmeClient.AuthorizationValid
            };
            var client     = execute.Resolve <AcmeClient>();
            var identifier = authorization.Identifier.Value;
            IValidationPlugin?validationPlugin = null;

            try
            {
                if (authorization.Status == AcmeClient.AuthorizationValid)
                {
                    if (!runLevel.HasFlag(RunLevel.Test) &&
                        !runLevel.HasFlag(RunLevel.IgnoreCache))
                    {
                        _log.Information("Cached authorization result: {Status}", authorization.Status);
                        return(valid);
                    }

                    if (runLevel.HasFlag(RunLevel.IgnoreCache))
                    {
                        // Due to the IgnoreCache flag (--force switch)
                        // we are going to attempt to re-authorize the
                        // domain even though its already autorized.
                        // On failure, we can still use the cached result.
                        // This helps for migration scenarios.
                        invalid = valid;
                    }
                }

                _log.Information("Authorize identifier: {identifier}", identifier);
                _log.Verbose("Challenge types available: {challenges}", authorization.Challenges.Select(x => x.Type ?? "[Unknown]"));
                var challenge = authorization.Challenges.FirstOrDefault(c => string.Equals(c.Type, options.ChallengeType, StringComparison.CurrentCultureIgnoreCase));
                if (challenge == null)
                {
                    if (authorization.Status == AcmeClient.AuthorizationValid)
                    {
                        var usedType = authorization.Challenges.
                                       Where(x => x.Status == AcmeClient.AuthorizationValid).
                                       FirstOrDefault();
                        _log.Warning("Expected challenge type {type} not available for {identifier}, already validated using {valided}.",
                                     options.ChallengeType,
                                     authorization.Identifier.Value,
                                     usedType?.Type ?? "[unknown]");
                        return(valid);
                    }
                    else
                    {
                        _log.Error("Expected challenge type {type} not available for {identifier}.",
                                   options.ChallengeType,
                                   authorization.Identifier.Value);
                        invalid.Error = "Expected challenge type not available";
                        return(invalid);
                    }
                }

                // We actually have to do validation now
                using var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier);
                try
                {
                    validationPlugin = validation.Resolve <IValidationPlugin>();
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error resolving validation plugin");
                }
                if (validationPlugin == null)
                {
                    _log.Error("Validation plugin not found or not created.");
                    invalid.Error = "Validation plugin not found or not created.";
                    return(invalid);
                }
                var(disabled, disabledReason) = validationPlugin.Disabled;
                if (disabled)
                {
                    _log.Error($"Validation plugin is not available. {disabledReason}");
                    invalid.Error = "Validation plugin is not available.";
                    return(invalid);
                }
                _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})",
                                 identifier,
                                 options.ChallengeType,
                                 options.Name);
                try
                {
                    var details = await client.DecodeChallengeValidation(authorization, challenge);

                    await validationPlugin.PrepareChallenge(details);
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error preparing for challenge answer");
                    invalid.Error = "Error preparing for challenge answer";
                    return(invalid);
                }

                _log.Debug("Submitting challenge answer");
                challenge = await client.AnswerChallenge(challenge);

                if (challenge.Status != AcmeClient.AuthorizationValid)
                {
                    if (challenge.Error != null)
                    {
                        _log.Error(challenge.Error.ToString());
                    }
                    _log.Error("Authorization result: {Status}", challenge.Status);
                    invalid.Error = challenge.Error;
                    return(invalid);
                }
                else
                {
                    _log.Information("Authorization result: {Status}", challenge.Status);
                    return(valid);
                }
            }
            catch (Exception ex)
            {
                _log.Error("Error authorizing {renewal}", targetPart);
                _exceptionHandler.HandleException(ex);
                invalid.Error = ex.Message;
                return(invalid);
            }
            finally
            {
                if (validationPlugin != null)
                {
                    try
                    {
                        _log.Verbose("Starting post-validation cleanup");
                        await validationPlugin.CleanUp();

                        _log.Verbose("Post-validation cleanup was succesful");
                    }
                    catch (Exception ex)
                    {
                        _log.Warning("An error occured during post-validation cleanup: {ex}", ex.Message);
                    }
                }
            }
        }