/// <summary> /// Checks if we have to renew the specific certificate just yet. /// </summary> /// <param name="options"></param> /// <param name="cfg"></param> /// <param name="cancellationToken"></param> /// <returns>The cert if it is still valid according to rules in config. False otherwise.</returns> private async Task <ICertificate> GetExistingCertificateAsync( IAcmeOptions options, CertificateRenewalOptions cfg, CancellationToken cancellationToken) { if (cfg.Overrides.NewCertificate) { // ignore existing certificate _log.LogWarning($"Override '{nameof(cfg.Overrides.NewCertificate)}' is enabled, forcing certificate renewal."); return(null); } var certStore = _renewalOptionParser.ParseCertificateStore(cfg); // determine if renewal is needed based on existing cert var existingCert = await certStore.GetCertificateAsync(cancellationToken); // check if cert exists and that it is valid right now if (existingCert != null) { // handle cases of manually uploaded certificates if (!existingCert.Expires.HasValue) { throw new NotSupportedException($"Missing expiration value on certificate {existingCert.Name} (provider: {certStore.Type}). " + "Must be set to expiration date of the certificate."); } var now = DateTime.UtcNow; // must be valid now and some day in the future based on config expiration rule var isValidAlready = !existingCert.NotBefore.HasValue || existingCert.NotBefore.Value < now; var isStillValid = existingCert.Expires.Value.Date.AddDays(-options.RenewXDaysBeforeExpiry) >= now; if (isValidAlready && isStillValid) { _log.LogInformation($"Certificate {existingCert.Name} (from source: {certStore.Name}) is still valid until {existingCert.Expires.Value}. " + $"Will be renewed in {(int)(existingCert.Expires.Value - now).TotalDays - options.RenewXDaysBeforeExpiry} days. Skipping renewal."); return(existingCert); } var reason = !isValidAlready ? $"certificate won't be valid until {existingCert.NotBefore}" : $"renewal is demanded {options.RenewXDaysBeforeExpiry} days before expiry and it is currently {(int)(existingCert.Expires.Value - now).TotalDays} days before expiry"; _log.LogInformation($"Certificate {existingCert.Name} (from source: {certStore.Name}) is no longer up to date ({reason})."); } // either no cert or expired return(null); }
/// <summary> /// Checks if we have to renew the specific certificate just yet. /// </summary> /// <param name="options"></param> /// <param name="cfg"></param> /// <param name="cancellationToken"></param> /// <returns>The cert if it is still valid according to rules in config. False otherwise.</returns> private async Task <ICertificate> GetExistingCertificateAsync( IAcmeOptions options, CertificateRenewalOptions cfg, CancellationToken cancellationToken) { if (cfg.Overrides.ForceNewCertificates) { // if overrides contain domain whitelist then only ignore existing certificate if it is not matched if (cfg.Overrides.DomainsToUpdate.Any()) { if (cfg.Overrides.DomainsToUpdate.Any(domain => cfg.HostNames.Contains(domain, StringComparison.OrdinalIgnoreCase))) { _logger.LogWarning($"Override '{nameof(cfg.Overrides.ForceNewCertificates)}' is enabled, forcing certificate renewal."); return(null); } _logger.LogWarning($"Override '{nameof(cfg.Overrides.ForceNewCertificates)}' is enabled but certificate does not match any of the hostnames -> force renewal is not applied to this certificate."); } else { // ignore existing certificate _logger.LogWarning($"Override '{nameof(cfg.Overrides.ForceNewCertificates)}' is enabled, forcing certificate renewal."); return(null); } } var certStore = _renewalOptionParser.ParseCertificateStore(cfg); // determine if renewal is needed based on existing cert var existingCert = await certStore.GetCertificateAsync(cancellationToken); // check if cert exists and that it is valid right now if (existingCert != null) { // handle cases of manually uploaded certificates if (!existingCert.Expires.HasValue) { throw new NotSupportedException($"Missing expiration value on certificate {existingCert.Name} (provider: {certStore.Type}). " + "Must be set to expiration date of the certificate."); } var now = DateTime.UtcNow; // must be valid now and some day in the future based on config expiration rule var isValidAlready = !existingCert.NotBefore.HasValue || existingCert.NotBefore.Value < now; var isStillValid = existingCert.Expires.Value.Date.AddDays(-options.RenewXDaysBeforeExpiry) >= now; if (isValidAlready && isStillValid) { _logger.LogInformation($"Certificate {existingCert.Name} (from source: {certStore.Name}) is still valid until {existingCert.Expires.Value}. " + $"Will be renewed in {(int)(existingCert.Expires.Value - now).TotalDays - options.RenewXDaysBeforeExpiry} days. Skipping renewal."); // ensure cert covers all requested domains exactly (order doesn't matter, but one cert more or less does) var requestedDomains = cfg.HostNames .Select(s => s.ToLowerInvariant()) .OrderBy(s => s) .ToArray(); var certDomains = existingCert.HostNames .Select(s => s.ToLowerInvariant()) .OrderBy(s => s) .ToArray(); if (!requestedDomains.SequenceEqual(certDomains)) { // if not exact domains as requested consider invalid and issue a new cert return(null); } return(existingCert); } var reason = !isValidAlready ? $"certificate won't be valid until {existingCert.NotBefore}" : $"renewal is demanded {options.RenewXDaysBeforeExpiry} days before expiry and it is currently {(int)(existingCert.Expires.Value - now).TotalDays} days before expiry"; _logger.LogInformation($"Certificate {existingCert.Name} (from source: {certStore.Name}) is no longer up to date ({reason})."); } // either no cert or expired return(null); }