Allow external HTTP root certificates to be configured by a policy file (#805)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-06-11 11:37:40 +02:00
1 parent 5272895441
commit 0ea63a16c0
4 files changed
+296 -31

No files matched your search

@@ -19,6 +19,10 @@ public static class ExternalHttpClientTimeout
private const string ENV_CUSTOM_ROOT_CERTIFICATES_ENABLED = "MINDWORK_AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATES_ENABLED";
private const string ENV_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH = "MINDWORK_AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH";
private const string ENV_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS = "MINDWORK_AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS";
private const string ENV_POLICY_CUSTOM_ROOT_CERTIFICATES_CONFIGURED = "AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATES_POLICY_CONFIGURED";
private const string ENV_POLICY_CUSTOM_ROOT_CERTIFICATES_ENABLED = "AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATES_ENABLED";
private const string ENV_POLICY_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH = "AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH";
private const string ENV_POLICY_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS = "AI_STUDIO_EXTERNAL_HTTP_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS";
// id-kp-serverAuth: Extended Key Usage for TLS server authentication.
// See RFC 5280, section 4.2.1.12: https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12
@@ -123,29 +127,43 @@ public static class ExternalHttpClientTimeout
private static CustomRootCertificateConfiguration ReadCustomRootCertificateConfiguration()
{
if (TryParseBooleanEnvironmentValue(Environment.GetEnvironmentVariable(ENV_POLICY_CUSTOM_ROOT_CERTIFICATES_CONFIGURED), out var policyConfigured) && policyConfigured)
{
var policyEnabled = TryParseBooleanEnvironmentValue(Environment.GetEnvironmentVariable(ENV_POLICY_CUSTOM_ROOT_CERTIFICATES_ENABLED), out var parsedPolicyEnabled) && parsedPolicyEnabled;
var policyBundlePath = Environment.GetEnvironmentVariable(ENV_POLICY_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH)?.Trim() ?? string.Empty;
var policyAllowedHosts = Environment.GetEnvironmentVariable(ENV_POLICY_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS) ?? string.Empty;
return new(policyEnabled, policyBundlePath, ReadAllowedHostPatternsFromDelimitedValue(policyAllowedHosts), TB("policy files"));
}
var envEnabled = Environment.GetEnvironmentVariable(ENV_CUSTOM_ROOT_CERTIFICATES_ENABLED);
var envBundlePath = Environment.GetEnvironmentVariable(ENV_CUSTOM_ROOT_CERTIFICATE_BUNDLE_PATH);
var envAllowedHosts = Environment.GetEnvironmentVariable(ENV_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS);
if (!string.IsNullOrWhiteSpace(envEnabled) || !string.IsNullOrWhiteSpace(envBundlePath) || !string.IsNullOrWhiteSpace(envAllowedHosts))
{
var enabled = TryParseBooleanEnvironmentValue(envEnabled, out var parsedEnvEnabled)
? parsedEnvEnabled
: SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled;
var enabled = TryParseBooleanEnvironmentValue(envEnabled, out var parsedEnvEnabled)
? parsedEnvEnabled
: SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled;
var bundlePath = !string.IsNullOrWhiteSpace(envBundlePath)
? envBundlePath.Trim()
: SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath.Trim();
var bundlePath = !string.IsNullOrWhiteSpace(envBundlePath)
? envBundlePath.Trim()
: SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath.Trim();
var allowedHostPatterns = !string.IsNullOrWhiteSpace(envAllowedHosts)
? ReadAllowedHostPatternsFromDelimitedValue(envAllowedHosts)
: ReadAllowedHostPatterns(SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateAllowedHosts);
var allowedHostPatterns = ReadAllowedHostPatterns(envAllowedHosts);
var source = ReadCustomRootCertificateConfigurationSource(envEnabled, envBundlePath, envAllowedHosts);
return new(enabled, bundlePath, allowedHostPatterns, TB("environment variables"));
}
return new(enabled, bundlePath, allowedHostPatterns, source);
return new(
SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled,
SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath.Trim(),
ReadAllowedHostPatterns(SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateAllowedHosts),
ReadCustomRootCertificateSettingsSource());
}
private static string ReadCustomRootCertificateConfigurationSource(string? envEnabled, string? envBundlePath, string? envAllowedHosts)
private static string ReadCustomRootCertificateSettingsSource()
{
if (!string.IsNullOrWhiteSpace(envEnabled) || !string.IsNullOrWhiteSpace(envBundlePath) || !string.IsNullOrWhiteSpace(envAllowedHosts))
return TB("environment variables");
var enabledIsManaged = ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificatesEnabled, out var enabledMeta) && enabledMeta.IsLocked;
var bundlePathIsManaged = ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateBundlePath, out var bundlePathMeta) && bundlePathMeta.IsLocked;
var allowedHostsIsManaged = ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateAllowedHosts, out var allowedHostsMeta) && allowedHostsMeta.IsLocked;
@@ -154,12 +172,13 @@ public static class ExternalHttpClientTimeout
: TB("app settings");
}
private static IReadOnlyList<string> ReadAllowedHostPatterns(string? envAllowedHosts)
private static IReadOnlyList<string> ReadAllowedHostPatternsFromDelimitedValue(string allowedHosts)
{
IEnumerable<string> rawPatterns = !string.IsNullOrWhiteSpace(envAllowedHosts)
? envAllowedHosts.Split([';', ','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
: SettingsManagerAccess.ConfigurationData.App.ExternalHttpCustomRootCertificateAllowedHosts;
return ReadAllowedHostPatterns(allowedHosts.Split([';', ','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
}
private static IReadOnlyList<string> ReadAllowedHostPatterns(IEnumerable<string> rawPatterns)
{
var patterns = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var rawPattern in rawPatterns)
{