diff options
author | Wouter Tinus <win.acme.simple@gmail.com> | 2020-03-19 08:38:04 +0100 |
---|---|---|
committer | Wouter Tinus <win.acme.simple@gmail.com> | 2020-03-19 08:38:04 +0100 |
commit | dab13c23a3e42e3312d0f3cda70c76e8a873b0a2 (patch) | |
tree | 13229839a83dfa8e2677135c773765639c9ed725 /src | |
parent | a76c5951817cb982e4fcc910bd707f5aa5bae7a3 (diff) | |
download | letsencrypt-win-simple-dab13c23a3e42e3312d0f3cda70c76e8a873b0a2.zip letsencrypt-win-simple-dab13c23a3e42e3312d0f3cda70c76e8a873b0a2.tar.gz letsencrypt-win-simple-dab13c23a3e42e3312d0f3cda70c76e8a873b0a2.tar.bz2 |
Order level cache to prevent rate limits
Diffstat (limited to 'src')
-rw-r--r-- | src/main.lib/Clients/Acme/OrderManager.cs | 129 | ||||
-rw-r--r-- | src/main.lib/RenewalExecutor.cs | 2 | ||||
-rw-r--r-- | src/main.lib/Services/CertificateService.cs | 12 |
3 files changed, 90 insertions, 53 deletions
diff --git a/src/main.lib/Clients/Acme/OrderManager.cs b/src/main.lib/Clients/Acme/OrderManager.cs index e06ee77..795b120 100644 --- a/src/main.lib/Clients/Acme/OrderManager.cs +++ b/src/main.lib/Clients/Acme/OrderManager.cs @@ -1,16 +1,19 @@ using ACMESharp.Protocol; using Newtonsoft.Json; +using PKISharp.WACS.Configuration; using PKISharp.WACS.DomainObjects; using PKISharp.WACS.Extensions; using PKISharp.WACS.Services; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace PKISharp.WACS.Clients.Acme { + /// <summary> + /// The OrderManager makes sure that we don't hit rate limits + /// </summary> class OrderManager { private readonly ILogService _log; @@ -18,6 +21,7 @@ namespace PKISharp.WACS.Clients.Acme private readonly AcmeClient _client; private readonly DirectoryInfo _orderPath; private readonly ICertificateService _certificateService; + private const string _orderFileExtension = "order.json"; public OrderManager(ILogService log, ISettingsService settings, AcmeClient client, ICertificateService certificateService) @@ -29,27 +33,54 @@ namespace PKISharp.WACS.Clients.Acme _orderPath = new DirectoryInfo(Path.Combine(settings.Client.ConfigurationPath, "Orders")); } - public async Task<OrderDetails?> GetOrCreate(Renewal renewal, Target target) + /// <summary> + /// Get a previously cached order or if its too old, create a new one + /// </summary> + /// <param name="renewal"></param> + /// <param name="target"></param> + /// <returns></returns> + public async Task<OrderDetails?> GetOrCreate(Renewal renewal, Target target, RunLevel runLevel) { var cacheKey = _certificateService.CacheKey(renewal, target); - var identifiers = target.GetHosts(false); - var existingOrder = FindRecentOrder(identifiers, cacheKey); + var existingOrder = FindRecentOrder(cacheKey); if (existingOrder != null) { try { + if (runLevel.HasFlag(RunLevel.IgnoreCache)) + { + _log.Warning("Cached order available but not used with the --{switch} switch.", + nameof(MainArguments.Force).ToLower()); + return null; + } existingOrder = await RefreshOrder(existingOrder); - _log.Information("Reusing existing order"); - return existingOrder; + if (existingOrder.Payload.Status == AcmeClient.OrderValid) + { + _log.Warning("Using cached order. To force issue of a new certificate within {days} days, " + + "run with the --{switch} switch. Be ware that you might run into rate limits doing so.", + _settings.Cache.ReuseDays, + nameof(MainArguments.Force).ToLower()); + return existingOrder; + } + else + { + _log.Debug($"Cached order has status {existingOrder.Payload.Status}, discarding"); + } } catch (Exception ex) { - _log.Warning("Unable to refresh order: {ex}", ex.Message); + _log.Warning("Unable to refresh cached order: {ex}", ex.Message); } - } + } + var identifiers = target.GetHosts(false); return await CreateOrder(identifiers, cacheKey); } + /// <summary> + /// Update order details from the server + /// </summary> + /// <param name="order"></param> + /// <returns></returns> private async Task<OrderDetails> RefreshOrder(OrderDetails order) { _log.Debug("Refreshing order..."); @@ -60,7 +91,7 @@ namespace PKISharp.WACS.Clients.Acme private async Task<OrderDetails?> CreateOrder(IEnumerable<string> identifiers, string cacheKey) { - _log.Verbose("Creating certificate order for hosts: {identifiers}", identifiers); + _log.Verbose("Creating order for hosts: {identifiers}", identifiers); var order = await _client.CreateOrder(identifiers); // Check if the order is valid if ((order.Payload.Status != AcmeClient.OrderReady && @@ -84,33 +115,19 @@ namespace PKISharp.WACS.Clients.Acme /// </summary> /// <param name="identifiers"></param> /// <returns></returns> - private OrderDetails? FindRecentOrder(IEnumerable<string> identifiers, string cacheKey) + private OrderDetails? FindRecentOrder(string cacheKey) { - var fi = new FileInfo(Path.Combine(_orderPath.FullName, $"{cacheKey}.order.json")); - if (!fi.Exists) + DeleteStaleFiles(); + var fi = new FileInfo(Path.Combine(_orderPath.FullName, $"{cacheKey}.{_orderFileExtension}")); + if (!fi.Exists || !IsValid(fi)) { return null; } try { - if (fi.LastWriteTime > DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1)) - { - var content = File.ReadAllText(fi.FullName); - var order = JsonConvert.DeserializeObject<OrderDetails>(content); - if (order.Payload.Identifiers.Length == identifiers.Count()) - { - if (order.Payload.Identifiers.All(x => - string.Equals(x.Type, "dns", StringComparison.CurrentCultureIgnoreCase) && - identifiers.Contains(x.Value.ToLower()))) - { - return order; - } - } - } - else - { - fi.Delete(); - } + var content = File.ReadAllText(fi.FullName); + var order = JsonConvert.DeserializeObject<OrderDetails>(content); + return order; } catch (Exception ex) { @@ -120,25 +137,49 @@ namespace PKISharp.WACS.Clients.Acme } /// <summary> + /// Delete files that are not valid anymore + /// </summary> + private void DeleteStaleFiles() + { + if (_orderPath.Exists) + { + var orders = _orderPath.EnumerateFiles($"*.{_orderFileExtension}"); + foreach (var order in orders) + { + if (!IsValid(order)) + { + order.Delete(); + } + } + } + } + + /// <summary> + /// Test if a cached order file is still usable + /// </summary> + /// <returns></returns> + private bool IsValid(FileInfo order) => order.LastWriteTime > DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1); + + /// <summary> /// Save order to disk /// </summary> /// <param name="order"></param> private void SaveOrder(OrderDetails order, string cacheKey) { - //try - //{ - // if (!_orderPath.Exists) - // { - // _orderPath.Create(); - // } - // var content = JsonConvert.SerializeObject(order); - // var path = Path.Combine(_orderPath.FullName, $"{cacheKey}.order.json"); - // File.WriteAllText(path, content); - //} - //catch (Exception ex) - //{ - // _log.Warning("Unable to save order: {ex}", ex.Message); - //} + try + { + if (!_orderPath.Exists) + { + _orderPath.Create(); + } + var content = JsonConvert.SerializeObject(order); + var path = Path.Combine(_orderPath.FullName, $"{cacheKey}.{_orderFileExtension}"); + File.WriteAllText(path, content); + } + catch (Exception ex) + { + _log.Warning("Unable to write to order cache: {ex}", ex.Message); + } } } } diff --git a/src/main.lib/RenewalExecutor.cs b/src/main.lib/RenewalExecutor.cs index 05fad23..3fe05aa 100644 --- a/src/main.lib/RenewalExecutor.cs +++ b/src/main.lib/RenewalExecutor.cs @@ -98,7 +98,7 @@ namespace PKISharp.WACS // Create the order var orderManager = es.Resolve<OrderManager>(); - var order = await orderManager.GetOrCreate(renewal, target); + var order = await orderManager.GetOrCreate(renewal, target, runLevel); if (order == null) { return OnRenewFail(new Challenge() { Error = "Unable to create order" }); diff --git a/src/main.lib/Services/CertificateService.cs b/src/main.lib/Services/CertificateService.cs index 3a7be53..a0fabbe 100644 --- a/src/main.lib/Services/CertificateService.cs +++ b/src/main.lib/Services/CertificateService.cs @@ -285,20 +285,16 @@ namespace PKISharp.WACS.Services { if (runLevel.HasFlag(RunLevel.IgnoreCache)) { - _log.Warning("Cached certificate available but not used with the --{switch} switch. " + - "Use 'Manage renewals > Run renewal' in the main menu to run unscheduled " + - "renewals without hitting rate limits.", + _log.Warning("Cached certificate available on disk but not used due to --{switch} switch.", nameof(MainArguments.Force).ToLower()); } else { - _log.Warning("Using cached certificate for {friendlyName}. To force issue of a " + - "new certificate within {days} days, delete the .pfx file from the CertificatePath " + - "or run with the --{switch} switch. Be ware that you might run into rate " + - "limits doing so.", + _log.Warning("Using cached certificate for {friendlyName}. To force a new request of the " + + "certificate within {days} days, run with the --{switch} switch.", friendlyNameBase, _settings.Cache.ReuseDays, - nameof(MainArguments.Force).ToLower()) ; + nameof(MainArguments.Force).ToLower()); return cache; } } |