-
Notifications
You must be signed in to change notification settings - Fork 258
Repository Signatures
Status: Reviewing
The discussion around this spec is tracked here - Repository Signatures issue #6378
NuGet packages do not include any mechanism to determine if a given package has been modified from creation to consumption or who is the legitimate author of the package. Content integrity depends on the HTTPS connection used to download the package, but once downloaded it’s not easy to check if the package has been tampered.
Author signatures provide content integrity checks, but they do not address the fact that many packages might depend on packages that have not been signed by their authors.
Repository signatures provide an integrity guarantee for all packages in a repository whether they are author signed or not, even if those packages are obtained from a different location than the original repository where they were signed.
NuGet servers that implement repository signatures must announce the list of certificates used to generate the repository signatures. This announcement will be available as part of the NuGet protocol v3.
NuGet clients who support repository signatures will define the list of repositories they trust. This information is stored in a configuration file that can be customized at different scopes.
During package validation NuGet clients must require that all packages from the claimed repository of origin are signed with one of the certificates included in the configuration file.
NuGet clients will help synchronize the config file with the certificates exposed by the feed with new gestures to keep the configuration file up to date.
Note: NuGet servers may choose to produce repository signatures or just host unsigned packages or packages signed by other repositories.
Repository signatures do not protect against other threats where the repository certificate's private key, or even the whole repository service has been compromised. These threats are protected by advanced monitoring techniques outside the scope of this document.
Package signing in general does not protect from a compromised local machine, where an attacker can change the trusted roots, the repository keys or even the original packages.
Considering author and repository signatures there are four possible package signing configurations; no other configurations of these signature types are allowed:
Package Type | Notes |
---|---|
Unsigned | The package does not include any signature. |
Author | The primary signature is an author signature. |
Repo | The primary signature is a repository signature. |
Author and Repo | The primary signature is an author signature, and the signature is repository countersigned. |
Note: Package repositories can host packages of any of these four types, however once NuGet.org implements repository signatures, all packages in NuGet.org will fall in the last two types.
Repository signing certificates will have some requirements which be detailed in the technical specification: Repository Signatures and Countersignatures Technical Specification
Repository certificates should chain to a trusted root on the local machine, in the case where the certificate does not, package readers will produce a warning unless the configuration specifies that untrusted certificates are allowed.
The NuGet Package Signatures Technical Specification defines how to identify a repository signature. Additional requirements are described in the next section:
Repository signatures add the next requirements to package signature definition:
- A package must have exactly 1 repository signature or exactly 1 repository countersignature on the primary signature, and not both a repository signature and repository countersignature. (To apply a repository signature on a package that already contains a repository signature, the existing signature must be removed).
- If only the repository signature is present it must be the primary signature.
The repository signature will include the following metadata:
Name | Description | Required |
---|---|---|
V3 Service Index URL | Official HTTPS URL to the repository V3 service index. (must be HTTPS) | Yes |
Package Owners | List of the package owners in that repository at the time of submission. | No |
Repository signatures require a NuGet server with a v3 service endpoint. Repositories based on local or shared folders can host packages signed in a different repository, however to produce their own repository signatures they will need to configure an HTTP server to publish the certificates.
Repositories will announce the certificates used for repository signing as part of the NuGet protocol v3.
If a repository announces that all packages are repository signed, then a package reader should verify that a package received from the repository is signed as expected or resolve validation failure by a policy.
The service index API will expose a new resource to allow clients verify the certificates used for package signing. Index service example with new resource:
{
"version": "3.0.0-beta.1",
"resources": [
{
"@id": "https://api.nuget.org/v3/registration2/",
"@type": "RegistrationsBaseUrl/3.0.0-rc",
"comment": "Base URL of Azure storage where NuGet package registration info is stored used by RC clients. This base URL does not include SemVer 2.0.0 packages."
},
{
"@id": "https://api.nuget.org/v3/repository-signature/",
"@type": "CertificatesBaseUrl/3.0.0-rc",
"comment" : "BaseUrl for certificate endpoints"
}
]
}
This new resource will include an endpoint to discover the list of certificates. For each certificate the next properties will be shown. Additionally, the complete certificate will be available as a DER encoded file.
Certificate Property | Purpose |
---|---|
Thumbprint SHA256 | Uniquely identify the certificate |
Subject Name | Text to display if needed |
Issued By | Name of the issuer |
Expiration Date | Identify expired certificates |
Repository administrators can configure the certificates used for repository signing. Over time the certificates will expire and would need to be renewed. Once renewed it will be added to the certificates list, previous certificates should not be removed from the list since that operation will invalidate existing signed packages.
If the repository private key is compromised the certificate must be revoked and all packages signed after the revocation effective date will become invalid and should be signed again with the new certificate.
Repositories must remove the revoked certificates from the service URL.
Note: There are some limitations in non-Windows platforms, where the revocation status does not include the revocation effective date. If the repository certificate is compromised, all packages should be signed again with the updated certificate.
Client policies allow to customize the security checks in three levels:
- Dev. This mode will be the default until all packages in NuGet.org include the repository signature. Clients will verify the signature if it’s available, but also allows unsigned packages. Most trust issues can be reported as warnings or ignored.
- Secure. All packages must be signed, and trust verification is enforced. This mode will be the default in future versions once all NuGet.org packages include the repository signature.
- Strict. Enables advanced locked-down configuration by requiring to explicitly trust package authors.
DEV | Secure | Strict |
---|---|---|
Allow unsigned | Require signed (author or repo) | Require signed (author) |
Enforce content integrity (if signed) | Enforce content integrity | Enforce content integrity |
Perform revocation checks | Perform revocation checks | Perform revocation checks |
Enforce trusted repositories | Enforce trusted repositories | |
Enforce trusted authors |
Package readers configure the client policy with the new signatureValidationMode
property in the packageRestore
configuration section. NuGet docs will include instructions to update the configuration file even without connection to the repository service index.
The rules to locate this file will follow the NuGet.config rules to work at different scopes.
The sample below shows a sample configuration file:
<configuration>
<packageRestore>
<add key="enabled" value="true" />
<add key="automatic" value="true" />
<add key="signatureValidationMode" value="Secure" />
</packageRestore>
<packageSources>
<add key="NuGet.org"
value="https://api.nuget.org/v3/index.json"
protocolVersion="3" />
<add key="ContosoSharedRepo"
value="https://api.myget.org/F/ContosoRepository"
protocolVersion="3" />
<add key="ContosoInternalRepo"
value="\\Contoso-FS01\ContosoRepository"/>
</packageSources>
</configuration>
Users trust specific repositories (and authors TBD) by listing the certificates in the NuGet configuration file.
<configuration>
<trustedRepositories>
<repository key="NuGet.org"
serviceIndex="https://api.nuget.org/v3/index.json">
<certificate subjectName="NuGet.org"
thumbprint="a897009b809809c..." />
<certificate subjectName="NuGet.org"
thumbprint="6565a656c65b65d..." />
</repository>
<repository key="ContosoSharedRepo"
serviceIndex="https://api.myget.org/F/ContosoRepository"
allowUntrustedRoot="true" >
<certificate subjectName="ContosoCert"
thumbprint="98098a0980b980c..." />
</repository>
<trustedRepositories>
</configuration>
NuGet official clients (VS, NuGet.exe and dotNet.exe) will include the NuGet.org keys by default. Future versions will add new certificates before the previous one gets expired. This means that clients who maintain their versions up to date will have the certificates registered by default. Clients running a specific version forever will need to update the keys manually as with any other repository.
Repositories should use a code signing certificate issued by a CA, with revocation information support. If the certificate is not trusted, or the certificate revocation information is not available, the restore operation will produce a warning unless the allowUntrustedRoot
option is set.
When adding a new repository, the client will query the service index, if repository signatures are announced the client will try to add the keys to the configuration file. These additions will only work if the certificates chain to a trusted root. When adding a repository with untrusted certificates, the client will produce a warning with instructions on how to update the configuration file:
nuget source add -Source https://api.myget.org/F/ContosoRepository
WARNING: The repository <BaseURL> announces certificates that are not in your trusted list configuration, do you want to add the next public keys to your trusted repositories list?
For more information visit https://aka.ms/nuget-sync-keys
VS package manager settings will show an icon to distinguish which package sources support repository signatures and will add a new window to see and update the keys.
Repository signatures will be validated in the same way as author signatures, just before extraction, as described in the author signatures spec. The next diagram shows the validation process for each client policy with all the possible outcomes:
Let’s review each of the decisions outlined in this diagram:
- Is Signed? The technical specification defines what is a NuGet signed package.
- Is Tampered? Will compute the file hash and compare it with the hash in the signature, this check enables any content modification detection.
- Is Revoked? The client will try to verify revocation information, and will fail if the certificate is revoked (only if the revocation occurred before the timestamp), but will never fail if the verification cannot be made: CRL not available, Server unavailable, etc.
- Has repository Signature? Based on the technical spec, the client will look for the expected attributes in the primary signature or in the countersignature.
- Is Trusted in config? This check will verify the repository certificate is trusted in the local config.
- Can verify certs from Repo Feed? When the repository certificate is not trusted, the client should show a warning asking the user to refresh his configuration.
- Has author and repo signature? Once the repository certificate has been validated, the client will check to see if there is an author signature. Note that in Secure mode the author trust check is not enforced.
- Is Author Trusted? The strict mode requires to explicitly trust the package authors. (The full definition of the strict mode will be available later).
When a signed package is found, just before extraction, the package reader must verify the package content integrity. If the computed hash does not match, the package will be considered tampered and the restore operation will fail. The DEV client policy will not set any additional enforcements, and all packages must be installed without any user interruption.
The Secure mode enforces that all packages must be signed with author or repo signatures: Author signatures require the signing certificate is trusted in the local machine, typically by the trusted roots certificates, if a package does not have an author signature it will check if there is a repository signature and if the repository is already trusted in the configuration.
The Strict mode enforces trust with the author, the format to specify the list of trusted authors will be defined in the Client Policies specification.
If the repository is not found in the configuration, the package reader will produce a warning with clear instructions to fix the issue, e.g.:
nuget install PackageFromNewRepository
ERROR: This Package has been signed by a repository that is not trusted.
You must update your configuration to include the repository public keys by running:
‘nuget sync-keys -source http://newrepository.com/index.json’, or using the IDE.
For more information on package signing visit http://aka.ms/nuget-sync-keys
Repository signatures will be added to packages as part of the server ingestion process using the NuGet libraries. NuGet client tools will not expose any CLI command to generate repository signatures but it will support viewing and eventually removing a repository signature from a given package.
Official clients like Visual Studio and Nuget.exe (dotnet.exe will be included later), will be aligned with the 3 stages defined in the original blog post, to summarize:
- Stage 1. Clients will validate author signatures if available, and will ignore repository signatures. The behavior will be the same as the DEV mode.
- Stage 2. Clients will require signed packages, and will set the secure mode as default.
- Stage 3. Clients can opt-in in the strict mode, and VS will provide a UI to help defining the trusted authors list.
Author Trust. In secure mode the author will be considered trusted if it chains to a trusted root in the local machine. In Strict mode it needs to be explicitly trusted in the configuration file. When revocation check cannot be validated, the trust will be considered as “undetermined”.
Repository Trust. Repository trust is determined based on the configuration file, if the certificate does not chain to a trusted root then the trust will be considered as “undetermined”.
Stage 1 clients will operate under DEV mode implicitily.
Author Trusted | Repo Trusted | Unsigned | Author | Repo | AuthorRepo |
---|---|---|---|---|---|
N/A | N/A | ✅ | ✅ | ✅ | ✅ |
Stage 2 clients will use the Secure mode by default. Users can opt-in the DEV mode to use unsigned packages.
Author Trusted | Repo Trusted | Unsigned | Author | Repo | AuthorRepo |
---|---|---|---|---|---|
No | No | ❌ | ❌ | ❌ | ❌ |
No | Undetermined | ❌ | ❌ | ||
No | Yes | ❌ | ❌ | ✅ | ✅ |
Yes | No | ❌ | ✅ | ✅ | ✅ |
Yes | Undetermined | ❌ | ✅ | ✅ | ✅ |
Yes | Yes | ❌ | ✅ | ✅ | ✅ |
Undertermined | No | ❌ | ❌ | ❌ | ❌ |
Undertermined | Undertermined | ❌ | |||
Undertermined | Yes | ❌ | ✅ | ✅ | ✅ |
Stage 3 clients will use the Secure mode by default. Users can opt-in the Strict mode to define a locked down environment.
Author Trusted | Repo Trusted | Unsigned | Author | Repo | AuthorRepo |
---|---|---|---|---|---|
No | No | ❌ | ❌ | ❌ | ❌ |
No | Undetermined | ❌ | ❌ | ❌ | ❌ |
No | Yes | ❌ | ❌ | ❌ | ❌ |
Yes | No | ❌ | ✅ | ❌ | ✅ |
Yes | Undetermined | ❌ | ✅ | ❌ | ✅ |
Yes | Yes | ❌ | ✅ | ❌ | ✅ |
Undertermined | No | ❌ | ❌ | ❌ | ❌ |
Undertermined | Undertermined | ❌ | ❌ | ❌ | ❌ |
Undertermined | Yes | ❌ | ❌ | ❌ | ❌ |
Check out the proposals in the accepted
& proposed
folders on the repository, and active PRs for proposals being discussed today.