Пример #1
0
        public void SendDomainCertChangeNotification(IList <NotificationSetting> notificationSettings, IList <DomainCertificateChangeEvent> events)
        {
            bool canSendEmail = InitEmail(notificationSettings);

            foreach (var evt in events)
            {
                foreach (var notification in notificationSettings)
                {
                    if (canSendEmail && notification.SendEmailNotification)
                    {
                        try
                        {
                            _logger.LogInformation($"Sending change notification email for {evt.Domain.HostAndPort()}");

                            var recipients = new List <string>();
                            recipients.Add(notification.ApplicationUser.Email);
                            if (!string.IsNullOrWhiteSpace(notification.AdditionalRecipients))
                            {
                                recipients.AddRange(notification.AdditionalRecipients
                                                    .Split(',', ';', StringSplitOptions.RemoveEmptyEntries)
                                                    .Select(x => x.Trim()));
                            }

                            _mailSender.Send($"[certera] {evt.Domain.HostAndPort()} - certificate change notification",
                                             TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateChangeEmail,
                                                                           new
                            {
                                Domain             = evt.Domain.HostAndPort(),
                                NewThumbprint      = evt.NewDomainCertificate.Thumbprint,
                                NewPublicKey       = evt.NewDomainCertificate.Certificate.PublicKeyPinningHash(),
                                NewValidFrom       = evt.NewDomainCertificate.ValidNotBefore.ToShortDateString(),
                                NewValidTo         = evt.NewDomainCertificate.ValidNotAfter.ToShortDateString(),
                                PreviousThumbprint = evt.PreviousDomainCertificate.Thumbprint,
                                PreviousPublicKey  = evt.PreviousDomainCertificate.Certificate.PublicKeyPinningHash(),
                                PreviousValidFrom  = evt.PreviousDomainCertificate.ValidNotBefore.ToShortDateString(),
                                PreviousValidTo    = evt.PreviousDomainCertificate.ValidNotAfter.ToShortDateString()
                            }),
                                             recipients.ToArray());
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error sending certificate change notification email");
                        }
                    }

                    if (notification.SendSlackNotification)
                    {
                        try
                        {
                            _logger.LogInformation($"Sending change notification slack for {evt.Domain.HostAndPort()}");

                            var json = TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateChangeSlack,
                                                                     new
                            {
                                Domain             = evt.Domain.HostAndPort(),
                                NewThumbprint      = evt.NewDomainCertificate.Thumbprint,
                                NewPublicKey       = evt.NewDomainCertificate.Certificate.PublicKeyPinningHash(),
                                NewValidFrom       = evt.NewDomainCertificate.ValidNotBefore.ToShortDateString(),
                                NewValidTo         = evt.NewDomainCertificate.ValidNotAfter.ToShortDateString(),
                                PreviousThumbprint = evt.PreviousDomainCertificate.Thumbprint,
                                PreviousPublicKey  = evt.PreviousDomainCertificate.Certificate.PublicKeyPinningHash(),
                                PreviousValidFrom  = evt.PreviousDomainCertificate.ValidNotBefore.ToShortDateString(),
                                PreviousValidTo    = evt.PreviousDomainCertificate.ValidNotAfter.ToShortDateString()
                            });

                            SendSlack(notification.SlackWebhookUrl, json);
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error sending certificate change notification email");
                        }
                    }
                }

                // Mark the event as processed so it doesn't show up in the next query
                evt.DateProcessed = DateTime.UtcNow;
            }
        }
