From 2e94dc678621868e7e814cf597343bf673d99997 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Thu, 28 Mar 2024 13:23:13 +1100 Subject: [PATCH] Stop the apphost from running if the application url is http and ASPIRE_ALLOW_UNSECURED_TRANSPORT is not set (#3177) --- .../Properties/launchSettings.json | 16 +- .../Properties/launchSettings.json | 43 ++- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 16 +- .../Properties/launchSettings.json | 35 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../AppHost/Properties/launchSettings.json | 3 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../AppHost/Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 16 +- .../Properties/launchSettings.json | 16 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 17 +- .../Properties/launchSettings.json | 16 +- src/Aspire.Hosting/Aspire.Hosting.csproj | 1 + .../Dashboard/TransportOptions.cs | 9 + .../Dashboard/TransportOptionsValidator.cs | 75 ++++ .../DistributedApplicationBuilder.cs | 12 + .../DistributedApplicationOptions.cs | 5 + src/Shared/KnownConfigNames.cs | 12 + .../IntegrationServicesFixture.cs | 1 + .../Aspire.Hosting.Testing.Tests.csproj | 4 + .../DistributedApplicationFixtureOfT.cs | 2 +- .../TestingBuilderTests.cs | 1 - .../TransportOptionsValidatorTests.cs | 327 ++++++++++++++++++ .../TestProject.AppHost/TestProgram.cs | 9 +- 34 files changed, 814 insertions(+), 43 deletions(-) create mode 100644 src/Aspire.Hosting/Dashboard/TransportOptions.cs create mode 100644 src/Aspire.Hosting/Dashboard/TransportOptionsValidator.cs create mode 100644 src/Shared/KnownConfigNames.cs create mode 100644 tests/Aspire.Hosting.Tests/Dashboard/TransportOptionsValidatorTests.cs diff --git a/playground/AWS/AWS.AppHost/Properties/launchSettings.json b/playground/AWS/AWS.AppHost/Properties/launchSettings.json index bde6c5a767..7def19447c 100644 --- a/playground/AWS/AWS.AppHost/Properties/launchSettings.json +++ b/playground/AWS/AWS.AppHost/Properties/launchSettings.json @@ -1,6 +1,18 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15068;http://localhost:15069", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16216", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -9,7 +21,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16216" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16216", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "manifest-publish": { diff --git a/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Properties/launchSettings.json b/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Properties/launchSettings.json index 69941b6f40..7867967352 100644 --- a/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Properties/launchSettings.json +++ b/playground/AzureSearchEndToEnd/AzureSearch.AppHost/Properties/launchSettings.json @@ -1,28 +1,41 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { - "http": { + "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:15288", + "applicationUrl": "https://localhost:15287;http://localhost:15288", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16155" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16155", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" }, - }, - "generate-manifest": { - "commandName": "Project", - "launchBrowser": true, - "dotnetRunMessages": true, - "commandLineArgs": "--publisher manifest --output-path aspire-manifest.json", - "applicationUrl": "http://localhost:15288", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16155" + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15288", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16155", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" + }, + "generate-manifest": { + "commandName": "Project", + "launchBrowser": true, + "dotnetRunMessages": true, + "commandLineArgs": "--publisher manifest --output-path aspire-manifest.json", + "applicationUrl": "http://localhost:15288", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16155" + } + } } } - } } diff --git a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Properties/launchSettings.json b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json index 32514f6378..1568103ca4 100644 --- a/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/CosmosEndToEnd/CosmosEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/CustomResources/CustomResources.AppHost/Properties/launchSettings.json b/playground/CustomResources/CustomResources.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/CustomResources/CustomResources.AppHost/Properties/launchSettings.json +++ b/playground/CustomResources/CustomResources.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/DatabaseMigration/DatabaseMigration.AppHost/Properties/launchSettings.json b/playground/DatabaseMigration/DatabaseMigration.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/DatabaseMigration/DatabaseMigration.AppHost/Properties/launchSettings.json +++ b/playground/DatabaseMigration/DatabaseMigration.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Properties/launchSettings.json b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Properties/launchSettings.json index 3185d212b1..a0a7265f68 100644 --- a/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/OpenAIEndToEnd/OpenAIEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,18 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15215;http://localhost:15216", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16195", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -9,7 +21,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16195" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16195", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Properties/launchSettings.json b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Properties/launchSettings.json index f0f955e2d6..a00013526c 100644 --- a/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/ParameterEndToEnd/ParameterEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,33 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https-UseContainer": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, + "https-UseConnectionString": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "SimulateProduction": "true" + } + }, "http-UseContainer": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +37,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "http-UseConnectionString": { @@ -22,8 +51,10 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", - "SimulateProduction": "true" + "SimulateProduction": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Properties/launchSettings.json b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/PostgresEndToEnd/PostgresEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Properties/launchSettings.json b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/ProxylessEndToEnd/ProxylessEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Properties/launchSettings.json b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Properties/launchSettings.json +++ b/playground/SqlServerEndToEnd/SqlServerEndToEnd.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/Stress/Stress.AppHost/Properties/launchSettings.json b/playground/Stress/Stress.AppHost/Properties/launchSettings.json index 32514f6378..cefdd7e8a4 100644 --- a/playground/Stress/Stress.AppHost/Properties/launchSettings.json +++ b/playground/Stress/Stress.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/TestShop/AppHost/Properties/launchSettings.json b/playground/TestShop/AppHost/Properties/launchSettings.json index e0d1eba7c6..d6edcc61d1 100644 --- a/playground/TestShop/AppHost/Properties/launchSettings.json +++ b/playground/TestShop/AppHost/Properties/launchSettings.json @@ -23,7 +23,8 @@ "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031", "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17031", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/bicep/BicepSample.AppHost/Properties/launchSettings.json b/playground/bicep/BicepSample.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/bicep/BicepSample.AppHost/Properties/launchSettings.json +++ b/playground/bicep/BicepSample.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/cdk/CdkSample.AppHost/Properties/launchSettings.json b/playground/cdk/CdkSample.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/cdk/CdkSample.AppHost/Properties/launchSettings.json +++ b/playground/cdk/CdkSample.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/dapr/AppHost/Properties/launchSettings.json b/playground/dapr/AppHost/Properties/launchSettings.json index eac35bd52b..89883cf642 100644 --- a/playground/dapr/AppHost/Properties/launchSettings.json +++ b/playground/dapr/AppHost/Properties/launchSettings.json @@ -1,5 +1,18 @@ { "profiles": { + "https": { + "commandName": "Project", + "launchBrowser": true, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16031", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "launchBrowser": true, @@ -9,7 +22,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/mongo/Mongo.AppHost/Properties/launchSettings.json b/playground/mongo/Mongo.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/mongo/Mongo.AppHost/Properties/launchSettings.json +++ b/playground/mongo/Mongo.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/mysql/MySqlDb.AppHost/Properties/launchSettings.json b/playground/mysql/MySqlDb.AppHost/Properties/launchSettings.json index 24959e7527..df1073aafa 100644 --- a/playground/mysql/MySqlDb.AppHost/Properties/launchSettings.json +++ b/playground/mysql/MySqlDb.AppHost/Properties/launchSettings.json @@ -1,6 +1,18 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15223;http://localhost:15224", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16160", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -9,7 +21,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16160" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16160", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/nats/Nats.AppHost/Properties/launchSettings.json b/playground/nats/Nats.AppHost/Properties/launchSettings.json index 1a551c295c..bd97744c4f 100644 --- a/playground/nats/Nats.AppHost/Properties/launchSettings.json +++ b/playground/nats/Nats.AppHost/Properties/launchSettings.json @@ -1,5 +1,17 @@ { "profiles": { + "https": { + "commandName": "Project", + "launchBrowser": true, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:18887;http://localhost:18888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16160", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" + } + }, "http": { "commandName": "Project", "launchBrowser": true, @@ -8,7 +20,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16160" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16160", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/orleans/OrleansAppHost/Properties/launchSettings.json b/playground/orleans/OrleansAppHost/Properties/launchSettings.json index e5c69689a7..6344471f89 100644 --- a/playground/orleans/OrleansAppHost/Properties/launchSettings.json +++ b/playground/orleans/OrleansAppHost/Properties/launchSettings.json @@ -1,5 +1,18 @@ { "profiles": { + "https": { + "commandName": "Project", + "launchBrowser": true, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16031", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "launchBrowser": true, @@ -9,7 +22,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/seq/Seq.AppHost/Properties/launchSettings.json b/playground/seq/Seq.AppHost/Properties/launchSettings.json index 32514f6378..f9dac51d48 100644 --- a/playground/seq/Seq.AppHost/Properties/launchSettings.json +++ b/playground/seq/Seq.AppHost/Properties/launchSettings.json @@ -1,6 +1,19 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15887;http://localhost:15888", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16175", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -10,7 +23,9 @@ "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16175", - "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true" + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/playground/signalr/SignalRAppHost/Properties/launchSettings.json b/playground/signalr/SignalRAppHost/Properties/launchSettings.json index cca203b452..241071480d 100644 --- a/playground/signalr/SignalRAppHost/Properties/launchSettings.json +++ b/playground/signalr/SignalRAppHost/Properties/launchSettings.json @@ -1,6 +1,18 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:15015;http://localhost:15016", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16099", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037" + } + }, "http": { "commandName": "Project", "dotnetRunMessages": true, @@ -9,7 +21,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16099" + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16099", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038", + "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" } }, "generate-manifest": { diff --git a/src/Aspire.Hosting/Aspire.Hosting.csproj b/src/Aspire.Hosting/Aspire.Hosting.csproj index b93b1c68ab..35c3800315 100644 --- a/src/Aspire.Hosting/Aspire.Hosting.csproj +++ b/src/Aspire.Hosting/Aspire.Hosting.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Aspire.Hosting/Dashboard/TransportOptions.cs b/src/Aspire.Hosting/Dashboard/TransportOptions.cs new file mode 100644 index 0000000000..ad6294c3d3 --- /dev/null +++ b/src/Aspire.Hosting/Dashboard/TransportOptions.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Aspire.Hosting.Dashboard; + +internal class TransportOptions +{ + public bool AllowUnsecureTransport { get; set; } +} diff --git a/src/Aspire.Hosting/Dashboard/TransportOptionsValidator.cs b/src/Aspire.Hosting/Dashboard/TransportOptionsValidator.cs new file mode 100644 index 0000000000..20e3fe2dd5 --- /dev/null +++ b/src/Aspire.Hosting/Dashboard/TransportOptionsValidator.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Aspire.Hosting.Dashboard; + +internal class TransportOptionsValidator(IConfiguration configuration, DistributedApplicationExecutionContext executionContext, DistributedApplicationOptions distributedApplicationOptions) : IValidateOptions +{ + public ValidateOptionsResult Validate(string? name, TransportOptions transportOptions) + { + var effectiveAllowUnsecureTransport = transportOptions.AllowUnsecureTransport || distributedApplicationOptions.DisableDashboard || distributedApplicationOptions.AllowUnsecuredTransport; + + if (executionContext.IsPublishMode || effectiveAllowUnsecureTransport) + { + return ValidateOptionsResult.Success; + } + + // Validate ASPNETCORE_URLS + var applicationUrls = configuration[KnownConfigNames.AspNetCoreUrls]; + if (string.IsNullOrEmpty(applicationUrls)) + { + return ValidateOptionsResult.Fail($"AppHost does not have applicationUrl in launch profile, or {KnownConfigNames.AspNetCoreUrls} environment variable set."); + } + + var firstApplicationUrl = applicationUrls.Split(";").First(); + + if (!Uri.TryCreate(firstApplicationUrl, UriKind.Absolute, out var parsedFirstApplicationUrl)) + { + return ValidateOptionsResult.Fail($"The 'applicationUrl' setting of the launch profile has value '{firstApplicationUrl}' which could not be parsed as a URI."); + } + + if (parsedFirstApplicationUrl.Scheme == "http") + { + return ValidateOptionsResult.Fail($"The 'applicationUrl' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details."); + } + + // Vaidate DOTNET_DASHBOARD_OTLP_ENDPOINT_URL + var dashboardOtlpEndpointUrl = configuration[KnownConfigNames.DashboardOtlpEndpointUrl]; + if (string.IsNullOrEmpty(dashboardOtlpEndpointUrl)) + { + return ValidateOptionsResult.Fail($"AppHost does not have the {KnownConfigNames.DashboardOtlpEndpointUrl} setting defined."); + } + + if (!Uri.TryCreate(dashboardOtlpEndpointUrl, UriKind.Absolute, out var parsedDashboardOtlpEndpointUrl)) + { + return ValidateOptionsResult.Fail($"The {KnownConfigNames.DashboardOtlpEndpointUrl} setting with a value of '{dashboardOtlpEndpointUrl}' could not be parsed as a URI."); + } + + if (parsedDashboardOtlpEndpointUrl.Scheme == "http") + { + return ValidateOptionsResult.Fail($"The '{KnownConfigNames.DashboardOtlpEndpointUrl}' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details."); + } + + // Vaidate DOTNET_DASHBOARD_RESOURCE_SERVER_ENDPOINT_URL + var resourceServiceEndpointUrl = configuration[KnownConfigNames.ResourceServiceEndpointUrl]; + if (string.IsNullOrEmpty(resourceServiceEndpointUrl)) + { + return ValidateOptionsResult.Fail($"AppHost does not have the {KnownConfigNames.ResourceServiceEndpointUrl} setting defined."); + } + + if (!Uri.TryCreate(resourceServiceEndpointUrl, UriKind.Absolute, out var parsedResourceServiceEndpointUrl)) + { + return ValidateOptionsResult.Fail($"The {KnownConfigNames.ResourceServiceEndpointUrl} setting with a value of '{resourceServiceEndpointUrl}' could not be parsed as a URI."); + } + + if (parsedResourceServiceEndpointUrl.Scheme == "http") + { + return ValidateOptionsResult.Fail($"The '{KnownConfigNames.ResourceServiceEndpointUrl}' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details."); + } + + return ValidateOptionsResult.Success; + } +} diff --git a/src/Aspire.Hosting/DistributedApplicationBuilder.cs b/src/Aspire.Hosting/DistributedApplicationBuilder.cs index bf4aea9c68..086ada7038 100644 --- a/src/Aspire.Hosting/DistributedApplicationBuilder.cs +++ b/src/Aspire.Hosting/DistributedApplicationBuilder.cs @@ -10,8 +10,10 @@ using Aspire.Hosting.Publishing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Aspire.Hosting; @@ -93,6 +95,8 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) _innerBuilder.Services.AddSingleton(); // Dashboard + _innerBuilder.Services.AddOptions().ValidateOnStart().PostConfigure(MapTransportOptionsFromCustomKeys); + _innerBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, TransportOptionsValidator>()); _innerBuilder.Services.AddSingleton(); _innerBuilder.Services.AddHostedService(sp => sp.GetRequiredService()); _innerBuilder.Services.AddLifecycleHook(); @@ -123,6 +127,14 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) LogBuilderConstructed(this); } + private void MapTransportOptionsFromCustomKeys(TransportOptions options) + { + if (Configuration.GetBool(KnownConfigNames.AllowUnsecuredTransport) is { } allowUnsecuredTransport) + { + options.AllowUnsecureTransport = allowUnsecuredTransport; + } + } + private static bool IsOtlpApiKeyAuthDisabled(IConfiguration configuration) { return configuration.GetBool(DisableOtlpApiKeyAuthKey) ?? false; diff --git a/src/Aspire.Hosting/DistributedApplicationOptions.cs b/src/Aspire.Hosting/DistributedApplicationOptions.cs index 7a3b55a116..890187eb43 100644 --- a/src/Aspire.Hosting/DistributedApplicationOptions.cs +++ b/src/Aspire.Hosting/DistributedApplicationOptions.cs @@ -43,6 +43,11 @@ public DistributedApplicationOptions() internal bool DashboardEnabled => !DisableDashboard; + /// + /// Allows the use of HTTP urls for for the AppHost resource endpoint. + /// + public bool AllowUnsecuredTransport { get; set; } + private string? ResolveProjectDirectory() { var assemblyMetadata = Assembly?.GetCustomAttributes(); diff --git a/src/Shared/KnownConfigNames.cs b/src/Shared/KnownConfigNames.cs new file mode 100644 index 0000000000..3e7efd5ac3 --- /dev/null +++ b/src/Shared/KnownConfigNames.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Aspire.Hosting; + +internal static class KnownConfigNames +{ + public static string AspNetCoreUrls = "ASPNETCORE_URLS"; + public static string AllowUnsecuredTransport = "ASPIRE_ALLOW_UNSECURED_TRANSPORT"; + public static string DashboardOtlpEndpointUrl = "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL"; + public static string ResourceServiceEndpointUrl = "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL"; +} diff --git a/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs b/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs index 46c1cefcd0..45118838cd 100644 --- a/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs +++ b/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs @@ -51,6 +51,7 @@ public IntegrationServicesFixture(IMessageSink diagnosticMessageSink) { BuildEnvironment.EnvVars["TestsRunningOutsideOfRepo"] = "true"; } + BuildEnvironment.EnvVars.Add("ASPIRE_ALLOW_UNSECURED_TRANSPORT", "true"); } public async Task InitializeAsync() diff --git a/tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj b/tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj index db7a21609a..fc5752930e 100644 --- a/tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj +++ b/tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj @@ -12,6 +12,10 @@ + + + + diff --git a/tests/Aspire.Hosting.Testing.Tests/DistributedApplicationFixtureOfT.cs b/tests/Aspire.Hosting.Testing.Tests/DistributedApplicationFixtureOfT.cs index 60f40eed85..0a9c2261ec 100644 --- a/tests/Aspire.Hosting.Testing.Tests/DistributedApplicationFixtureOfT.cs +++ b/tests/Aspire.Hosting.Testing.Tests/DistributedApplicationFixtureOfT.cs @@ -4,7 +4,7 @@ namespace Aspire.Hosting.Testing.Tests; -public sealed class DistributedApplicationFixture : DistributedApplicationFactory, IAsyncLifetime where TEntryPoint : class +public class DistributedApplicationFixture : DistributedApplicationFactory, IAsyncLifetime where TEntryPoint : class { public DistributedApplicationFixture() { diff --git a/tests/Aspire.Hosting.Testing.Tests/TestingBuilderTests.cs b/tests/Aspire.Hosting.Testing.Tests/TestingBuilderTests.cs index 78035ea944..154885df8a 100644 --- a/tests/Aspire.Hosting.Testing.Tests/TestingBuilderTests.cs +++ b/tests/Aspire.Hosting.Testing.Tests/TestingBuilderTests.cs @@ -15,7 +15,6 @@ public async Task HasEndPoints() { var appHost = await DistributedApplicationTestingBuilder.CreateAsync(); await using var app = await appHost.BuildAsync(); - await app.StartAsync(); // Get an endpoint from a resource diff --git a/tests/Aspire.Hosting.Tests/Dashboard/TransportOptionsValidatorTests.cs b/tests/Aspire.Hosting.Tests/Dashboard/TransportOptionsValidatorTests.cs new file mode 100644 index 0000000000..5d1084c5f5 --- /dev/null +++ b/tests/Aspire.Hosting.Tests/Dashboard/TransportOptionsValidatorTests.cs @@ -0,0 +1,327 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Hosting.Dashboard; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Aspire.Hosting.Tests.Dashboard; + +public class TransportOptionsValidatorTests +{ + [Fact] + public void ValidationFailsWhenHttpUrlSpecifiedWithAllowSecureTransportSetToFalse() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "http://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The 'applicationUrl' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details.", + result.FailureMessage + ); + } + + [Fact] + public void InvalidTransportOptionSucceedValidationInPublishMode() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Publish); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "http://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } + + [Fact] + public void InvalidTransportOptionSucceedValidationWithDashboardDisabled() + { + var distributedApplicationOptions = new DistributedApplicationOptions() + { + DisableDashboard = true + }; + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "http://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } + + [Fact] + public void InvalidTransportOptionSucceedValidationWithDistributedAppOptionsFlag() + { + var distributedApplicationOptions = new DistributedApplicationOptions() + { + AllowUnsecuredTransport = true + }; + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "http://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } + + [Fact] + public void ValidationFailsWithInvalidUrl() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var invalidUrl = "...invalid...url..."; + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = invalidUrl; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The 'applicationUrl' setting of the launch profile has value '{invalidUrl}' which could not be parsed as a URI.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWithMissingUrl() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"AppHost does not have applicationUrl in launch profile, or {KnownConfigNames.AspNetCoreUrls} environment variable set.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWithStringEmptyUrl() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = string.Empty; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"AppHost does not have applicationUrl in launch profile, or {KnownConfigNames.AspNetCoreUrls} environment variable set.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenResourceUrlNotDefined() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = string.Empty; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = "https://localhost:1236"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"AppHost does not have the {KnownConfigNames.ResourceServiceEndpointUrl} setting defined.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenOtlpUrlNotDefined() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = "https://localhost:1235"; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = string.Empty; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"AppHost does not have the {KnownConfigNames.DashboardOtlpEndpointUrl} setting defined.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenResourceServiceUrlMalformed() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var invalidUrl = "...invalid...url..."; + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = invalidUrl; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = "https://localhost:1236"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The {KnownConfigNames.ResourceServiceEndpointUrl} setting with a value of '{invalidUrl}' could not be parsed as a URI.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenOtlpUrlMalformed() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var invalidUrl = "...invalid...url..."; + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = "https://localhost:1235"; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = invalidUrl; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The {KnownConfigNames.DashboardOtlpEndpointUrl} setting with a value of '{invalidUrl}' could not be parsed as a URI.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenDashboardOtlpUrlIsHttp() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = "https://localhost:1235"; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = "http://localhost:1236"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The '{KnownConfigNames.DashboardOtlpEndpointUrl}' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationFailsWhenResourceServiceUrlIsHttp() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = "http://localhost:1235"; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = "https://localhost:1236"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Failed); + Assert.Equal( + $"The '{KnownConfigNames.ResourceServiceEndpointUrl}' setting must be an https address unless the '{KnownConfigNames.AllowUnsecuredTransport}' environment variable is set to true. This configuration is commonly set in the launch profile. See https://aka.ms/dotnet/aspire/allowunsecuredtransport for more details.", + result.FailureMessage + ); + } + + [Fact] + public void ValidationSucceedsWhenHttpUrlSpecifiedWithAllowSecureTransportSetToTrue() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = true; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "http://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } + + [Fact] + public void ValidationSucceedsWhenHttpsUrlSpecifiedWithAllowSecureTransportSetToTrue() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = true; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } + + [Fact] + public void ValidationSucceedsWhenHttpsUrlSpecifiedWithAllowSecureTransportSetToFalse() + { + var distributedApplicationOptions = new DistributedApplicationOptions(); + var executionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run); + var options = new TransportOptions(); + options.AllowUnsecureTransport = false; + + var config = new ConfigurationBuilder().AddInMemoryCollection().Build(); + config[KnownConfigNames.AspNetCoreUrls] = "https://localhost:1234"; + config[KnownConfigNames.DashboardOtlpEndpointUrl] = "https://localhost:1235"; + config[KnownConfigNames.ResourceServiceEndpointUrl] = "https://localhost:1236"; + + var validator = new TransportOptionsValidator(config, executionContext, distributedApplicationOptions); + var result = validator.Validate(null, options); + Assert.True(result.Succeeded, result.FailureMessage); + } +} diff --git a/tests/testproject/TestProject.AppHost/TestProgram.cs b/tests/testproject/TestProject.AppHost/TestProgram.cs index d56bf5a8d2..235945587c 100644 --- a/tests/testproject/TestProject.AppHost/TestProgram.cs +++ b/tests/testproject/TestProject.AppHost/TestProgram.cs @@ -9,8 +9,9 @@ public class TestProgram : IDisposable { - private TestProgram(string[] args, Assembly assembly, bool includeIntegrationServices, bool includeNodeApp, bool disableDashboard) + private TestProgram(string[] args, Assembly assembly, bool includeIntegrationServices, bool includeNodeApp, bool disableDashboard, bool allowUnsecuredTransport) { + ISet? resourcesToSkip = null; for (int i = 0; i < args.Length; i++) { @@ -33,7 +34,7 @@ private TestProgram(string[] args, Assembly assembly, bool includeIntegrationSer disableDashboard = true; } - AppBuilder = DistributedApplication.CreateBuilder(new DistributedApplicationOptions { Args = args, DisableDashboard = disableDashboard, AssemblyName = assembly.FullName }); + AppBuilder = DistributedApplication.CreateBuilder(new DistributedApplicationOptions { Args = args, DisableDashboard = disableDashboard, AssemblyName = assembly.FullName, AllowUnsecuredTransport = allowUnsecuredTransport }); var serviceAPath = Path.Combine(Projects.TestProject_AppHost.ProjectPath, @"..\TestProject.ServiceA\TestProject.ServiceA.csproj"); @@ -123,8 +124,8 @@ private TestProgram(string[] args, Assembly assembly, bool includeIntegrationSer AppBuilder.Services.AddLifecycleHook(); } - public static TestProgram Create(string[]? args = null, bool includeIntegrationServices = false, bool includeNodeApp = false, bool disableDashboard = true) => - new TestProgram(args ?? [], typeof(T).Assembly, includeIntegrationServices, includeNodeApp, disableDashboard); + public static TestProgram Create(string[]? args = null, bool includeIntegrationServices = false, bool includeNodeApp = false, bool disableDashboard = true, bool allowUnsecuredTransport = true) => + new TestProgram(args ?? [], typeof(T).Assembly, includeIntegrationServices, includeNodeApp, disableDashboard, allowUnsecuredTransport); public IDistributedApplicationBuilder AppBuilder { get; private set; } public IResourceBuilder ServiceABuilder { get; private set; }