Пример #1
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);
        }
Пример #2
0
        public async Task <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);

            var dnsSettings = _dataContext.GetDnsSettings();

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

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

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

            try
            {
                if (acmeCert.IsDnsChallengeType())
                {
                    _logger.LogDebug($"[{acmeCert.Subject}] - setting DNS records");
                    var dnsSetResult = _certesAcmeProvider.SetDnsRecords(dnsSettings);

                    if (dnsSetResult)
                    {
                        _logger.LogDebug($"[{acmeCert.Subject}] - validating DNS records");
                        var dnsValidateResult = await _certesAcmeProvider.ValidateDnsRecords();
                    }
                }

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

                _dataContext.SaveChanges();

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

                if (acmeCert.IsDnsChallengeType())
                {
                    _logger.LogDebug($"[{acmeCert.Subject}] - cleaning up DNS records");
                    _certesAcmeProvider.CleanupDnsRecords(dnsSettings);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                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
            {
                var notificationSettings = _dataContext.NotificationSettings
                                           .Include(x => x.ApplicationUser)
                                           .Where(x => x.AcquisitionFailureAlerts == true)
                                           .ToList();

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

                _notificationService.SendCertAcquitionFailureNotification(notificationSettings, acmeOrder, lastValidAcmeOrder);
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Error sending certificate acquisition failure notification");
            }

            return(acmeOrder);
        }
Пример #3
0
        public async Task Invoke(HttpContext httpContext,
                                 ILogger <SetupAcmeCertMiddleware> logger,
                                 CertesAcmeProvider certes,
                                 DataContext dataContext,
                                 IOptionsSnapshot <HttpServer> httpServerOptions)
        {
            if (httpContext.Request.Method != "POST")
            {
                httpContext.Response.Redirect("/");
                return;
            }

            var host = httpServerOptions.Value.SiteHostname;

            await httpContext.Response.WriteAsync(ConsoleHeader);

            await httpContext.Response.WriteAsync($@"
        Starting certificate acquisition for {host}... <br />");

            var acmeCert = await dataContext.AcmeCertificates
                           .Include(x => x.Key)
                           .Include(x => x.AcmeAccount)
                           .ThenInclude(x => x.Key)
                           .FirstAsync(x => x.Subject == host);

            await httpContext.Response.WriteAsync($@"
        Initializing ACME client and ensuring account... <br />");

            certes.Initialize(acmeCert);

            // Begin the order
            await httpContext.Response.WriteAsync($@"
        Creating order... <br />");

            var acmeOrder = await certes.BeginOrder();

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

            // Validate (i.e. ask ACME to check via HTTP-01 or DNS-01)
            await httpContext.Response.WriteAsync($@"
        Requesting ACME validation... <br />");

            await certes.Validate();

            dataContext.SaveChanges();

            // Complete the order (i.e. obtain the certifiate)
            await httpContext.Response.WriteAsync($@"
        Completing order... (this can take up to 30 seconds)<br />");

            await certes.Complete();

            // Remove old ACME requests because they're irrelevant
            await httpContext.Response.WriteAsync($@"
        Cleaning up... <br />");

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

            await httpContext.Response.WriteAsync($@"
        Done. Status: {acmeOrder.Status}... <br />");

            bool restart = false;

            if (!acmeOrder.Completed)
            {
                var errors = acmeOrder.Errors.Replace("\r\n", "<br />");
                await httpContext.Response.WriteAsync($@"
        <div class=""red""><p>Errors:<br />{errors}</p></div><br />");
            }
            else
            {
                restart = true;
                await httpContext.Response.WriteAsync(@"
        <br />
        Setup finished successfully! 
        <br />
        <hr />
        <br />
        Certera is restarting...
        <br />
        <script>
            var restartFinished = setInterval(checkLoaded, 5000);
            var tries = 5;
        
            function checkLoaded() {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        window.location.href=""/"";
                    }
                };
                xhttp.open(""GET"", ""/api/test"", true);
                xhttp.send();
            }
        </script>
        <noscript>
            Please wait 10 seconds for server to restart. Then, <a href=""/"">click here to continue</a>.
        </noscript>");
            }

            await httpContext.Response.WriteAsync(@"
    </p>
</body>
</html>");

            if (restart)
            {
                Program.Restart();
            }
        }
Пример #4
0
        public async Task <IActionResult> OnPostAsync(long id, string action, string key)
        {
            AcmeCertificate = await _context.AcmeCertificates
                              .Include(a => a.AcmeAccount)
                              .ThenInclude(a => a.Key)
                              .Include(a => a.AcmeOrders)
                              .ThenInclude(o => o.DomainCertificate)
                              .FirstOrDefaultAsync(m => m.AcmeCertificateId == id);

            if (AcmeCertificate == null)
            {
                return(NotFound());
            }

            switch (action.ToLower())
            {
            case "keychange":
                switch (key)
                {
                case "apikey1":
                    AcmeCertificate.ApiKey1 = ApiKeyGenerator.CreateApiKey();
                    break;

                case "apikey2":
                    AcmeCertificate.ApiKey2 = ApiKeyGenerator.CreateApiKey();
                    break;
                }

                await _context.SaveChangesAsync();

                break;

            case "ocspcheck":
                try
                {
                    var order = AcmeCertificate.GetLatestValidAcmeOrder();
                    if (order?.Certificate != null)
                    {
                        var client = new OcspClient();
                        var status = client.GetOcspStatus(order.Certificate);
                        OcspStatus = status.ToString();
                    }
                    else
                    {
                        OcspStatus = "No certificate";
                    }
                }
                catch (Exception e)
                {
                    _logger.LogWarning($"Error obtaining OCSP status:{e.Message}");
                    OcspStatus = "Error";
                }
                break;

            case "revoke":
            {
                var order = AcmeCertificate.GetLatestValidAcmeOrder();
                if (order?.RawDataPem != null)
                {
                    _certesAcmeProvider.Initialize(AcmeCertificate);

                    var cert   = new Certes.Acme.CertificateChain(order.RawDataPem);
                    var reason = (RevocationReason)Enum.Parse(typeof(RevocationReason), RevocationReason, true);
                    await _certesAcmeProvider.Revoke(cert.Certificate.ToDer(), reason);

                    StatusMessage = "Certificate revocation submitted";
                }
                break;
            }
            }

            return(Page());
        }