Пример #2
0
        public void SendExpirationNotification(NotificationSetting notificationSetting, Data.Views.TrackedCertificate expiringCert)
        {
            var days     = (int)Math.Floor(expiringCert.ValidTo.Value.Subtract(DateTime.Now).TotalDays);
            var daysText = $"{days} {(days == 1 ? " day" : "days")}";

            bool canSendEmail = InitEmail(new List <NotificationSetting> {
                notificationSetting
            });

            if (canSendEmail && notificationSetting.SendEmailNotification)
            {
                try
                {
                    _logger.LogInformation($"Sending certificate expiration notification email for {expiringCert.Subject}");

                    var recipients = new List <string>();
                    recipients.Add(notificationSetting.ApplicationUser.Email);
                    if (!string.IsNullOrWhiteSpace(notificationSetting.AdditionalRecipients))
                    {
                        recipients.AddRange(notificationSetting.AdditionalRecipients
                                            .Split(',', ';', StringSplitOptions.RemoveEmptyEntries)
                                            .Select(x => x.Trim()));
                    }

                    _mailSender.Send($"[certera] {expiringCert.Subject} - certificate expiration notification",
                                     TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateExpirationEmail,
                                                                   new
                    {
                        Domain     = expiringCert.Subject,
                        Thumbprint = expiringCert.Thumbprint,
                        DateTime   = expiringCert.ValidTo.ToString(),
                        DaysText   = daysText,
                        PublicKey  = expiringCert.PublicKeyHash,
                        ValidFrom  = expiringCert.ValidFrom.Value.ToShortDateString(),
                        ValidTo    = expiringCert.ValidTo.Value.ToShortDateString()
                    }),
                                     recipients.ToArray());
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error sending certificate expiration notification email");
                }
            }

            if (notificationSetting.SendSlackNotification)
            {
                try
                {
                    _logger.LogInformation($"Sending certificate expiration notification slack for {expiringCert.Subject}");

                    var json = TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateExpirationSlack,
                                                             new
                    {
                        Domain     = expiringCert.Subject,
                        Thumbprint = expiringCert.Thumbprint,
                        DateTime   = expiringCert.ValidTo.ToString(),
                        DaysText   = daysText,
                        PublicKey  = expiringCert.PublicKeyHash,
                        ValidFrom  = expiringCert.ValidFrom.Value.ToShortDateString(),
                        ValidTo    = expiringCert.ValidTo.Value.ToShortDateString()
                    });

                    SendSlack(notificationSetting.SlackWebhookUrl, json);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error sending certificate expiration notification slack");
                }
            }
        }
Пример #3
0
        public void SendCertAcquitionFailureNotification(IList <NotificationSetting> notificationSettings,
                                                         AcmeOrder acmeOrder, AcmeOrder lastValidAcmeOrder)
        {
            bool canSendEmail = InitEmail(notificationSettings);

            foreach (var notification in notificationSettings)
            {
                if (canSendEmail && notification.SendEmailNotification)
                {
                    try
                    {
                        var recipients = new List <string>();
                        recipients.Add(notification.ApplicationUser.Email);
                        if (!string.IsNullOrWhiteSpace(notification.AdditionalRecipients))
                        {
                            recipients.AddRange(notification.AdditionalRecipients
                                                .Split(',', ';', StringSplitOptions.RemoveEmptyEntries)
                                                .Select(x => x.Trim()));
                        }

                        string previousCertText = string.Empty;
                        string lastAcquiryText  = "Never";

                        if (lastValidAcmeOrder?.DomainCertificate != null)
                        {
                            lastAcquiryText = lastValidAcmeOrder.DateCreated.ToString();

                            var thumbprint = lastValidAcmeOrder.DomainCertificate.Thumbprint;
                            var publicKey  = lastValidAcmeOrder.DomainCertificate.Certificate.PublicKeyPinningHash();
                            var validFrom  = lastValidAcmeOrder.DomainCertificate.ValidNotBefore.ToShortDateString();
                            var validTo    = lastValidAcmeOrder.DomainCertificate.ValidNotAfter.ToShortDateString();

                            var sb = new StringBuilder();
                            sb.AppendLine("<u>Current certificate details</u>");
                            sb.AppendLine();
                            sb.AppendLine("<b>Thumbprint</b>");
                            sb.AppendLine($"{thumbprint}");
                            sb.AppendLine();
                            sb.AppendLine("<b>Public Key (hash)</b>");
                            sb.AppendLine($"{publicKey}");
                            sb.AppendLine();
                            sb.AppendLine("<b>Valid</b>");
                            sb.AppendLine($"{validFrom} to {validTo}");
                            previousCertText = sb.ToString();
                        }

                        _logger.LogInformation($"Sending certificate acquisition failure notification email for {acmeOrder.AcmeCertificate.Name}");

                        _mailSender.Send($"[certera] {acmeOrder.AcmeCertificate.Name} - certificate acquisition failure notification",
                                         TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateAcquisitionFailureEmail,
                                                                       new
                        {
                            Domain = acmeOrder.AcmeCertificate.Subject,
                            Error  = acmeOrder.Errors,
                            PreviousCertificateDetails = previousCertText,
                            LastAcquiryText            = lastAcquiryText
                        }),
                                         recipients.ToArray());
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error sending certificate acquisition failure notification email");
                    }
                }

                if (notification.SendSlackNotification)
                {
                    try
                    {
                        _logger.LogInformation($"Sending acquisition failure notification slack for {acmeOrder.AcmeCertificate.Name}");

                        string previousCertText = string.Empty;
                        string lastAcquiryText  = "Never";

                        if (lastValidAcmeOrder?.DomainCertificate != null)
                        {
                            lastAcquiryText = lastValidAcmeOrder.DateCreated.ToString();

                            var thumbprint = lastValidAcmeOrder.DomainCertificate.Thumbprint;
                            var publicKey  = lastValidAcmeOrder.DomainCertificate.Certificate.PublicKeyPinningHash();
                            var validFrom  = lastValidAcmeOrder.DomainCertificate.ValidNotBefore.ToShortDateString();
                            var validTo    = lastValidAcmeOrder.DomainCertificate.ValidNotAfter.ToShortDateString();

                            var sb = new StringBuilder();
                            sb.Append("*Current certificate details*\n");
                            sb.Append($"*Thumbprint:*\n{thumbprint}\n");
                            sb.Append($"*Public Key (hash):*\n{publicKey}\n");
                            sb.Append($"*Valid:*\n{validFrom} to {validTo}");
                            previousCertText = sb.ToString();
                        }

                        var json = TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateAcquisitionFailureSlack,
                                                                 new
                        {
                            Domain = acmeOrder.AcmeCertificate.Subject,
                            Error  = acmeOrder.Errors,
                            PreviousCertificateDetails = previousCertText,
                            LastAcquiryText            = lastAcquiryText
                        });

                        SendSlack(notification.SlackWebhookUrl, json);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error sending certificate acquisition failure notification slack");
                    }
                }
            }
        }
