-
Notifications
You must be signed in to change notification settings - Fork 103
Addition of an option to throttle the signing requests. #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
32438aa
697ce60
4a1a803
f344781
64c4c3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,6 +86,9 @@ internal sealed class SignCommand | |
[Option("-mdop | --max-degree-of-parallelism", "The maximum number of concurrent signing operations.", CommandOptionType.SingleValue), Range(-1, int.MaxValue)] | ||
public int? MaxDegreeOfParallelism { get; set; } | ||
|
||
[Option("-st | --signing-throttle", "Controls the rate of signing operations. If this value is specified it indicates the number of seconds to hold off between each signing operation. Only valid when parallelism is disabled.", CommandOptionType.SingleValue), Range(-1, int.MaxValue)] | ||
public int? SigningThrottle { get; set; } | ||
|
||
[Option("--colors", "Enable color output on the command line.", CommandOptionType.NoValue)] | ||
public bool Colors { get; set; } = false; | ||
|
||
|
@@ -96,7 +99,9 @@ internal sealed class SignCommand | |
[Argument(0, "file", "The path to the file.")] | ||
public string[] Files { get; set; } = Array.Empty<string>(); | ||
|
||
private static DateTime _lastSigningOperation; | ||
private HashSet<string> _allFiles; | ||
|
||
public HashSet<string> AllFiles | ||
{ | ||
get | ||
|
@@ -152,6 +157,11 @@ private ValidationResult OnValidate(ValidationContext context, CommandLineContex | |
return new ValidationResult("Cannot use '--timestamp-rfc3161' and '--timestamp-authenticode' options together.", new[] { nameof(Rfc3161Timestamp), nameof(AuthenticodeTimestamp) }); | ||
} | ||
|
||
if (SigningThrottle is > 0 && MaxDegreeOfParallelism > 1) | ||
{ | ||
return new ValidationResult("Cannot use '--signing-throttle' and '--max-degree-of-parallelism' options together."); | ||
} | ||
|
||
if (KeyVaultClientId.Present && !KeyVaultClientSecret.Present) | ||
{ | ||
return new ValidationResult("Must supply '--azure-key-vault-client-secret' when using '--azure-key-vault-client-id'.", new[] { nameof(KeyVaultClientSecret) }); | ||
|
@@ -169,7 +179,7 @@ private ValidationResult OnValidate(ValidationContext context, CommandLineContex | |
{ | ||
return new ValidationResult("At least one file must be specified to sign."); | ||
} | ||
foreach(var file in AllFiles) | ||
foreach (var file in AllFiles) | ||
{ | ||
if (!File.Exists(file)) | ||
{ | ||
|
@@ -254,6 +264,13 @@ public async Task<int> OnExecuteAsync(CommandLineApplication app, IConsole conso | |
{ | ||
performPageHashing = false; | ||
} | ||
if (SigningThrottle is > 0) | ||
{ | ||
logger?.LogTrace($"Forcing MaxDegreeOfParallelism to 1 because signing throttling is requested."); | ||
MaxDegreeOfParallelism = 1; | ||
_lastSigningOperation = DateTime.Now.AddSeconds(SigningThrottle.Value * -1); | ||
} | ||
|
||
var configurationDiscoverer = new KeyVaultConfigurationDiscoverer(logger); | ||
var materializedResult = await configurationDiscoverer.Materialize(configuration); | ||
AzureKeyVaultMaterializedConfiguration materialized; | ||
|
@@ -279,6 +296,7 @@ public async Task<int> OnExecuteAsync(CommandLineApplication app, IConsole conso | |
{ | ||
options.MaxDegreeOfParallelism = MaxDegreeOfParallelism.Value; | ||
} | ||
|
||
logger.LogTrace("Creating context"); | ||
|
||
using (var keyVault = RSAFactory.Create(materialized.TokenCredential, materialized.KeyId, materialized.PublicCertificate)) | ||
|
@@ -294,6 +312,33 @@ public async Task<int> OnExecuteAsync(CommandLineApplication app, IConsole conso | |
{ | ||
return state; | ||
} | ||
|
||
if (SigningThrottle is > 0) | ||
{ | ||
DateTime goTime = _lastSigningOperation.AddSeconds(SigningThrottle.Value); | ||
|
||
if (goTime >= DateTime.Now) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have some concerns around this since DateTime.Now is not monotonic - for example, a Daylight Saving Time. Let's say the Using Can we re-work this to use DateTimeOffset.UtcNow? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could possibly use System.Threading.PeriodicTimer instead, which was added at some point, but I'd need to check the version availability for it. You just call the wait method on the class; it'll return immediately if there's already been one or more ticks since the method was last called, or it will wait until there is one and then return. |
||
{ | ||
logger.LogTrace("Holding off between signing requests until {time}.", goTime); | ||
|
||
while (goTime >= DateTime.Now && !cancellationSource.IsCancellationRequested) | ||
{ | ||
Thread.Sleep(500); | ||
} | ||
|
||
logger.LogTrace("Continuing signing operation."); | ||
} | ||
|
||
if (cancellationSource.IsCancellationRequested) | ||
{ | ||
pls.Stop(); | ||
} | ||
if (pls.IsStopped) | ||
{ | ||
return state; | ||
} | ||
} | ||
|
||
using (logger.BeginScope("File: {Id}", filePath)) | ||
{ | ||
logger.LogInformation("Signing file."); | ||
|
@@ -315,6 +360,8 @@ public async Task<int> OnExecuteAsync(CommandLineApplication app, IConsole conso | |
break; | ||
} | ||
|
||
_lastSigningOperation = DateTime.Now; | ||
|
||
if (result == S_OK) | ||
{ | ||
logger.LogInformation("Signing completed successfully."); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be static? Does it even need to be a field, or can it be part of the state in
Parallel.ForEach
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a while since I wrote this and I'll have to double check whether throttling limits it you to a single request thread. I have a feeling I made it so that it did; if I did then there's not really any need for it to be static and I'll pull it in to the state.