Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 2953a8f

Browse files
authored
[Repository Signing] Add Signature Validator (#426)
Introduces the validator that runs after a package is repository signed. Its purpose is to run all validations on the package's signature. See NuGet/NuGetGallery#5753
1 parent 0bb6322 commit 2953a8f

23 files changed

Lines changed: 741 additions & 107 deletions

File tree

src/NuGet.Services.Validation.Orchestrator/Job.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public class Job : JobBase
5757

5858
private const string VcsBindingKey = VcsSectionName;
5959
private const string PackageVerificationTopicClientBindingKey = "PackageVerificationTopicClient";
60-
private const string PackageSigningBindingKey = PackageSigningSectionName;
60+
private const string PackageSignatureBindingKey = PackageSigningSectionName;
6161
private const string PackageCertificatesBindingKey = PackageCertificatesSectionName;
6262
private const string ValidationStorageBindingKey = "ValidationStorage";
6363
private const string OrchestratorBindingKey = "Orchestrator";
@@ -206,7 +206,8 @@ private void ConfigureJobServices(IServiceCollection services, IConfigurationRoo
206206
services.AddTransient<IBrokeredMessageSerializer<CertificateValidationMessage>, CertificateValidationMessageSerializer>();
207207
services.AddTransient<IValidatorStateService, ValidatorStateService>();
208208
services.AddTransient<ISimpleCloudBlobProvider, SimpleCloudBlobProvider>();
209-
services.AddTransient<PackageSigningValidator>();
209+
services.AddTransient<PackageSignatureProcessor>();
210+
services.AddTransient<PackageSignatureValidator>();
210211
services.AddTransient<MailSenderConfiguration>(serviceProvider =>
211212
{
212213
var smtpConfigurationAccessor = serviceProvider.GetRequiredService<IOptionsSnapshot<SmtpConfiguration>>();
@@ -326,21 +327,21 @@ private static IServiceProvider CreateProvider(IServiceCollection services)
326327
IMessageHandler<PackageValidationMessageData>>(
327328
OrchestratorBindingKey);
328329

329-
ConfigurePackageSigningValidator(containerBuilder);
330+
ConfigurePackageSigningValidators(containerBuilder);
330331
ConfigurePackageCertificatesValidator(containerBuilder);
331332

332333
return new AutofacServiceProvider(containerBuilder.Build());
333334
}
334335

335-
private static void ConfigurePackageSigningValidator(ContainerBuilder builder)
336+
private static void ConfigurePackageSigningValidators(ContainerBuilder builder)
336337
{
337338
// Configure the validator state service for the package certificates validator.
338339
builder
339340
.RegisterType<ValidatorStateService>()
340341
.WithParameter(
341342
(pi, ctx) => pi.ParameterType == typeof(string),
342-
(pi, ctx) => ValidatorName.PackageSigning)
343-
.Keyed<IValidatorStateService>(PackageSigningBindingKey);
343+
(pi, ctx) => ValidatorName.PackageSignatureProcessor)
344+
.Keyed<IValidatorStateService>(PackageSignatureBindingKey);
344345

345346
// Configure the package signature verification enqueuer.
346347
builder
@@ -350,18 +351,25 @@ private static void ConfigurePackageSigningValidator(ContainerBuilder builder)
350351

351352
return new TopicClientWrapper(configuration.ConnectionString, configuration.TopicPath);
352353
})
353-
.Keyed<ITopicClient>(PackageSigningBindingKey);
354+
.Keyed<ITopicClient>(PackageSignatureBindingKey);
354355

355356
builder
356357
.RegisterType<ProcessSignatureEnqueuer>()
357-
.WithKeyedParameter(typeof(ITopicClient), PackageSigningBindingKey)
358+
.WithKeyedParameter(typeof(ITopicClient), PackageSignatureBindingKey)
358359
.As<IProcessSignatureEnqueuer>();
359360

360-
// Configure the package signing validator.
361+
// Configure the package signature validators. The processor runs before packages are
362+
// repository signed and can strip unacceptable repository signatures. The validator
363+
// runs after packages are repository signed.
361364
builder
362-
.RegisterType<PackageSigningValidator>()
363-
.WithKeyedParameter(typeof(IValidatorStateService), PackageSigningBindingKey)
364-
.As<PackageSigningValidator>();
365+
.RegisterType<PackageSignatureProcessor>()
366+
.WithKeyedParameter(typeof(IValidatorStateService), PackageSignatureBindingKey)
367+
.As<PackageSignatureProcessor>();
368+
369+
builder
370+
.RegisterType<PackageSignatureValidator>()
371+
.WithKeyedParameter(typeof(IValidatorStateService), PackageSignatureBindingKey)
372+
.As<PackageSignatureValidator>();
365373
}
366374

367375
private static void ConfigurePackageCertificatesValidator(ContainerBuilder builder)