Пример #4
0
        public async Task <Data.Models.AcmeOrder> AcquireAcmeCert(long id, bool userRequested = false)
        {
            var acmeCert = await _dataContext.AcmeCertificates
                           .Include(x => x.Key)
                           .Include(x => x.AcmeAccount)
                           .ThenInclude(x => x.Key)
                           .SingleAsync(x => x.AcmeCertificateId == id);

            _logger.LogDebug($"[{acmeCert.Subject}] - starting certificate acquisition");
            _certesAcmeProvider.Initialize(acmeCert);

            _logger.LogDebug($"[{acmeCert.Subject}] - creating order");
            var acmeOrder = await _certesAcmeProvider.BeginOrder();

            _dataContext.AcmeOrders.Add(acmeOrder);
            _dataContext.SaveChanges();

            _logger.LogDebug($"[{acmeCert.Subject}] - requestion ACME validation");
            await _certesAcmeProvider.Validate();

            _dataContext.SaveChanges();

            _logger.LogDebug($"[{acmeCert.Subject}] - completing order");
            await _certesAcmeProvider.Complete();

            acmeOrder.AcmeRequests.Clear();
            _dataContext.SaveChanges();

            if (!acmeOrder.Completed)
            {
                _logger.LogError($"[{acmeCert.Subject}] - error obtaining certificate: {acmeOrder.Errors}");
            }

            _logger.LogDebug($"[{acmeCert.Subject}] - done");

            if (acmeOrder.Completed || userRequested)
            {
                return(acmeOrder);
            }

            try
            {
                if (string.IsNullOrWhiteSpace(_mailSenderInfo?.Value?.Host))
                {
                    _logger.LogWarning("SMTP not configured. Unable to send certificate change notifications.");
                    return(acmeOrder);
                }

                var usersToNotify = _dataContext.NotificationSettings
                                    .Include(x => x.ApplicationUser)
                                    .Where(x => x.AcquisitionFailureAlerts == true);
                _mailSender.Initialize(_mailSenderInfo.Value);

                foreach (var user in usersToNotify)
                {
                    var recipients = new List <string>();
                    recipients.Add(user.ApplicationUser.Email);
                    if (!string.IsNullOrWhiteSpace(user.AdditionalRecipients))
                    {
                        recipients.AddRange(user.AdditionalRecipients.Split(',', ';', StringSplitOptions.RemoveEmptyEntries));
                    }

                    var lastValidAcmeOrder = _dataContext.AcmeOrders
                                             .Include(x => x.DomainCertificate)
                                             .Where(x => x.AcmeCertificateId == acmeOrder.AcmeCertificateId)
                                             .OrderByDescending(x => x.DateCreated)
                                             .FirstOrDefault(x => x.Status == AcmeOrderStatus.Completed);

                    string previousCertText = string.Empty;
                    string lastAcquiryText  = "Never";

                    if (lastValidAcmeOrder?.DomainCertificate != null)
                    {
                        lastAcquiryText = lastValidAcmeOrder.DateCreated.ToString();

                        var thumbprint = lastValidAcmeOrder.DomainCertificate.Thumbprint;
                        var publicKey  = lastValidAcmeOrder.DomainCertificate.Certificate.PublicKeyPinningHash();
                        var validFrom  = lastValidAcmeOrder.DomainCertificate.ValidNotBefore.ToShortDateString();
                        var validTo    = lastValidAcmeOrder.DomainCertificate.ValidNotAfter.ToShortDateString();

                        var sb = new StringBuilder();
                        sb.AppendLine("<u>Current certificate details</u>");
                        sb.AppendLine();
                        sb.AppendLine("<b>Thumbprint</b>");
                        sb.AppendLine($"{thumbprint}");
                        sb.AppendLine();
                        sb.AppendLine("<b>Public Key (hash)</b>");
                        sb.AppendLine($"{publicKey}");
                        sb.AppendLine();
                        sb.AppendLine("<b>Valid</b>");
                        sb.AppendLine($"{validFrom} to {validTo}");
                        previousCertText = sb.ToString();
                    }

                    _logger.LogInformation($"Sending acquiry failure notification email for {acmeOrder.AcmeCertificate.Name}");

                    _mailSender.Send($"[certera] {acmeOrder.AcmeCertificate.Name} - certificate acquisition failure notification",
                                     TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateAcquisitionFailure,
                                                                   new
                    {
                        Domain = acmeOrder.AcmeCertificate.Subject,
                        Error  = acmeOrder.Errors,
                        PreviousCertificateDetails = previousCertText,
                        LastAcquiryText            = lastAcquiryText
                    }),
                                     recipients.ToArray());
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Error sending certificate change notification email");
            }
            return(acmeOrder);
        }
        private void CheckAndSendNotification(NotificationSetting notificationSetting,
                                              KeyValuePair <NotificationEvent, List <TrackedCertificate> > bucket,
                                              Dictionary <string, UserNotification> userNotifications,
                                              DataContext dataContext,
                                              MailSender mailSender)
        {
            foreach (var expiringCert in bucket.Value)
            {
                try
                {
                    var key = GetNotificationEventKey(notificationSetting.ApplicationUserId, expiringCert.CertId, bucket.Key);
                    if (!userNotifications.ContainsKey(key))
                    {
                        if (!ShouldSend(notificationSetting, bucket.Key))
                        {
                            continue;
                        }

                        var recipients = new List <string>();
                        recipients.Add(notificationSetting.ApplicationUser.Email);
                        if (!string.IsNullOrWhiteSpace(notificationSetting.AdditionalRecipients))
                        {
                            recipients.AddRange(notificationSetting.AdditionalRecipients.Split(',', ';', StringSplitOptions.RemoveEmptyEntries));
                        }

                        var days     = (int)Math.Floor(expiringCert.ValidTo.Value.Subtract(DateTime.Now).TotalDays);
                        var daysText = $"{days} {(days == 1 ? " day" : "days")}";

                        // Show some debug info regarding the reason for the email being sent out
                        _logger.LogDebug($"Send key info: {key}.");

                        var sentNotifications = string.Join(Environment.NewLine, userNotifications.Keys);
                        _logger.LogDebug($"Sent notifications:{Environment.NewLine}{sentNotifications}");

                        _logger.LogInformation($"Sending expiration notification email for {expiringCert.Subject}");

                        mailSender.Send($"[certera] {expiringCert.Subject} - certificate expiration notification",
                                        TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateExpiration,
                                                                      new
                        {
                            Domain     = expiringCert.Subject,
                            Thumbprint = expiringCert.Thumbprint,
                            DateTime   = expiringCert.ValidTo.ToString(),
                            DaysText   = daysText,
                            PublicKey  = expiringCert.PublicKeyHash,
                            ValidFrom  = expiringCert.ValidFrom.Value.ToShortDateString(),
                            ValidTo    = expiringCert.ValidTo.Value.ToShortDateString()
                        }),
                                        recipients.ToArray());

                        // Save the notification so the user isn't notified again for this certificate and this time period
                        var userNotification = new UserNotification
                        {
                            ApplicationUser     = notificationSetting.ApplicationUser,
                            DateCreated         = DateTime.UtcNow,
                            DomainCertificateId = expiringCert.CertId,
                            NotificationEvent   = bucket.Key
                        };
                        dataContext.UserNotifications.Add(userNotification);

                        userNotifications.Add(key, userNotification);
                    }
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Error sending certificate expiration email");
                }
            }
        }
