Syntax Highlighter

miércoles, 21 de octubre de 2020

Storing Azure App Service secrets on Azure Key Vault

Today I'm going to show how to store Azure App Service configuration secrets on Azure Key Vault. In this example, I'm going to use a DNN Platform based website deployed on Azure App Service using a SQL Database, storing the SQL database connection string in Azure Key Vault. I won't change the application codebase to access the Azure Key Vault in any way. We Will take advantage of the App Service managed identity feature to automatically retrieve the Key Vault secrets.

Provision the Azure Key Vault

  1. Provision the Azure Key Vault

     

  2. For this demo, we will change the permissions model to the new Azure role-based access control (RBAC). It's currently in Preview.

  3. Depending on your deployment, change the Networking as desired. For this demo we will leave it as Public endpoint to match the App Service configuration

  4. Click on the Review + Create button to start the deployment
  5. Once created, add yourself to the role "Key Vault Administrator (Preview)" via the Access Control (IAM). Now you can start managing secrets.

Add the Connection String to the Key Vault secrets

  1. Add a new secret to the Key Vault

  2. Specify the secret of type "Manual", give it a name and set the value to the current SQL Database connection string stored in the Application web.config

  3. Click on Create. Once created, click on the secret to see the secret details, and then click again on the current version
  4. Next to the Secret Identifier, click on the copy to the clipboard button

     

Add the secret identifier reference to the Azure App Service Settings

  1. Open the App Service configuration settings, and add a new Connection String setting
  2. Type the name of the connection string ("SiteSqlServer" for DNN Platform) and set the value "@Microsoft.KeyVault(SecretUri=VALUE_FROM_CLIPBOARD)", where the VALUE_FROM_CLIPBOARD is the one from step 4 in previous section.
  3. Click on Save to save the app settings

     

Allow the App Service to access the Key Vault

  1. On the App Service again, click on Identity to enable the System Assigned identity.
  2. Click on save after turning "On" the status

  3. Click on the "Role Assignments" button and then click on the "Add role assigment (Preview)"

  4. In the role assignment, choose scope "Key Vault", subscription the subscription where you created the Key Vault on previous steps and the name of the Key Vault resource. For the role just select "Key Vault Secrets User (preview)"

  5. Finally go to the web.config file of your DNN Platform website and clear the connection string contents.

  6. Visit the website and check that loads successfully. Now the connection string is safely stored in the Azure Key Vault, and it's no longer stored on the file system.

 

Known issues

  1. ERROR: You get an error "Keyword not supported: '@microsoft.keyvault(secreturi'". I have experienced that the RBAC permissions can take a one or two minutes to be applied, so try after a few minutes. Also try restarting the application thought the App Service portal so nothing is cached.

  2. When checking the DNN log4net logs, you see lot of errors from a process trying to initialize a database connection with an invalid connection string during the DNN initialization process. This error is currently happening on versions 9.7.2 or earlier with a pending pull request to fix it https://github.com/dnnsoftware/Dnn.Platform/issues/4227
  3. IMPORTANT: If you plan to use the Azure App Service Backup feature, don't use this method to store SQL Database connection strings in Azure Key Vault, since the App Service backup feature doesn't support them https://stackoverflow.com/questions/56101504/backingup-azure-webapp-when-connection-string-is-injected-through-keyvault  

Hope this helps!

 

 

martes, 18 de agosto de 2020

Using custom claim mappings on DNN Azure AD module

With the arrival of the DNN Azure AD v4.0.x module, lot of new settings have been introduced to support scenarios that were already resolved with the twin module for Azure AD B2C. Things such as Role Sync, Profile sync (including the profile picture), JWT auth using Azure AD tokens on DNN WebAPI controllers, reusing the client-side token to call other services outside DNN and claim mapping are now supported.

Before continue reading, please note that Azure AD is supporting now 1.0 and 2.0 (preview) tokens, and there are some differences between them. I recommend to read "How to: Provide optional claims to your app" article

"While optional claims are supported in both v1.0 and v2.0 format tokens, as well as SAML tokens, they provide most of their value when moving from v1.0 to v2.0. One of the goals of the v2.0 Microsoft identity platform endpoint is smaller token sizes to ensure optimal performance by clients. As a result, several claims formerly included in the access and ID tokens are no longer present in v2.0 tokens and must be asked for specifically on a per-application basis."

