-
Notifications
You must be signed in to change notification settings - Fork 717
Description
Background and Motivation
We want to make it easy for users to implement and test their services with end-to-end HTTPS support. While local .NET projects can automatically take advantage of the ASP.NET developer certificate (dev cert), many resources such as Node applications or resources running in containers aren't automatically able to trust the certificate.
The goal is to provide a simple opinionated experience for the common case of trusting the ASP.NET developer certificate, while also providing flexibility for users who need more control over certificate trust. Most users shouldn't need to interact with this API at all. We'll work towards the default experience being that resources trust the ASP.NET dev cert unless explicitly opted out, but advanced APIs will allow granular control over certificate trust including the ability to trust custom CA certificates.
The initial scope of this feature will be limited to local development scenarios. In particular, the Developer Certificate is only valid for local development and is not suitable for production use.
Some of the main benefit of trusting the Dev Cert won't be realized until the GA of .NET 10 as that is when the certificate will be updated to cover common local development domains such as host.docker.internal, host.containers.internal, and others that will allow trusted container to host communication over HTTPS.
Proposed API
IResourceBuilder API
WithDeveloperCertificateTrust
IResourceBuilder<T> WithDeveloperCertificateTrust<T>(this IResourceBuilder<T> builder, bool trust = true)Used to opt out (or in) to trusting the ASP.NET developer certificate for a given resource (eventually this may be expanded to include Aspire specific certificates). The default behavior for a resource with no other configuration is to trust the dev cert. Calling this method with trust set to false will opt out of trusting the dev cert. Calling with trust set to true will explicitly trust the dev cert. If a resource trusts the developer certificate it will be included with the certificates from any trusted CertificateAuthorityCollection resources when setting up the certificates at run time.
Usage:
var myApp = builder.AddNpmApp("my-app", ...)
.WithDeveloperCertificateTrust(false); // opt out of trusting the dev certWithCertificateAuthorityCollection
IResourceBuilder<T> WithCertificateAuthorityCollection<T>(this IResourceBuilder<T> builder, IResourceBuilder<CertificateAuthorityCollection> certificateAuthority)Used to add a custom CA certificate collection to a given resource. If WithCertificateAuthorityCollection is called multiple times, the trusted certificates are concatenated to each other for the purposes of the final set of trusted certificates.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle");
var myApp = builder.AddPythonApp("my-app", ...)
.WithCertificateAuthorityCollection(caBundle);WithCustomCertificateAuthoritiesScope
enum CustomCertificateAuthoritiesScope
{
Append, // The default behavior
Override,
}
IResourceBuilder<T> WithCustomCertificateAuthoritiesScope<T>(this IResourceBuilder<T> builder, CertificateAuthorityCollectionScope scope)Used to control how custom certificate authorities for a resource are applied. The default if not specified is CustomCertificateAuthoritiesScope.Append which indicates we should attempt to configure the resource to trust additional certificates in addition to the default CA trust behavior for the resource type. CustomCertificateAuthoritiesScope.Override indicates that we should attempt to override the default CA trust for a resource such that only the custom assigned resources are trusted. In all cases, this should be considered best effort as not all resources may support full CA trust customization (or be configured in such a way that any changes we make are effectively ignored).
Usage:
var myApp = builder.AddNpmApp("my-app", ...)
.WithCustomCertificateAuthoritiesScope(CustomCertificateAuthoritiesScope.Override);DistributedApplicationBuilder API
AddCertificateAuthorityCollection
IResourceBuilder<CertificateAuthorityCollection> AddCertificateAuthorityCollection(this IDistributedApplicationBuilder builder, [ResourceName] string name)Creates a builder for a named CA certificate collection resource. The returned builder can be used to add individual CA certificates to the collection.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithPemCertificateFile("path/to/ca.pem")
.WithDevCert(); // include the ASP.NET dev cert in the bundle
myApp.WithCertificateAuthorityCollection(caBundle);IResourceBuilder API
WithCertificate
IResourceBuilder<CertificateAuthorityCollection> WithCertificate(this IResourceBuilder<CertificateAuthorityCollection> builder, X509Certificate2 certificate)Include a single X509Certificate2 in the collection.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithCertificate(new X509Certificate2("path/to/ca.cer"));WithCertificates
IResourceBuilder<CertificateAuthorityCollection> WithCertificates(this IResourceBuilder<CertificateAuthorityCollection> builder, IEnumerable<X509Certificate2> certificates)Include a collection of X509Certificate2 in the collection.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithCertificates(new List<X509Certificate2>
{
new X509Certificate2("path/to/ca1.cer"),
new X509Certificate2("path/to/ca2.cer")
});WithCertificates
IResourceBuilder<CertificateAuthorityCollection> WithCertificates(this IResourceBuilder<CertificateAuthorityCollection> builder, params X509Certificate2[] certificates)Include a params array of X509Certificate2 in the collection.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithCertificates(
new X509Certificate2("path/to/ca1.cer"),
new X509Certificate2("path/to/ca2.cer"));WithCertificates
IResourceBuilder<CertificateAuthorityCollection> WithCertificates(this IResourceBuilder<CertificateAuthorityCollection> builder, X509Certificate2Collection certificates)Include an X509Certificate2Collection in the collection.
Usage:
var certs = new X509Certificate2Collection();
certs.ImportFromPemFile("path/to/cas.pem");
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithCertificates(certs);WithDevCert
IResourceBuilder<CertificateAuthorityCollection> WithDevCert(this IResourceBuilder<CertificateAuthorityCollection> builder)Includes the ASP.NET developer certificate in a CertificateAuthorityCollection.
Usage:
var caBundle = builder.AddCertificateAuthorityCollection("my-ca-bundle")
.WithDevCert();Annotation(s)
The state of the CA configuration for a resource would be tracked in a CertificateAuthorityCollectionsAnnotation:
public sealed class CertificateAuthorityCollectionAnnotation(CertificateAuthorityCollection certificateAuthorityCollections, bool? trustDeveloperCertificates = null, CustomCertificateAuthoritiesScope? scope = null) : IResourceAnnotation
{
/// <summary>
/// Gets the list of <see cref="CertificateAuthorityCollection"/> that are being referenced.
/// </summary>
public List<CertificateAuthorityCollection> CertificateAuthorityCollections { get; internal set; } = certificateAuthorityCollections ?? new();
/// <summary>
/// Gets a value indicating whether platform developer certificates should be considered trusted.
/// </summary>
public bool? TrustDeveloperCertificates { get; internal set; } = trustDeveloperCertificates;
/// <summary>
/// Gets a value indicating whether the resource should attempt to override its default CA trust behavior in
/// favor of the provided certificates (not all resources will support this).
/// </summary>
public CustomCertificateAuthoritiesScope? Scope { get; internal set; } = scope;
}Alternative Designs
Rather than introducing a new resource type to capture custom CA certificate collections, we could also consider a pure annotation model via IResourceBuilder extensions that works with native certificate types such as X509Certificate2Collection. However, this would somewhat limit our ability to extend the API for trusting certificates from novel sources in the future (such as adding one or more certificates from a KeyVault or other source to a CertificateAuthorityCollection).
Integration API
There will need to be base implementations for Executable and Container based resources that will prepare the required artifact formats for trusted certificate authorities (PEM format CA bundles, individual OpenSSL compatible certificates, etc.) and ensure they are available to the resource (written to a temp folder for Executable resources or added to the container file system for Container resources). For Containers, the tryOverrideResourceDefaults setting will determine where the artifacts are added to the container (do they replace well known system CA paths or are they added as additional files while leaving the existing certificates untouched).