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; } }
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; } }
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; })); }