I'm currently working on adapting the DNN Azure AD module v5.0 to fully supports the v2.0 Microsoft Identity Platform endpoint. The following instructions work for DNN Azure AD module v4.0.3 and later.

Configuring claim mappings

Let's start today with the new "User Mappings" tab.

The new mappings tab has three subareas:

  • User Mappings: area to specify how user mappings are done, by mapping user DNN user properties with claims available on the JWT issued token from Azure AD once signed in. These properties are fixed:
    • PortalId: (optional) Allows to specify a claim to map the user portal ID. Note that this must be implemented with an Azure AD User attribute through application extensions, see more on this later below;
    • Id: Allows to specify which claim will be used as User Id. By default, will be the "upn" (user principal name). You can potentially use "upn" or "unique_name" (this only only on v1.0 tokens), that looks like username@domainname (or username@tenant.onmicrosoft.com), the "oid" (object ID) claim that is the guid of the Azure AD user object, or any other claim uniquely that identifies the user, such as an Azure AD User attribute through application extensions with, for example, the EmployeeID as value.
    • FirstName: Allows to specify which claim will be used as first name. By default, the "given_name" claim
    • LastName: Allows to specify which claim will be used as last name. By default, the "family name" claim
    • DisplayName: Allows to specify which claim will be used as display name. By default, the "name" claim
    • Email: Allows to specify which claim will be used as e-mail. By default, the "upn" claim. The "email" claim can also be setup after adding it as optional claim to the token on the Azure portal, on the application token configuration section. This also requires to grant the "email" permission to the Azure AD application.
  • User Profile Mappings: area to specify mappings between user profile properties and token claims. Works in the same way than the user mappings, but you will be using mostly optional and custom claims setup on the Azure AD Application. See how to setup optional and custom claims below.
  • Role Mappings: this allows to map Azure AD roles with DNN roles. Has nothing to do with claims, and another article talking about role sync setup is coming.

The list of supported claims for an application can be obtained from:

  • v1.0 tokens: https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration?appid={client-id}
  • v2.0 tokens: https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration?appid={client-id}

Plus, the "core claim set" present in every token regardless of the policy. These claims are also considered restricted and can't be modified. More info at https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping

Also note that "upn", "given_name", "family_name" and "unique_name" claims are part of the "core claim set" when using v1.0 tokens, but are not included by default when using v2.0 tokens. "unique_name" is only available on v1.0 so the recommendation is to use "upn" instead that is available on both versions.

Token configuration on the Azure AD application

Once you have setup the application, by default you will be using the v1.0 endpoint so tokens will have the "core claim set" by default. A default issued v1.0 token will look like the one below:

The most interesting claims from the DNN point of view are:

  • "aud" (Audience): the audience of the token, normally the Application ID unless you change the audience setting on the JWT Auth settings on the module advanced settings
  • "exp" (Expires at): expiration datetime in "NumericDate" format
  • "amr" (Authentication Method References): indicating if the user used "pwd", "mfa", or other authentication methods
  • "appid": the application id setup on the DNN module settings
  • "family_name": the last name of the user as stored in Azure AD
  • "given_name": the first name of the user as stored in the Azure AD
  • "name": display name of the user as stored in the Azure AD
  • "ipaddr": client IP address from where the token was requested
  • "oid": object Id of the user as stored in the Azure AD
  • "scp": scopes allowed for the token (the module issues a token for all the scopes configured unless something else is setup in the API Resource settings on the module advanced settings).
  • "tid": Azure AD tenant id
  • "unique_name": (only on v1.0 tokens), the unique name of the user. When using external providers such as MSA, can be in the format "live#user@tenant". Recommended to use "upn"
  • "upn": the user principal name. Not available on v1.0 tokens and MSA (live.com, …) accounts.

So, by default, the default DNN Azure AD module configuration supports the default token settings to make the things easier to setup.

Setting up optional claims

