From 9549d5c804975768e6e070c37177ef8050ae95fe Mon Sep 17 00:00:00 2001 From: Thomas Clark Date: Wed, 12 Jun 2024 17:12:25 +0100 Subject: [PATCH] feat: add microsoft entra id authentication scheme (#79) --- README.md | 210 ++++++++++++++++-- .../AzureAppConfigurationEmulator.csproj | 1 + src/AzureAppConfigurationEmulator/Program.cs | 25 ++- .../appsettings.json | 4 +- 4 files changed, 219 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ebd67bb..ec6f691 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,17 @@ Please note that Emulator for Azure App Configuration is unofficial and not endo ## Getting Started ```shell -openssl req -x509 -out ./emulator.crt -keyout ./emulator.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -addext 'subjectAltName=DNS:localhost' docker build -f ./src/AzureAppConfigurationEmulator/Dockerfile -t azure-app-configuration-emulator . -docker run -e ASPNETCORE_HTTP_PORTS=8080 -e ASPNETCORE_HTTPS_PORTS=8081 -p 8080:8080 -p 8081:8081 -v ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro -v ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro azure-app-configuration-emulator-data-potection-keys:/root/.aspnet/DataProtection-Keys azure-app-configuration-emulator +docker run -p 8080:8080 azure-app-configuration-emulator ``` ## Authentication -The emulator supports HMAC authentication but does not support Microsoft Entra ID authentication. +The emulator supports HMAC authentication and Microsoft Entra ID authentication. -The credential and secret can be overridden using the environment variables `Authentication__Schemes__Hmac__Credential` and `Authentication__Schemes__Hmac__Secret` respectively. +### HMAC + +The credential and secret may be overridden using the environment variables `Authentication__Schemes__Hmac__Credential` and `Authentication__Schemes__Hmac__Secret` respectively. ```yaml services: @@ -23,14 +24,43 @@ services: context: https://github.com/tnc1997/azure-app-configuration-emulator.git dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile environment: - - ASPNETCORE_HTTP_PORTS=8080 - Authentication__Schemes__Hmac__Credential=xyz - Authentication__Schemes__Hmac__Secret=c2VjcmV0 - ports: - - "8080:8080" ``` -### Postman +#### .NET + +The client may authenticate requests using the connection string for the emulator. + +```csharp +using Azure.Data.AppConfiguration; + +var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings__AzureAppConfiguration"); +var client = new ConfigurationClient(connectionString); + +var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World"); +await client.SetConfigurationSettingAsync(setting); +``` + +```yaml +services: + azure-app-configuration-emulator: + build: + context: https://github.com/tnc1997/azure-app-configuration-emulator.git + dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile + console-application: + build: + context: . + dockerfile: ./ConsoleApplication/Dockerfile + depends_on: + - azure-app-configuration-emulator + environment: + - ConnectionStrings__AzureAppConfiguration=Endpoint=http://azure-app-configuration-emulator:8080;Id=abcd;Secret=c2VjcmV0; +``` + +#### Postman + +The authentication related headers may be generated using the following script: ```javascript const credential = "abcd"; @@ -48,6 +78,133 @@ pm.request.headers.upsert(`x-ms-content-sha256: ${contentHash}`); pm.request.headers.upsert(`Authorization: HMAC-SHA256 Credential=${credential}&SignedHeaders=${signedHeaders}&Signature=${signature}`); ``` +### Microsoft Entra ID + +HMAC authentication is recommended because it does not require a Microsoft Entra tenant and an Azure App Configuration resource. + +1. [Register an application](https://learn.microsoft.com/entra/identity-platform/quickstart-register-app) within the Microsoft Entra tenant. + 1. On the Overview page, in the Essentials accordion, copy the following values: + * Application (client) ID + * Directory (tenant) ID + 2. On the Certificates & secrets page, in the Client secrets tab, add a client secret. +2. [Create an Azure App Configuration resource](https://learn.microsoft.com/azure/azure-app-configuration/quickstart-azure-app-configuration-create) to be emulated. + 1. On the Overview page, in the Essentials accordion, copy the following values: + * Endpoint + 2. On the Access control (IAM) page, add a role assignment. + 1. In the Role tab, select the App Configuration Data Owner role. + 2. In the Members tab, assign access to the registered application. +3. [Generate a self-signed certificate](#ssl--tls) with the `` as the [Subject Alternative Name](https://wikipedia.org/wiki/Subject_Alternative_Name). + +The metadata address must be set using the environment variable `Authentication__Schemes__MicrosoftEntraId__MetadataAddress`. + +```yaml +services: + azure-app-configuration-emulator: + build: + context: https://github.com/tnc1997/azure-app-configuration-emulator.git + dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile + environment: + - ASPNETCORE_HTTP_PORTS=80 + - ASPNETCORE_HTTPS_PORTS=443 + - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com//v2.0/.well-known/openid-configuration + networks: + default: + aliases: + - + volumes: + - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro + - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro +``` + +The valid audience should be overriden using the environment variable `Authentication__Schemes__MicrosoftEntraId__ValidAudience`. + +```yaml +services: + azure-app-configuration-emulator: + build: + context: https://github.com/tnc1997/azure-app-configuration-emulator.git + dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile + environment: + - ASPNETCORE_HTTP_PORTS=80 + - ASPNETCORE_HTTPS_PORTS=443 + - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com//.well-known/openid-configuration + - Authentication__Schemes__MicrosoftEntraId__ValidAudience=https:// + networks: + default: + aliases: + - + volumes: + - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro + - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro +``` + +#### .NET + +The client may authenticate requests using the Microsoft Entra tenant. + +```csharp +using Azure.Data.AppConfiguration; +using Azure.Identity; + +var tenantId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__TenantId"); +var clientId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientId"); +var clientSecret = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientSecret"); +var credential = new ClientSecretCredential(tenantId, clientId, clientSecret); + +var endpoint = Environment.GetEnvironmentVariable("Endpoints__AzureAppConfiguration"); +var client = new ConfigurationClient(new Uri(endpoint), credential); + +var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World"); +await client.SetConfigurationSettingAsync(setting); +``` + +```yaml +services: + azure-app-configuration-emulator: + build: + context: https://github.com/tnc1997/azure-app-configuration-emulator.git + dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile + environment: + - ASPNETCORE_HTTP_PORTS=80 + - ASPNETCORE_HTTPS_PORTS=443 + - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com//.well-known/openid-configuration + - Authentication__Schemes__MicrosoftEntraId__ValidAudience=https:// + networks: + default: + aliases: + - + volumes: + - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro + - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro + console-application: + build: + context: . + dockerfile: ./ConsoleApplication/Dockerfile + depends_on: + - azure-app-configuration-emulator + entrypoint: /bin/sh -c "update-ca-certificates && dotnet ConsoleApplication.dll" + environment: + - Authentication__Schemes__MicrosoftEntraId__ClientId= + - Authentication__Schemes__MicrosoftEntraId__ClientSecret= + - Authentication__Schemes__MicrosoftEntraId__TenantId= + - Endpoints__AzureAppConfiguration=https:// + volumes: + - ./emulator.crt:/usr/local/share/ca-certificates/emulator.crt:ro +``` + +#### Postman + +The access token may be obtained using the following configuration: + +| Configuration | | +|------------------|-------------------------------------------------------------------| +| Auth Type | OAuth 2.0 | +| Grant Type | Client Credentials | +| Access Token URL | `https://login.microsoftonline.com//oauth2/v2.0/token` | +| Client ID | `` | +| Client Secret | `` | +| Scope | `https:///.default` | + ## Compatibility The emulator is compatible with the following operations: @@ -156,7 +313,7 @@ services: The emulator integrates with Azure Event Grid to publish events using the [Event Grid event schema](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics#event-grid-event-schema) when configuration settings are deleted or modified. -The endpoint and key for the [Event Grid Topic](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics) can be set using the environment variables `Messaging__EventGridTopics__xyz__Endpoint` and `Messaging__EventGridTopics__xyz__Credential__Key` respectively where `xyz` is an arbitrary name. +The endpoint and key for the [Event Grid Topic](https://learn.microsoft.com/en-us/azure/event-grid/custom-topics) may be set using the environment variables `Messaging__EventGridTopics__xyz__Endpoint` and `Messaging__EventGridTopics__xyz__Credential__Key` respectively where `xyz` is an arbitrary name. ```yaml services: @@ -165,18 +322,15 @@ services: context: https://github.com/tnc1997/azure-app-configuration-emulator.git dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile environment: - - ASPNETCORE_HTTP_PORTS=8080 - Messaging__EventGridTopics__Contoso__Credential__Key=a2V5 - Messaging__EventGridTopics__Contoso__Endpoint=https://contoso.uksouth-1.eventgrid.azure.net/api/events - ports: - - "8080:8080" ``` ## Observability The emulator integrates with OpenTelemetry to provide metrics and traces. -The endpoint for the [OpenTelemetry Protocol (OTLP) Exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter) can be overridden using the environment variable `OTEL_EXPORTER_OTLP_ENDPOINT`. +The endpoint for the [OpenTelemetry Protocol (OTLP) Exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter) may be overridden using the environment variable `OTEL_EXPORTER_OTLP_ENDPOINT`. ```yaml services: @@ -187,13 +341,31 @@ services: depends_on: - opentelemetry-collector environment: - - ASPNETCORE_HTTP_PORTS=8080 - OTEL_EXPORTER_OTLP_ENDPOINT=http://opentelemetry-collector:4317 - ports: - - "8080:8080" opentelemetry-collector: image: otel/opentelemetry-collector-contrib - ports: - - "4317:4317" - - "4318:4318" +``` + +## SSL / TLS + +The emulator may be configured to serve requests over HTTPS using a [self-signed certificate](https://wikipedia.org/wiki/self-signed_certificate). + +```shell +openssl req -x509 -out ./emulator.crt -keyout ./emulator.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=azure-app-configuration-emulator' -addext 'subjectAltName=DNS:azure-app-configuration-emulator' +``` + +The port for HTTPS must be set using the environment variable [`ASPNETCORE_HTTPS_PORTS`](https://learn.microsoft.com/aspnet/core/security/enforcing-ssl#port-configuration). + +```yaml +services: + azure-app-configuration-emulator: + build: + context: https://github.com/tnc1997/azure-app-configuration-emulator.git + dockerfile: ./src/AzureAppConfigurationEmulator/Dockerfile + environment: + - ASPNETCORE_HTTP_PORTS=8080 + - ASPNETCORE_HTTPS_PORTS=8081 + volumes: + - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro + - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro ``` diff --git a/src/AzureAppConfigurationEmulator/AzureAppConfigurationEmulator.csproj b/src/AzureAppConfigurationEmulator/AzureAppConfigurationEmulator.csproj index 2e54da8..85b213c 100644 --- a/src/AzureAppConfigurationEmulator/AzureAppConfigurationEmulator.csproj +++ b/src/AzureAppConfigurationEmulator/AzureAppConfigurationEmulator.csproj @@ -9,6 +9,7 @@ + diff --git a/src/AzureAppConfigurationEmulator/Program.cs b/src/AzureAppConfigurationEmulator/Program.cs index 6bdc000..7c4a097 100644 --- a/src/AzureAppConfigurationEmulator/Program.cs +++ b/src/AzureAppConfigurationEmulator/Program.cs @@ -1,3 +1,4 @@ +using System.Net.Http.Headers; using Azure.Messaging.EventGrid; using AzureAppConfigurationEmulator; using AzureAppConfigurationEmulator.Authentication.Hmac; @@ -22,7 +23,29 @@ options.AddOtlpExporter(); }); -builder.Services.AddAuthentication().AddHmac(); +builder.Services.AddAuthentication(options => + { + options.DefaultScheme = "MicrosoftEntraId"; + }) + .AddHmac() + .AddJwtBearer("MicrosoftEntraId", options => + { + options.TokenValidationParameters.ValidateAudience = true; + options.TokenValidationParameters.ValidateIssuer = true; + + options.ForwardDefaultSelector = context => + { + if (AuthenticationHeaderValue.TryParse(context.Request.Headers.Authorization, out var value)) + { + if (value.Scheme.Equals("HMAC-SHA256", StringComparison.OrdinalIgnoreCase)) + { + return HmacDefaults.AuthenticationScheme; + } + } + + return null; + }; + }); builder.Services.AddAuthorization(); diff --git a/src/AzureAppConfigurationEmulator/appsettings.json b/src/AzureAppConfigurationEmulator/appsettings.json index 06b149f..4ccbde0 100644 --- a/src/AzureAppConfigurationEmulator/appsettings.json +++ b/src/AzureAppConfigurationEmulator/appsettings.json @@ -1,10 +1,12 @@ { "Authentication": { - "DefaultScheme": "Hmac", "Schemes": { "Hmac": { "Credential": "abcd", "Secret": "c2VjcmV0" + }, + "MicrosoftEntraId": { + "ValidAudience": "https://azconfig.io" } } },