Пример #6
0
        private void RunNotificationCheck()
        {
            using (var scope = _services.CreateScope())
            {
                try
                {
                    var setupOptions = scope.ServiceProvider.GetService <IOptionsSnapshot <Setup> >();
                    if (!setupOptions.Value.Finished)
                    {
                        _logger.LogInformation("Skipping execution of certificate change notification service because setup is not complete.");
                        return;
                    }

                    var mailSenderInfo = scope.ServiceProvider.GetService <IOptionsSnapshot <MailSenderInfo> >();
                    if (string.IsNullOrWhiteSpace(mailSenderInfo?.Value?.Host))
                    {
                        _logger.LogWarning("SMTP not configured. Unable to send certificate change notifications.");
                        return;
                    }

                    using (var mailSender = scope.ServiceProvider.GetService <MailSender>())
                    {
                        mailSender.Initialize(mailSenderInfo.Value);

                        var dataContext = scope.ServiceProvider.GetService <DataContext>();

                        // Get the change events that were created when scans occurred
                        var events = dataContext.DomainCertificateChangeEvents
                                     .Include(x => x.Domain)
                                     .Include(x => x.NewDomainCertificate)
                                     .Include(x => x.PreviousDomainCertificate)
                                     .Where(x => x.DateProcessed == null)
                                     .ToList();

                        var usersToNotify = dataContext.NotificationSettings
                                            .Include(x => x.ApplicationUser)
                                            .Where(x => x.ChangeAlerts == true);

                        foreach (var evt in events)
                        {
                            foreach (var user in usersToNotify)
                            {
                                try
                                {
                                    var recipients = new List <string>();
                                    recipients.Add(user.ApplicationUser.Email);
                                    if (!string.IsNullOrWhiteSpace(user.AdditionalRecipients))
                                    {
                                        recipients.AddRange(user.AdditionalRecipients.Split(',', ';', StringSplitOptions.RemoveEmptyEntries));
                                    }

                                    _logger.LogInformation($"Sending change notification email for {evt.Domain.HostAndPort()}");

                                    mailSender.Send($"[certera] {evt.Domain.HostAndPort()} - certificate change notification",
                                                    TemplateManager.BuildTemplate(TemplateManager.NotificationCertificateChange,
                                                                                  new
                                    {
                                        Domain             = evt.Domain.HostAndPort(),
                                        NewThumbprint      = evt.NewDomainCertificate.Thumbprint,
                                        NewPublicKey       = evt.NewDomainCertificate.Certificate.PublicKeyPinningHash(),
                                        NewValidFrom       = evt.NewDomainCertificate.ValidNotBefore.ToShortDateString(),
                                        NewValidTo         = evt.NewDomainCertificate.ValidNotAfter.ToShortDateString(),
                                        PreviousThumbprint = evt.PreviousDomainCertificate.Thumbprint,
                                        PreviousPublicKey  = evt.PreviousDomainCertificate.Certificate.PublicKeyPinningHash(),
                                        PreviousValidFrom  = evt.PreviousDomainCertificate.ValidNotBefore.ToShortDateString(),
                                        PreviousValidTo    = evt.PreviousDomainCertificate.ValidNotAfter.ToShortDateString()
                                    }),
                                                    recipients.ToArray());
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "Error sending certificate change notification email");
                                }
                            }

                            // Mark the event as processed so it doesn't show up in the next query
                            evt.DateProcessed = DateTime.UtcNow;
                        }

                        // User notified, save the record
                        dataContext.SaveChanges();
                    }
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Certificate change notification job error.");
                }
            }
        }