diff options
Diffstat (limited to 'src/main.lib/RenewalExecutor.cs')
-rw-r--r-- | src/main.lib/RenewalExecutor.cs | 225 |
1 files changed, 134 insertions, 91 deletions
diff --git a/src/main.lib/RenewalExecutor.cs b/src/main.lib/RenewalExecutor.cs index 70a1e12..94665dc 100644 --- a/src/main.lib/RenewalExecutor.cs +++ b/src/main.lib/RenewalExecutor.cs @@ -46,9 +46,10 @@ namespace PKISharp.WACS using var es = _scopeBuilder.Execution(ts, renewal, runLevel); // Generate the target var targetPlugin = es.Resolve<ITargetPlugin>(); - if (targetPlugin.Disabled.Item1) + var (disabled, disabledReason) = targetPlugin.Disabled; + if (disabled) { - throw new Exception($"Target plugin is not available. {targetPlugin.Disabled.Item2}"); + throw new Exception($"Target plugin is not available. {disabledReason}"); } var target = await targetPlugin.Generate(); if (target is INull) @@ -77,7 +78,7 @@ namespace PKISharp.WACS var cache = cs.CachedInfo(renewal, target); if (cache != null) { - _log.Information(LogType.All, "Renewal for {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate()); + _log.Information("Renewal for {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate()); return null; } else if (!renewal.New) @@ -98,19 +99,30 @@ namespace PKISharp.WACS // Create the order var client = es.Resolve<AcmeClient>(); var identifiers = target.GetHosts(false); + _log.Verbose("Creating certificate order for hosts: {identifiers}", identifiers); var order = await client.CreateOrder(identifiers); // Check if the order is valid - if (order.Payload.Status != AcmeClient.OrderReady && - order.Payload.Status != AcmeClient.OrderPending) + if ((order.Payload.Status != AcmeClient.OrderReady && + order.Payload.Status != AcmeClient.OrderPending) || + order.Payload.Error != null) { - return OnRenewFail(new Challenge() { Error = order.Payload.Error }); + _log.Error("Failed to create order {url}: {detail}", order.OrderUrl, order.Payload.Error.Detail); + return OnRenewFail(new Challenge() { Error = "Unable to create order" }); + } + else + { + _log.Verbose("Order {url} created", order.OrderUrl); } // Answer the challenges foreach (var authUrl in order.Payload.Authorizations) { // Get authorization details + _log.Verbose("Handle authorization {n}/{m}", + order.Payload.Authorizations.ToList().IndexOf(authUrl) + 1, + order.Payload.Authorizations.Length + 1); + var authorization = await client.GetAuthorizationDetails(authUrl); // Find a targetPart that matches the challenge @@ -145,11 +157,12 @@ namespace PKISharp.WACS var errors = challenge?.Error; if (errors != null) { - _log.Error("ACME server reported:"); - _log.Error("{@value}", errors); + return new RenewResult($"Authorization failed: {errors.ToString()}"); + } + else + { + return new RenewResult($"Authorization failed"); } - return new RenewResult("Authorization failed"); - } /// <summary> @@ -163,9 +176,13 @@ namespace PKISharp.WACS { var certificateService = renewalScope.Resolve<ICertificateService>(); var csrPlugin = target.CsrBytes == null ? renewalScope.Resolve<ICsrPlugin>() : null; - if (csrPlugin != null && csrPlugin.Disabled.Item1) + if (csrPlugin != null) { - return new RenewResult($"CSR plugin is not available. {csrPlugin.Disabled.Item2}"); + var (disabled, disabledReason) = csrPlugin.Disabled; + if (disabled) + { + return new RenewResult($"CSR plugin is not available. {disabledReason}"); + } } var oldCertificate = certificateService.CachedInfo(renewal); var newCertificate = await certificateService.RequestCertificate(csrPlugin, runLevel, renewal, target, order); @@ -208,9 +225,10 @@ namespace PKISharp.WACS { _log.Information("Store with {name}...", storeOptions.Name); } - if (storePlugin.Disabled.Item1) + var (disabled, disabledReason) = storePlugin.Disabled; + if (disabled) { - return new RenewResult($"Store plugin is not available. {storePlugin.Disabled.Item2}"); + return new RenewResult($"Store plugin is not available. {disabledReason}"); } await storePlugin.Save(newCertificate); storePlugins.Add(storePlugin); @@ -247,9 +265,10 @@ namespace PKISharp.WACS { _log.Information("Installing with {name}...", installOptions.Name); } - if (installPlugin.Disabled.Item1) + var (disabled, disabledReason) = installPlugin.Disabled; + if (disabled) { - return new RenewResult($"Installation plugin is not available. {installPlugin.Disabled.Item2}"); + return new RenewResult($"Installation plugin is not available. {disabledReason}"); } await installPlugin.Install(storePlugins, newCertificate, oldCertificate); } @@ -338,76 +357,123 @@ namespace PKISharp.WACS var valid = new Challenge { Status = AcmeClient.AuthorizationValid }; var client = execute.Resolve<AcmeClient>(); var identifier = authorization.Identifier.Value; + IValidationPlugin? validationPlugin = null; 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 + if (authorization.Status == AcmeClient.AuthorizationValid) { - using var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier); - IValidationPlugin? validationPlugin = null; - try + if (!runLevel.HasFlag(RunLevel.Test) && + !runLevel.HasFlag(RunLevel.IgnoreCache)) { - validationPlugin = validation.Resolve<IValidationPlugin>(); + _log.Information("Cached authorization result: {Status}", authorization.Status); + return valid; } - catch (Exception ex) + + if (runLevel.HasFlag(RunLevel.IgnoreCache)) { - _log.Error(ex, "Error resolving validation plugin"); + // 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; } - if (validationPlugin == null) - { - _log.Error("Validation plugin not found or not created."); - return invalid; - } - if (validationPlugin.Disabled.Item1) + } + + _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) { - _log.Error($"Validation plugin is not available. {validationPlugin.Disabled.Item2}"); - return invalid; - } - var challenge = authorization.Challenges.FirstOrDefault(c => string.Equals(c.Type, options.ChallengeType, StringComparison.CurrentCultureIgnoreCase)); - if (challenge == null) + 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; } + } - 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; - } + // 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.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.Debug("Submitting challenge answer"); + challenge = await client.AnswerChallenge(challenge); + if (challenge.Status != AcmeClient.AuthorizationValid) + { + if (challenge.Error != null) { - _log.Error(ex, "Error preparing for challenge answer"); - return invalid; + _log.Error(challenge.Error.ToString()); } - - _log.Debug("Submitting challenge answer"); - challenge = await client.AnswerChallenge(challenge); - + _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"); @@ -418,31 +484,8 @@ namespace PKISharp.WACS { _log.Warning("An error occured during post-validation cleanup: {ex}", ex.Message); } - - 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; - } } } } |