예제 #1
0
        private async void TestWebhook_Click(object sender, EventArgs e)
        {
            try
            {
                Button_TestWebhook.IsEnabled = false;

                var config = MainViewModel.SelectedItem.RequestConfig;
                if (!Uri.TryCreate(config.WebhookUrl, UriKind.Absolute, out var result))
                {
                    MessageBox.Show($"The webhook url must be a valid url.", SR.ManagedItemSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                if (string.IsNullOrEmpty(config.WebhookMethod))
                {
                    MessageBox.Show($"The webhook method must be selected.", SR.ManagedItemSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                bool forSuccess = config.WebhookTrigger == Webhook.ON_SUCCESS;
                var(success, status) = await Webhook.SendRequest(config, forSuccess);

                string completed = success ? SR.succeed : SR.failed;
                MessageBox.Show(string.Format(SR.ManagedItemSettings_WebhookRequestResult, completed, status), SR.ManagedItemSettings_WebhookTest, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format(SR.ManagedItemSettings_WebhookRequestError, ex.Message), SR.ManagedItemSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            finally
            {
                Button_TestWebhook.IsEnabled = true;
            }
        }
예제 #2
0
        private async void TestWebhook_Click(object sender, EventArgs e)
        {
            try
            {
                Button_TestWebhook.IsEnabled = false;

                var config = ItemViewModel.SelectedItem.RequestConfig;

                if (!Uri.TryCreate(config.WebhookUrl, UriKind.Absolute, out var result))
                {
                    MessageBox.Show($"The webhook url must be a valid url.", SR.ManagedCertificateSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                if (string.IsNullOrEmpty(config.WebhookMethod))
                {
                    MessageBox.Show($"The webhook method must be selected.", SR.ManagedCertificateSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                var forSuccess = config.WebhookTrigger == Webhook.ON_SUCCESS;

                var webhookResult = await Webhook.SendRequest(
                    new Webhook.WebhookConfig
                {
                    Url         = config.WebhookUrl,
                    Trigger     = config.WebhookTrigger,
                    ContentType = config.WebhookContentType,
                    ContentBody = config.WebhookContentBody,
                    Method      = config.WebhookMethod
                }, ItemViewModel.SelectedItem, forSuccess);

                var completed = webhookResult.Success ? SR.succeed : SR.failed;

                MessageBox.Show(string.Format(SR.ManagedCertificateSettings_WebhookRequestResult, completed, webhookResult.StatusCode), SR.ManagedCertificateSettings_WebhookTest, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format(SR.ManagedCertificateSettings_WebhookRequestError, ex.Message), SR.ManagedCertificateSettings_WebhookTestFailed, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            finally
            {
                Button_TestWebhook.IsEnabled = true;
            }
        }
예제 #3
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(ManagedSite managedSite, IProgress <RequestProgressState> progress = null)
        {
            // FIXME: refactor into different concerns, there's way too much being done here
            if (managedSite.RequestConfig.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP && managedSite.RequestConfig.PerformExtensionlessConfigChecks)
            {
                ReportProgress(progress, new RequestProgressState {
                    IsRunning = true, CurrentState = RequestState.Running, Message = Certify.CoreSR.CertifyManager_PerformingConfigTests
                });

                var testResult = await TestChallenge(managedSite, isPreviewMode : false);

                if (!testResult.IsOK)
                {
                    return(new CertificateRequestResult {
                        ManagedItem = managedSite, IsSuccess = false, Message = String.Join("; ", testResult.FailedItemSummary), Result = testResult.Result
                    });
                }
            }

            return(await Task.Run(async() =>
            {
                // start with a failure result, set to success when succeeding
                var result = new CertificateRequestResult {
                    ManagedItem = managedSite, IsSuccess = false, Message = ""
                };

                var config = managedSite.RequestConfig;
                try
                {
                    // run pre-request script, if set
                    if (!string.IsNullOrEmpty(config.PreRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PreRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Pre-Request Script output: \n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Pre-Request Script error:\n{ex.Message}");
                        }
                    }

                    // if the script has requested the certificate request to be aborted, skip the request
                    if (result.Abort)
                    {
                        LogMessage(managedSite.Id, $"Certificate Request Aborted: {managedSite.Name}");
                        result.Message = Certify.CoreSR.CertificateRequestWasAbortedByPSScript;
                        goto CertRequestAborted;
                    }

                    LogMessage(managedSite.Id, $"Beginning Certificate Request Process: {managedSite.Name}");

                    //enable or disable EFS flag on private key certs based on preference
                    if (CoreAppSettings.Current.EnableEFS)
                    {
                        _vaultProvider.EnableSensitiveFileEncryption();
                    }

                    //primary domain and each subject alternative name must now be registered as an identifier with LE and validated
                    ReportProgress(progress, new RequestProgressState {
                        IsRunning = true, CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_RegisterDomainIdentity
                    });

                    await Task.Delay(200); //allow UI update, we should we using async calls instead

                    List <string> allDomains = new List <string> {
                        config.PrimaryDomain
                    };

                    if (config.SubjectAlternativeNames != null)
                    {
                        allDomains.AddRange(config.SubjectAlternativeNames);
                    }

                    // begin by assuming all identifiers are valid
                    bool allIdentifiersValidated = true;

                    if (config.ChallengeType == null)
                    {
                        config.ChallengeType = ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP;
                    }

                    List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();
                    var distinctDomains = allDomains.Distinct();

                    string failureSummaryMessage = null;

                    // perform validation process for each domain
                    foreach (var domain in distinctDomains)
                    {
                        //begin authorization process (register identifier, request authorization if not already given)
                        var domainIdentifierId = _vaultProvider.ComputeDomainIdentifierId(domain);

                        LogMessage(managedSite.Id, $"Attempting Domain Validation: {domain}", LogItemType.CertificateRequestStarted);
                        ReportProgress(progress, string.Format(Certify.CoreSR.CertifyManager_RegisteringAndValidatingX0, domain));

                        //TODO: make operations async and yield IO of vault

                        /*var authorization = await Task.Run(() =>
                         * {
                         *  return vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);
                         * });*/

                        // begin authorization by registering the domain identifier. This may return
                        // an already validated authorization or we may still have to complete the
                        // authorization challenge. When rate limits are encountered, this step may fail.
                        var authorization = _vaultProvider.BeginRegistrationAndValidation(config, domainIdentifierId, challengeType: config.ChallengeType, domain: domain);

                        if (authorization != null && authorization.Identifier != null)
                        {
                            // check if authorization is pending, it may already be valid if an
                            // existing authorization was reused
                            if (authorization.Identifier.IsAuthorizationPending)
                            {
                                if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                                {
                                    ReportProgress(progress, string.Format(Certify.CoreSR.CertifyManager_PerformingChallengeResponseViaIISX0, domain));

                                    // ask LE to check our answer to their authorization challenge
                                    // (http-01 or tls-sni-01), LE will then attempt to fetch our
                                    // answer, if all accessible and correct (authorized) LE will
                                    // then allow us to request a certificate
                                    authorization = _vaultProvider.PerformIISAutomatedChallengeResponse(_iisManager, managedSite, authorization);

                                    // pass authorization log items onto main log

                                    /*authorization.LogItems?.ForEach((msg) =>
                                     * {
                                     *  if (msg != null) LogMessage(managedSite.Id, msg, LogItemType.GeneralInfo);
                                     * });*/

                                    if ((config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP && config.PerformExtensionlessConfigChecks && !authorization.ExtensionlessConfigCheckedOK) ||
                                        (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI && config.PerformTlsSniBindingConfigChecks && !authorization.TlsSniConfigCheckedOK))
                                    {
                                        //if we failed the config checks, report any errors
                                        LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_FailedPrerequisiteCheck, managedSite.ItemType), LogItemType.CertficateRequestFailed);

                                        _siteManager.StoreSettings();

                                        if (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP)
                                        {
                                            result.Message = string.Format(CoreSR.CertifyManager_AutomateConfigurationCheckFailed_HTTP, domain);
                                        }

                                        if (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI)
                                        {
                                            result.Message = Certify.CoreSR.CertifyManager_AutomateConfigurationCheckFailed_SNI;
                                        }

                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Error, Message = result.Message, Result = result
                                        });

                                        break;
                                    }
                                    else
                                    {
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Running, Message = string.Format(CoreSR.CertifyManager_ReqestValidationFromLetsEncrypt, domain)
                                        });
                                        try
                                        {
                                            //ask LE to validate our challenge response
                                            _vaultProvider.SubmitChallenge(domainIdentifierId, config.ChallengeType);

                                            bool identifierValidated = _vaultProvider.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                            if (!identifierValidated)
                                            {
                                                var identifierInfo = _vaultProvider.GetDomainIdentifier(domain);
                                                var errorMsg = identifierInfo?.ValidationError;
                                                var errorType = identifierInfo?.ValidationErrorType;

                                                failureSummaryMessage = string.Format(CoreSR.CertifyManager_DomainValidationFailed, domain, errorMsg);
                                                ReportProgress(progress, new RequestProgressState {
                                                    CurrentState = RequestState.Error, Message = failureSummaryMessage
                                                }, managedSite.Id);

                                                allIdentifiersValidated = false;
                                            }
                                            else
                                            {
                                                ReportProgress(progress, new RequestProgressState {
                                                    CurrentState = RequestState.Running, Message = string.Format(CoreSR.CertifyManager_DomainValidationCompleted, domain)
                                                }, managedSite.Id);

                                                identifierAuthorizations.Add(authorization);
                                            }
                                        }
                                        finally
                                        {
                                            // clean up challenge answers
                                            // (.well-known/acme-challenge/* files for http-01 or iis
                                            // bindings for tls-sni-01)
                                            authorization.Cleanup();
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // we already have a completed authorization, check it's valid
                                if (authorization.Identifier.Status == "valid")
                                {
                                    LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_DomainValidationSkipVerifed, domain));

                                    identifierAuthorizations.Add(new PendingAuthorization {
                                        Identifier = authorization.Identifier
                                    });
                                }
                                else
                                {
                                    var errorMsg = "";
                                    if (authorization?.Identifier != null)
                                    {
                                        errorMsg = authorization.Identifier.ValidationError;
                                        var errorType = authorization.Identifier.ValidationErrorType;
                                    }

                                    failureSummaryMessage = $"Domain validation failed: {domain} \r\n{errorMsg}";

                                    LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_DomainValidationFailed, domain));

                                    allIdentifiersValidated = false;
                                }
                            }
                        }
                        else
                        {
                            // could not begin authorization : TODO: pass error from authorization
                            // step to UI

                            var lastActionLogItem = _vaultProvider.GetLastActionLogItem();
                            var actionLogMsg = "";
                            if (lastActionLogItem != null)
                            {
                                actionLogMsg = lastActionLogItem.ToString();
                            }

                            LogMessage(managedSite.Id, $"Could not begin authorization for domain with Let's Encrypt: { domain } {(authorization?.AuthorizationError != null ? authorization?.AuthorizationError : "Could not register domain identifier")} - {actionLogMsg}");

                            /*if (authorization != null && authorization.LogItems != null)
                             * {
                             *  LogMessage(managedSite.Id, authorization.LogItems);
                             * }*/
                            allIdentifiersValidated = false;
                        }

                        // abandon authorization attempts if one of our domains has failed verification
                        if (!allIdentifiersValidated)
                        {
                            break;
                        }
                    }

                    //check if all identifiers have a valid authorization
                    if (identifierAuthorizations.Count != distinctDomains.Count())
                    {
                        allIdentifiersValidated = false;
                    }

                    if (allIdentifiersValidated)
                    {
                        string primaryDnsIdentifier = identifierAuthorizations.First().Identifier.Alias;
                        string[] alternativeDnsIdentifiers = identifierAuthorizations.Select(i => i.Identifier.Alias).ToArray();

                        ReportProgress(progress, new RequestProgressState {
                            CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_RequestCertificate
                        }, managedSite.Id);

                        // Perform CSR request
                        // FIXME: make call async
                        var certRequestResult = _vaultProvider.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);

                        if (certRequestResult.IsSuccess)
                        {
                            ReportProgress(progress, new RequestProgressState {
                                CurrentState = RequestState.Success, Message = CoreSR.CertifyManager_CompleteRequest
                            }, managedSite.Id);

                            string pfxPath = certRequestResult.Result.ToString();

                            // update managed site summary
                            try
                            {
                                var certInfo = CertificateManager.LoadCertificate(pfxPath);
                                managedSite.DateStart = certInfo.NotBefore;
                                managedSite.DateExpiry = certInfo.NotAfter;
                                managedSite.DateRenewed = DateTime.Now;

                                managedSite.CertificatePath = pfxPath;
                                managedSite.CertificateRevoked = false;

                                //ensure certificate contains all the requested domains
                                var subjectNames = certInfo.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.UpnName, false);

                                //FIXME: LogMessage(managedSite.Id, "New certificate contains following domains: " + subjectNames, LogItemType.GeneralInfo);
                            }
                            catch (Exception)
                            {
                                LogMessage(managedSite.Id, "Failed to parse certificate dates", LogItemType.GeneralError);
                            }

                            if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                            {
                                ReportProgress(progress, new RequestProgressState {
                                    CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_AutoBinding
                                });

                                // Install certificate into certificate store and bind to IIS site
                                if (_iisManager.InstallCertForRequest(managedSite, pfxPath, cleanupCertStore: true))
                                {
                                    //all done
                                    LogMessage(managedSite.Id, CoreSR.CertifyManager_CompleteRequestAndUpdateBinding, LogItemType.CertificateRequestSuccessful);

                                    _siteManager.UpdatedManagedSite(managedSite);

                                    result.IsSuccess = true;
                                    result.Message = string.Format(CoreSR.CertifyManager_CertificateInstalledAndBindingUpdated, config.PrimaryDomain);
                                    ReportProgress(progress, new RequestProgressState {
                                        IsRunning = false, CurrentState = RequestState.Success, Message = result.Message
                                    });
                                }
                                else
                                {
                                    result.Message = string.Format(CoreSR.CertifyManager_CertificateInstallFailed, pfxPath);
                                    LogMessage(managedSite.Id, result.Message, LogItemType.GeneralError);
                                }
                            }
                            else
                            {
                                //user has opted for manual binding of certificate

                                _siteManager.UpdatedManagedSite(managedSite);

                                result.IsSuccess = true;
                                result.Message = string.Format(CoreSR.CertifyManager_CertificateCreatedForBinding, pfxPath);
                                LogMessage(managedSite.Id, result.Message, LogItemType.CertificateRequestSuccessful);
                            }
                        }
                        else
                        {
                            result.Message = string.Format(CoreSR.CertifyManager_LetsEncryptServiceTimeout, certRequestResult.ErrorMessage ?? "");
                            LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                        }
                    }
                    else
                    {
                        result.Message = string.Format(CoreSR.CertifyManager_ValidationForChallengeNotSuccess, (failureSummaryMessage != null ? failureSummaryMessage : ""));
                        LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    }

                    // Goto label for aborted certificate request
                    CertRequestAborted : { }
                }
                catch (Exception exp)
                {
                    result.IsSuccess = false;
                    result.Message = string.Format(Certify.CoreSR.CertifyManager_RequestFailed, managedSite.Name, exp.Message, exp);
                    LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    //LogMessage(managedSite.Id, String.Join("\r\n", _vaultProvider.GetActionSummary())); FIXME: needs to be filtered in managed site
                    System.Diagnostics.Debug.WriteLine(exp.ToString());
                }
                finally
                {
                    // if the request was not aborted, perform post-request actions
                    if (!result.Abort)
                    {
                        // run post-request script, if set
                        if (!string.IsNullOrEmpty(config.PostRequestPowerShellScript))
                        {
                            try
                            {
                                string scriptOutput = await PowerShellManager.RunScript(result, config.PostRequestPowerShellScript);
                                LogMessage(managedSite.Id, $"Post-Request Script output:\n{scriptOutput}");
                            }
                            catch (Exception ex)
                            {
                                LogMessage(managedSite.Id, $"Post-Request Script error: {ex.Message}");
                            }
                        }
                        // run webhook triggers, if set
                        if ((config.WebhookTrigger == Webhook.ON_SUCCESS && result.IsSuccess) ||
                            (config.WebhookTrigger == Webhook.ON_ERROR && !result.IsSuccess) ||
                            (config.WebhookTrigger == Webhook.ON_SUCCESS_OR_ERROR))
                        {
                            try
                            {
                                var(success, code) = await Webhook.SendRequest(config, result.IsSuccess);
                                LogMessage(managedSite.Id, $"Webhook invoked: Url: {config.WebhookUrl}, Success: {success}, StatusCode: {code}");
                            }
                            catch (Exception ex)
                            {
                                LogMessage(managedSite.Id, $"Webhook error: {ex.Message}");
                            }
                        }
                    }
                }
                return result;
            }));
        }