Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Aspire.Hosting.NodeJs/NodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ private static IResourceBuilder<NodeAppResource> WithNodeDefaults(this IResource
else
{
ctx.CertificateTrustArguments.Add("--use-openssl-ca");
ctx.CertificateBundleEnvironment.Add("SSL_CERT_FILE");
}

return Task.CompletedTask;
Expand Down
33 changes: 22 additions & 11 deletions src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.ComponentModel;
using System.Runtime.CompilerServices;

#pragma warning disable ASPIREEXTENSION001
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Python;
Expand Down Expand Up @@ -281,20 +282,30 @@ private static IResourceBuilder<PythonAppResource> AddPythonAppCore(
});

// Configure required environment variables for custom certificate trust when running as an executable
resourceBuilder.WithExecutableCertificateTrustCallback(ctx =>
{
if (ctx.Scope == CustomCertificateAuthoritiesScope.Override)
// Python defaults to using System scope to allow combining custom CAs with system CAs as there's no clean
// way to simply append additional certificates to default Python trust stores such as certifi.
resourceBuilder
.WithCustomCertificateAuthoritiesScope(CustomCertificateAuthoritiesScope.System)
.WithExecutableCertificateTrustCallback(ctx =>
{
// See: https://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification
ctx.CertificateBundleEnvironment.Add("REQUESTS_CA_BUNDLE");
}
if (ctx.Scope != CustomCertificateAuthoritiesScope.Append)
{
// Override default certificates path for the requests module.
// See: https://docs.python-requests.org/en/latest/user/advanced/#ssl-cert-verification
ctx.CertificateBundleEnvironment.Add("REQUESTS_CA_BUNDLE");

// Override default certificates path for Python modules that honor OpenSSL style paths.
// This has been tested with urllib, urllib3, httpx, and aiohttp.
// See: https://docs.openssl.org/3.0/man3/SSL_CTX_load_verify_locations/#description
ctx.CertificateBundleEnvironment.Add("SSL_CERT_FILE");
}

// Override default opentelemetry-python certificate bundle path
// See: https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html#module-opentelemetry.exporter.otlp
ctx.CertificateBundleEnvironment.Add("OTEL_EXPORTER_OTLP_CERTIFICATE");
// Override default opentelemetry-python certificate bundle path
// See: https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html#module-opentelemetry.exporter.otlp
ctx.CertificateBundleEnvironment.Add("OTEL_EXPORTER_OTLP_CERTIFICATE");

return Task.CompletedTask;
});
return Task.CompletedTask;
});

// VS Code debug support - only applicable for Script and Module types
if (entrypointType is EntrypointType.Script or EntrypointType.Module)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,32 @@
namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Defines the scope of custom certificate authorities for a resource. The default is <see cref="Append"/>.
/// Defines the scope of custom certificate authorities for a resource. The default scope for most resources
/// is <see cref="Append"/>, but some resources may choose to override this default behavior.
/// </summary>
public enum CustomCertificateAuthoritiesScope
{
/// <summary>
/// Append the specified certificate authorities to the default set of trusted CAs for a resource.
/// Append the specified certificate authorities to the default set of trusted CAs for a resource. Not all
/// resources support this mode, in which case custom certificate authorities may not be applied. In that case,
/// consider using <see cref="Override"/> or <see cref="System"/> instead.
/// </summary>
Append,
/// <summary>
/// Replace the default set of trusted CAs for a resource with the specified certificate authorities.
/// </summary>
Override,
/// <summary>
/// Attempt to configure the resource to trust default system certificate authorities in addition to
/// any configured custom certificate trust. This mode is useful for resources that don't otherwise
/// allow appending to their default trusted certificate authorities but do allow overriding the set
/// of trusted certificates (e.g. Python, Rust, etc.).
/// </summary>
System,
/// <summary>
/// Disable all custom certificate authority configuration for a resource.
/// </summary>
None,
}