src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,16 @@
6262
<Compile Include="MessageService.cs" />
6363
<Compile Include="OrchestrationRunner.cs" />
6464
<Compile Include="Configuration\OrchestrationRunnerConfiguration.cs" />
65+
<Compile Include="PackageSigning\ProcessSignature\BaseSignatureProcessor.cs" />
6566
<Compile Include="PackageSigning\ProcessSignature\IProcessSignatureEnqueuer.cs" />
66-
<Compile Include="PackageSigning\ProcessSignature\PackageSigningValidator.cs" />
67+
<Compile Include="PackageSigning\ProcessSignature\PackageSignatureProcessor.cs" />
6768
<Compile Include="PackageSigning\ProcessSignature\ProcessSignatureConfiguration.cs" />
6869
<Compile Include="PackageSigning\ProcessSignature\ProcessSignatureEnqueuer.cs" />
6970
<Compile Include="PackageSigning\ValidateCertificate\IValidateCertificateEnqueuer.cs" />
7071
<Compile Include="PackageSigning\ValidateCertificate\PackageCertificatesValidator.cs" />
7172
<Compile Include="PackageSigning\ValidateCertificate\ValidateCertificateConfiguration.cs" />
7273
<Compile Include="PackageSigning\ValidateCertificate\ValidateCertificateEnqueuer.cs" />
74+
<Compile Include="PackageSigning\ProcessSignature\PackageSignatureValidator.cs" />
7375
<Compile Include="PackageStatusProcessor.cs" />
7476
<Compile Include="PackageValidationMessageDataSerializer.cs" />
7577
<Compile Include="Program.cs" />

src/NuGet.Services.Validation.Orchestrator/PackageSigning/ProcessSignature/PackageSigningValidator.cs renamed to src/NuGet.Services.Validation.Orchestrator/PackageSigning/ProcessSignature/BaseSignatureProcessor.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,28 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Diagnostics;
65
using System.Threading.Tasks;
76
using Microsoft.Extensions.Logging;
8-
using NuGet.Jobs.Validation;
97
using NuGet.Jobs.Validation.PackageSigning.Storage;
108
using NuGet.Jobs.Validation.Storage;
119
using NuGet.Services.Validation.Orchestrator.Telemetry;
1210

