Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/WebAppSSLManager/AzureHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,11 @@ public static async Task<bool> NeedsNewCertificateAsync()
{
ResourceConfiguration resource = await GetResourceConfigurationAsync();

// Not filtering by issuer here: Let's Encrypt rotates its intermediate CA name over time
// (was "Let's Encrypt Authority X3", then "R3", now "R11"/"R12"/"R13"/"YR1", etc.), so any
// hardcoded issuer match goes stale. ExistingCertificates is already scoped to this hostname
// by GetResourceConfigurationAsync, so picking the latest-expiring one here is sufficient.
IAppServiceCertificate existingCert = resource.ExistingCertificates?
.Where(c => c.Issuer.Contains(Constants.DefaultCA))
.OrderByDescending(c => c.ExpirationDate)
.FirstOrDefault();

Expand Down
7 changes: 7 additions & 0 deletions src/WebAppSSLManager/CertificatesHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ public static async Task<bool> GetCertificateAsync()
await AzureHelper.RemoveDNSVerificationTXTRecord(recordName); //to be sure we start clean
await AzureHelper.CreateDNSVerificationTXTRecord(recordName, dnsTxt);

// Validating immediately after creating the TXT record races Azure DNS propagation: Let's
// Encrypt can check before the record is live, which fails the challenge and flips the
// authorization out of "pending" - so every retry after that first failure errors with
// "authorization must be pending" instead of actually retrying. Give DNS time to propagate first.
_logger.LogInformation($" Waiting {Settings.WaitTimeBeforeValidate.TotalSeconds}s for DNS propagation before validating...");
await Task.Delay(Settings.WaitTimeBeforeValidate);

_logger.LogInformation($" Validating DNS authorization challenge. Can take up to 90 seconds...");
var validatedChallege = await dnsChallenge.Validate();
var waitUntil = DateTime.Now.AddSeconds(90);
Expand Down
1 change: 1 addition & 0 deletions src/WebAppSSLManager/Models/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public class Constants
public const string DefaultCA = "Let's Encrypt Authority";
public const int DefaultBatchSize = 0;
public static readonly TimeSpan DefaultTimeBeforeExpiryToRenewCertificate = TimeSpan.FromDays(30);
public static readonly TimeSpan DefaultWaitTimeBeforeValidate = TimeSpan.FromSeconds(15);
}
}
9 changes: 9 additions & 0 deletions src/WebAppSSLManager/Models/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static class Settings
public static bool UseStaging { get; private set; }
public static int BatchSize { get; private set; }
public static TimeSpan TimeBeforeExpiryToRenew { get; private set; }
public static TimeSpan WaitTimeBeforeValidate { get; private set; }

public static void Init(ILogger logger)
{
Expand Down Expand Up @@ -97,6 +98,14 @@ public static void Init(ILogger logger)
_logger.LogWarning("TimeBeforeExpiryToRenew environment variable is null or invalid. Reverting to default");
TimeBeforeExpiryToRenew = Constants.DefaultTimeBeforeExpiryToRenewCertificate;
}

if (int.TryParse(Environment.GetEnvironmentVariable("WaitTimeBeforeValidateInSeconds"), out int waitSeconds) && waitSeconds >= 0)
WaitTimeBeforeValidate = TimeSpan.FromSeconds(waitSeconds);
else
{
_logger.LogWarning("WaitTimeBeforeValidateInSeconds environment variable is null or invalid. Reverting to default");
WaitTimeBeforeValidate = Constants.DefaultWaitTimeBeforeValidate;
}
}
}
}