/// <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); }
/// <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(); }); }
public ValidationContextParameters( AuthorizationContext authorization, TargetPart targetPart, ValidationPluginOptions options) { TargetPart = targetPart; OrderContext = authorization.Order; Authorization = authorization.Authorization; Options = options; }
/// <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(); })); }
/// <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); } } }
/// <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)); }
/// <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)); }
/// <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); } }
/// <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); } } }
/// <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); } }
/// <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); } }
/// <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); } } } }