1311
namespace NuGet.Services.Validation.PackageSigning.ProcessSignature
1412
{
15-
[ValidatorName(ValidatorName.PackageSigning)]
16-
public class PackageSigningValidator : IProcessor
13+
public abstract class BaseSignatureProcessor
1714
{
1815
private readonly IValidatorStateService _validatorStateService;
1916
private readonly IProcessSignatureEnqueuer _signatureVerificationEnqueuer;
2017
private readonly ISimpleCloudBlobProvider _blobProvider;
2118
private readonly ITelemetryService _telemetryService;
22-
private readonly ILogger<PackageSigningValidator> _logger;
19+
private readonly ILogger<BaseSignatureProcessor> _logger;
2320

24-
public PackageSigningValidator(
21+
public BaseSignatureProcessor(
2522
IValidatorStateService validatorStateService,
2623
IProcessSignatureEnqueuer signatureVerificationEnqueuer,
2724
ISimpleCloudBlobProvider blobProvider,
2825
ITelemetryService telemetryService,
29-
ILogger<PackageSigningValidator> logger)
26+
ILogger<BaseSignatureProcessor> logger)
3027
{
3128
_validatorStateService = validatorStateService ?? throw new ArgumentNullException(nameof(validatorStateService));
3229
_signatureVerificationEnqueuer = signatureVerificationEnqueuer ?? throw new ArgumentNullException(nameof(signatureVerificationEnqueuer));
@@ -35,14 +32,14 @@ public PackageSigningValidator(
3532
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
3633
}
3734

38-
public async Task<IValidationResult> GetResultAsync(IValidationRequest request)
35+
public virtual async Task<IValidationResult> GetResultAsync(IValidationRequest request)
3936
{
4037
var validatorStatus = await _validatorStateService.GetStatusAsync(request);
4138

4239
return validatorStatus.ToValidationResult();
4340
}
4441

45-
public async Task<IValidationResult> StartAsync(IValidationRequest request)
42+
public virtual async Task<IValidationResult> StartAsync(IValidationRequest request)
4643
{
4744
var validatorStatus = await StartInternalAsync(request);
4845

@@ -68,6 +65,11 @@ public async Task CleanUpAsync(IValidationRequest request)
6865
await blob.DeleteIfExistsAsync();
6966
}
7067

68+
/// <summary>
69+
/// Whether the package MUST have an acceptable repository signature to pass validation.
70+
/// </summary>
71+
protected abstract bool RequiresRepositorySignature { get; }
72+
7173
private async Task<ValidatorStatus> StartInternalAsync(IValidationRequest request)
7274
{
7375
// Check that this is the first validation for this specific request.
@@ -86,15 +88,12 @@ private async Task<ValidatorStatus> StartInternalAsync(IValidationRequest reques
8688

8789
// Kick off the verification process. Note that the jobs will not verify the package until the
8890
// state of this validator has been persisted to the database.
89-
var stopwatch = Stopwatch.StartNew();
90-
91-
await _signatureVerificationEnqueuer.EnqueueVerificationAsync(request);
92-
93-
var result = await _validatorStateService.TryAddValidatorStatusAsync(request, validatorStatus, ValidationStatus.Incomplete);
94-
95-
_telemetryService.TrackDurationToStartPackageSigningValidator(stopwatch.Elapsed);
91+
using (_telemetryService.TrackDurationToStartPackageSigningValidator())
92+
{
93+
await _signatureVerificationEnqueuer.EnqueueProcessSignatureAsync(request, RequiresRepositorySignature);
9694

97-
return result;
95+
return await _validatorStateService.TryAddValidatorStatusAsync(request, validatorStatus, ValidationStatus.Incomplete);
96+
}
9897
}
9998
}
10099
}

src/NuGet.Services.Validation.Orchestrator/PackageSigning/ProcessSignature/IProcessSignatureEnqueuer.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@ namespace NuGet.Services.Validation.PackageSigning.ProcessSignature
1111
public interface IProcessSignatureEnqueuer
1212
{
1313
/// <summary>
14-
/// Kicks off the package verification process for the given request. Verification will begin when the
15-
/// <see cref="ValidationEntitiesContext"/> has a <see cref="ValidatorStatus"/> that matches the
16-
/// <see cref="IValidationRequest"/>'s validationId. Once verification completes, the <see cref="ValidatorStatus"/>'s
17-
/// State will be updated to "Succeeded" or "Failed".
14+
/// Processes the package's signatures, if any. Unacceptable repository signatures will be stripped off.
15+
/// Author signatures that fail trust or integrity verification will fail the validation.
1816
/// </summary>
17+
/// <remarks>
18+
/// Verification will begin when the <see cref="ValidationEntitiesContext"/> has a <see cref="ValidatorStatus"/>
19+
/// that matches the <see cref="IValidationRequest"/>'s validationId. Once verification completes,
20+
/// the <see cref="ValidatorStatus"/>'s State will be updated to "Succeeded" or "Failed".
21+
/// </remarks>
1922
/// <param name="request">The request that details the package to be verified.</param>
23+
/// <param name="requireRepositorySignature">
24+
/// If true, the package must have an acceptable repository signature to pass validation.
25+
/// </param>
2026
/// <returns>A task that will complete when the verification process has been queued.</returns>
21-
Task EnqueueVerificationAsync(IValidationRequest request);
27+
Task EnqueueProcessSignatureAsync(IValidationRequest request, bool requireRepositorySignature);
2228
}
2329
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Logging;
6+
using NuGet.Jobs.Validation;
7+
using NuGet.Jobs.Validation.PackageSigning.Storage;
8+
using NuGet.Jobs.Validation.Storage;
9+
using NuGet.Services.Validation.Orchestrator.Telemetry;
10+
11+
namespace NuGet.Services.Validation.PackageSigning.ProcessSignature
12+
{
13+
/// <summary>
14+
/// The processor that strips unacceptable repository signatures and then validates signed packages.
15+
/// This runs before a package is repository signed.
16+
/// </summary>
17+
[ValidatorName(ValidatorName.PackageSignatureProcessor)]
18+
public class PackageSignatureProcessor : BaseSignatureProcessor, IProcessor
19+
{
20+
private readonly IValidatorStateService _validatorStateService;
21+
private readonly IProcessSignatureEnqueuer _signatureVerificationEnqueuer;
22+
private readonly ISimpleCloudBlobProvider _blobProvider;
23+
private readonly ITelemetryService _telemetryService;
24+
private readonly ILogger<PackageSignatureProcessor> _logger;
25+
26+
public PackageSignatureProcessor(
27+
IValidatorStateService validatorStateService,
28+
IProcessSignatureEnqueuer signatureVerificationEnqueuer,
29+
ISimpleCloudBlobProvider blobProvider,
30+
ITelemetryService telemetryService,
31+
ILogger<PackageSignatureProcessor> logger)
32+
: base(validatorStateService, signatureVerificationEnqueuer, blobProvider, telemetryService, logger)
33+
{
34+
_validatorStateService = validatorStateService ?? throw new ArgumentNullException(nameof(validatorStateService));
35+
_signatureVerificationEnqueuer = signatureVerificationEnqueuer ?? throw new ArgumentNullException(nameof(signatureVerificationEnqueuer));
36+
_blobProvider = blobProvider ?? throw new ArgumentNullException(nameof(blobProvider));
37+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
38+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
39+
}
40+
41+
/// <summary>
42+
/// This processor runs before packages are repository signed. Unacceptable
43+
/// repository signatures, if present, will be stripped.
44+
/// </summary>
45+
protected override bool RequiresRepositorySignature => false;
46+
}
47+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Microsoft.Extensions.Logging;
8+
using NuGet.Jobs.Validation;
9+
using NuGet.Jobs.Validation.PackageSigning.Storage;
10+
using NuGet.Jobs.Validation.Storage;
11+
using NuGet.Services.Validation.Orchestrator.Telemetry;
12+
13+
namespace NuGet.Services.Validation.PackageSigning.ProcessSignature
14+
{
15+
/// <summary>
16+
/// The validator that ensures the package's repository signature is valid. This does the
17+
/// final signature validation after a package has been repository signed.
18+
/// </summary>
19+
[ValidatorName(ValidatorName.PackageSignatureValidator)]
20+
public class PackageSignatureValidator : BaseSignatureProcessor, IValidator
21+
{
22+
private readonly IValidatorStateService _validatorStateService;
23+
private readonly IProcessSignatureEnqueuer _signatureVerificationEnqueuer;
24+
private readonly ISimpleCloudBlobProvider _blobProvider;
25+
private readonly ITelemetryService _telemetryService;
26+
private readonly ILogger<PackageSignatureValidator> _logger;
27+
28+
public PackageSignatureValidator(
29+
IValidatorStateService validatorStateService,
30+
IProcessSignatureEnqueuer signatureVerificationEnqueuer,
31+
ISimpleCloudBlobProvider blobProvider,
32+
ITelemetryService telemetryService,
33+
ILogger<PackageSignatureValidator> logger)
34+
: base(validatorStateService, signatureVerificationEnqueuer, blobProvider, telemetryService, logger)
35+
{
36+
_validatorStateService = validatorStateService ?? throw new ArgumentNullException(nameof(validatorStateService));
37+
_signatureVerificationEnqueuer = signatureVerificationEnqueuer ?? throw new ArgumentNullException(nameof(signatureVerificationEnqueuer));
38+
_blobProvider = blobProvider ?? throw new ArgumentNullException(nameof(blobProvider));
39+
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
40+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
41+
}
42+
43+
/// <summary>
44+
/// This validator runs after packages are repository signed. It will verify that the repository
45+
/// signature is acceptable.
46+
/// </summary>
47+
protected override bool RequiresRepositorySignature => true;
48+
49+
public override async Task<IValidationResult> GetResultAsync(IValidationRequest request)
50+
{
51+
var result = await base.GetResultAsync(request);
52+
53+
return Validate(result);
54+
}
55+
56+
public override async Task<IValidationResult> StartAsync(IValidationRequest request)
57+
{
58+
var result = await base.StartAsync(request);
59+
60+
return Validate(result);
61+
}
62+
63+
private IValidationResult Validate(IValidationResult result)
64+
{
65+
/// The package signature validator runs after the <see cref="PackageSignatureProcessor" />.
66+
/// All signature validation issues should be caught and handled by the processor.
67+
if (result.Status == ValidationStatus.Failed || result.NupkgUrl != null)
68+
{
69+
_logger.LogCritical(
70+
"Unexpected validation result in package signature validator. This may be caused by an invalid repository " +
71+
"signature. Status = {ValidationStatus}, Nupkg URL = {NupkgUrl}, validation issues = {Issues}",
72+
result.Status,
73+
result.NupkgUrl,
74+
result.Issues.Select(i => i.IssueCode));
75+
76+
throw new InvalidOperationException("Package signature validator has an unexpected validation result");
77+
}
78+
79+
/// Suppress all validation issues. The <see cref="PackageSignatureProcessor"/> should
80+
/// have already reported any issues related to the author signature. Customers should
81+
/// not be notified of validation issues due to the repository signature.
82+
if (result.Issues.Count != 0)
83+
{
84+
_logger.LogWarning(
85+
"Ignoring {ValidationIssueCount} validation issues from result. Issues: {Issues}",
86+
result.Issues.Count,
87+
result.Issues.Select(i => i.IssueCode));
88+
89+
return new ValidationResult(result.Status);
90+
}
91+
92+
return result;
93+
}
94+
}
95+
}

src/NuGet.Services.Validation.Orchestrator/PackageSigning/ProcessSignature/ProcessSignatureConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace NuGet.Services.Validation.PackageSigning.ProcessSignature
88
{
99
/// <summary>
10-
/// Configuration for initializing the <see cref="PackageSigningValidator"/>.
10+
/// Configuration for initializing the <see cref="ProcessSignatureEnqueuer"/>.
1111
/// </summary>
1212
public class ProcessSignatureConfiguration
1313
{

0 commit comments

Comments
 (0)