예제 #1
0
        internal async Task <IChallengeValidationDetails> DecodeChallengeValidation(acme.Authorization auth, acme.Challenge challenge)
        {
            var client = await GetClient();

            return(AuthorizationDecoder.DecodeChallengeValidation(auth, challenge.Type, client.Signer));
        }
예제 #2
0
        /// <summary>
        /// Make sure we have authorization for every host in target
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private async Task HandleChallenge(ExecutionContext context, TargetPart targetPart, acme.Authorization authorization)
        {
            var valid      = false;
            var client     = context.Scope.Resolve <AcmeClient>();
            var identifier = authorization.Identifier.Value;
            var options    = context.Renewal.ValidationPluginOptions;
            IValidationPlugin?validationPlugin = null;

            using var validation = _scopeBuilder.Validation(context.Scope, options, targetPart, identifier);
            try
            {
                if (authorization.Status == AcmeClient.AuthorizationValid)
                {
                    _log.Information("Cached authorization result for {identifier}: {Status}", identifier, authorization.Status);
                    if (!context.RunLevel.HasFlag(RunLevel.Test) &&
                        !context.RunLevel.HasFlag(RunLevel.IgnoreCache))
                    {
                        return;
                    }
                    // Used to make --force or --test re-validation errors non-fatal
                    _log.Information("Handling challenge anyway because --test and/or --force is active");
                    valid = true;
                }

                _log.Information("Authorize identifier {identifier}", identifier);
                _log.Verbose("Initial authorization status: {status}", authorization.Status);
                _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 (valid)
                    {
                        var usedType = authorization.Challenges.
                                       Where(x => x.Status == AcmeClient.ChallengeValid).
                                       FirstOrDefault();
                        _log.Warning("Expected challenge type {type} not available for {identifier}, already validated using {valided}.",
                                     options.ChallengeType,
                                     authorization.Identifier.Value,
                                     usedType?.Type ?? "[unknown]");
                        return;
                    }
                    else
                    {
                        _log.Error("Expected challenge type {type} not available for {identifier}.",
                                   options.ChallengeType,
                                   authorization.Identifier.Value);
                        context.Result.AddErrorMessage("Expected challenge type not available", !valid);
                        return;
                    }
                }
                else
                {
                    _log.Verbose("Initial challenge status: {status}", challenge.Status);
                    if (challenge.Status == AcmeClient.ChallengeValid)
                    {
                        // We actually should not get here because if one of the
                        // challenges is valid, the authorization itself should also
                        // be valid.
                        if (!context.RunLevel.HasFlag(RunLevel.Test) &&
                            !context.RunLevel.HasFlag(RunLevel.IgnoreCache))
                        {
                            _log.Information("Cached challenge result: {Status}", authorization.Status);
                            return;
                        }
                    }
                }

                // We actually have to do validation now
                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");
                    context.Result.AddErrorMessage("Validation plugin not found or not created", !valid);
                    return;
                }
                var(disabled, disabledReason) = validationPlugin.Disabled;
                if (disabled)
                {
                    _log.Error($"Validation plugin is not available. {disabledReason}");
                    context.Result.AddErrorMessage("Validation plugin is not available", !valid);
                    return;
                }
                _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");
                    context.Result.AddErrorMessage("Error preparing for challenge answer", !valid);
                    return;
                }

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

                if (challenge.Status != AcmeClient.ChallengeValid)
                {
                    if (challenge.Error != null)
                    {
                        _log.Error(challenge.Error.ToString());
                    }
                    _log.Error("Authorization result: {Status}", challenge.Status);
                    context.Result.AddErrorMessage(challenge.Error?.ToString() ?? "Unspecified error", !valid);
                    return;
                }
                else
                {
                    _log.Information("Authorization result: {Status}", challenge.Status);
                    return;
                }
            }
            catch (Exception ex)
            {
                _log.Error("Error authorizing {renewal}", targetPart);
                var message = _exceptionHandler.HandleException(ex);
                context.Result.AddErrorMessage(message, !valid);
            }
            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);
                    }
                }
            }
        }