/// <summary>
Expand Down
42 changes: 38 additions & 4 deletions src/Aspire.Hosting/Dcp/DcpExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2067,6 +2067,22 @@ await modelResource.ProcessContainerRuntimeArgValues(
scope = caAnnotation.Scope.GetValueOrDefault(scope);
}

if (scope == CustomCertificateAuthoritiesScope.None)
{
return (new List<string>(), new List<EnvVar>(), false);
}

if (scope == CustomCertificateAuthoritiesScope.System)
{
// Read the system root certificates and add them to the collection
foreach (var location in Enum.GetValues<StoreLocation>())
{
using var rootStore = new X509Store(StoreName.Root, location);
rootStore.Open(OpenFlags.ReadOnly);
certificates.AddRange(rootStore.Certificates);
}
}

if (trustDevCert)
{
foreach (var cert in _developerCertificateService.Certificates)
Expand Down Expand Up @@ -2176,6 +2192,23 @@ await modelResource.ProcessContainerRuntimeArgValues(
scope = caAnnotation.Scope.GetValueOrDefault(scope);
}

if (scope == CustomCertificateAuthoritiesScope.None)
{
// Resource has disabled custom certificate authorities
return (new List<string>(), new List<EnvVar>(), new List<ContainerCreateFileSystem>(), false);
}

if (scope == CustomCertificateAuthoritiesScope.System)
{
// Read the system root certificates and add them to the collection
foreach (var location in Enum.GetValues<StoreLocation>())
{
using var rootStore = new X509Store(StoreName.Root, location);
rootStore.Open(OpenFlags.ReadOnly);
certificates.AddRange(rootStore.Certificates);
}
}

if (trustDevCert)
{
foreach (var cert in _developerCertificateService.Certificates)
Expand All @@ -2191,9 +2224,10 @@ await modelResource.ProcessContainerRuntimeArgValues(
Certificates = certificates,
CancellationToken = cancellationToken
};
if (scope == CustomCertificateAuthoritiesScope.Override)

if (scope != CustomCertificateAuthoritiesScope.Append)
{
// Override default OpenSSL certificate bundle path resolution
// When not in Append scope, override the default OpenSSL certificate bundle path resolution
// SSL_CERT_FILE is always added to the defaults when the scope is Override
// See: https://docs.openssl.org/3.0/man3/SSL_CTX_load_verify_locations/#description
context.CertificateBundleEnvironment.Add("SSL_CERT_FILE");
Expand Down Expand Up @@ -2313,9 +2347,9 @@ await modelResource.ProcessContainerRuntimeArgValues(
],
});

if (scope == CustomCertificateAuthoritiesScope.Override)
if (scope != CustomCertificateAuthoritiesScope.Append)
{
// If overriding the system CA bundle, then we want to copy our bundle to the well-known locations
// If overriding the default resource CA bundle, then we want to copy our bundle to the well-known locations
// used by common Linux distributions to make it easier to ensure applications pick it up.
foreach (var bundlePath in context.DefaultContainerCertificateAuthorityBundlePaths)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Hosting/ResourceBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2163,8 +2163,8 @@ public static IResourceBuilder<TResource> WithDeveloperCertificateTrust<TResourc
/// <param name="scope">The scope to apply to custom certificate authorities associated with the resource.</param>
/// <returns>The <see cref="IResourceBuilder{TResource}"/>.</returns>
/// <remarks>
/// The default scope is <see cref="CustomCertificateAuthoritiesScope.Append"/> which means that custom certificate authorities
/// should be appended to the default trusted certificate authorities for the resource. Setting the scope to
/// The default scope if not overridden is <see cref="CustomCertificateAuthoritiesScope.Append"/> which means that custom certificate
/// authorities should be appended to the default trusted certificate authorities for the resource. Setting the scope to
/// <see cref="CustomCertificateAuthoritiesScope.Override"/> indicates the set of certificates in referenced
/// <see cref="CertificateAuthorityCollection"/> (and optionally Aspire developer certificiates) should be used as the
/// exclusive source of trust for a resource.
Expand Down
Loading