With v1.0 tokens, you can specify two types of additional optional claims:

  • Built-in: you can specify optional built-in claims by selecting them through the token configuration application section or manually editing them directly through the manifest:

  • Extension attributes: you can create your own directory schema extensions for the application on the "Users" object and store your personal attributes directly on Azure AD. In order to do it, you need to modify the Azure AD schema first.
    • Open a new Azure Cloud Shell in the Azure portal (icon on the top right portal area) using a PowerShell console, and run the following commands
    • Login into Azure Active Directory

      > Connect-AzureAD

       

    • Set a variable with the application object Id. Note that this isn't the AppID, is the Application Object Id available on the application settings page on the Azure portal

      > $appObjectId = "xxxxxxx-xxxxx-xxxxx-xxxxx-xxxxxxxxxx"

       

    • Create the application extension property "Department"

      > New-AzureADApplicationExtensionProperty -ObjectId $appObjectId -Name "Department" -DataType "String" -TargetObjects "User"

       

    • Now set the setting of this user through the Graph API or just by executing the following PowerShell command, where $userObjId is the object Id of the user and <appId> is the App Id without dashes

      > Set-AzureADUserExtension -ObjectId $userObjectId -ExtensionName "extension_<appId>_Department" -ExtensionValue "Cloud Security"

       

      Once this is done, you can select the extension from the optional claims UI. The claim name will be "extn.<attributeName>" on the JWT token.

 

Now if you login again into the site, you will have the new claims as part of your token, and you can use them for mapping to user or profile properties.

 

 

Mapping non built-in Azure AD user object properties and more

All of what we have seen before has been done by using v1.0 tokens. What about adding non built-in properties, such as office location, phone number and so on? You can start using the new v2.0 tokens even while still using the 1.0 auth endpoint. How? I recommend first to take a look to the article "How to: Customize claims emitted in tokens for a specific app in a tenant (Preview)". Currently this way of customizing the claims is supported only by PowerShell, and when implemented, supersedes the claims customization offered through the portal today. Configurations made through the methods detailed in this section will not be reflected in the Azure portal (at the time of writing).

  • Edit the application manifest and set:

    "acceptMappedClaims": true,

    "accessTokenAcceptedVersion": 2

     

     

  • Open a PowerShell session in your desktop and install the AzureADPreview module (I tried to run this into the Azure Cloud Shell, and while I wasn't able to run the necessary commands):

    > Install-Module AzureADPreview

     

  • Login into Azure Active Directory

    > Connect-AzureAD

     

  • Obtain the service principal object

    > $appID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx" # The AppID of the application

    > $svcPrincipal = Get-AzureADServicePrincipal -Filter "servicePrincipalNames/any(n: n eq '$appID')"

     

  • Create a new Azure AD policy

     

    > $policy = New-AzureADPolicy -Definition @('{"ClaimsMappingPolicy":{"Version":1,"IncludeBasicClaimSet":"true", "ClaimsSchema": [{"Source":"user","ID":"physicalDeliveryOfficeName","JwtClaimType":"officename"},{"Source":"user","ID":"country","JwtClaimType":"country"},{"Source":"user","ID":"givenName","JwtClaimType":"given_name"},{"Source":"user","ID":"surname","JwtClaimType":"family_name"},{"Source":"user","ID":"displayName","JwtClaimType":"name"},{"Source":"user","ID":"userPrincipalName","JwtClaimType":"upn"}]}}') -DisplayName "IntelequiaWebMappings" -Type "ClaimsMappingPolicy"

     

  • Assign the new policy to the application

    > Add-AzureADServicePrincipalPolicy -Id $svcPrincipal.ObjectId -RefObjectId $policy.Id

     

  • Create an application signing key if not created before. This is necessary to use policy-based claims.

    > $appObjectId = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyy" # This is the object id of the application that can be found in the portal, not the application Id

    > New-AzureADApplicationKeyCredential -ObjectId $appObjectId -CustomKeyIdentifier "MySigningKey" -StartDate "11/7/2020" -Type "Symmetric" -Usage "Sign" -Value "P@assw0rd1!"

     

As result, we get a token like the one below. Note that the token includes the core claims set and the optional ones specified in the portal (because in the policy the "IncludeBasicClaimSet" was set to "true"), plus the claims added by setting the policy to the application.

References

Related Posts Plugin for WordPress, Blogger...