Enforce authentication when sending Application Insights telemetry

Tobias Zimmergren
Tobias Zimmergren

Presently sponsored by ScriptRunner
Get your free PowerShell Security e-Book!


Microsoft recently announced the capability, and public preview, of authenticated requests to Application Insights. This is an important update for a wide range of reasons.

In this post, we'll explore how to configure your Application Insights instance to reject requests that aren't authenticated and with the correct authorization, and also how to write the code to send authenticated requests.

We will now take a look at these things:

  • Configure required authentication on App Insights.
    • Verifying the configuration.
  • Sending telemetry.
    • Set the correct permissions to your identities/users.
    • Write and run the code.
  • Verify telemetry ingestion using Fiddler.
    • Discover any successes and errors.
    • Verify that it works when you are correctly authenticated and authorized.
    • Verify that it does not work if you are not authorized or authenticated correctly.

Tag along and enjoy.

Configure required authentication.

For demonstration purposes, I'm doing this from the Azure Portal.

Go to your Application Insights instance and select "Properties". From there, you now have the option to define the "LOCAL AUTHENTICATION" property, which you should now disable.

Configure local authentication with Application Insights from the Azure Portal.

Click the link to switch to Disabled and you'll see this sidebar slide out. Please make an important note of what this says about the required role for shipping logs. You will have to assign the named role to any identity or user that wants to send logs to this instance of Application Insights.

Disabling local authentication for Application Insights.

Verify the configuration is in place.

Exporting the template reveals the change has been made on the resource, and this is something we can define when we programmatically create new Application Insights resources. But that's for another time.

Here are the bits from the exported template that relates to the disabling of auth, namely DisableLocalAuth being set to true. This now enforces every request to this App Insights resource to be authenticated; otherwise, it will fail.

{
            "type": "microsoft.insights/components",
            "apiVersion": "2020-02-02",
            "name": "[parameters('components_appi_demos_auth_name')]",
            "location": "westeurope",
            "kind": "web",
            "properties": {
                "Application_Type": "web",
                "Flow_Type": "Redfield",
                "Request_Source": "IbizaAIExtension",
                "WorkspaceResourceId": "[parameters('workspaces_defaultworkspace_guidredacted_weu_externalid')]",
                "IngestionMode": "LogAnalytics",
                "publicNetworkAccessForIngestion": "Enabled",
                "publicNetworkAccessForQuery": "Enabled",
                "DisableLocalAuth": true
            }
        },
        ```

Send telemetry as an authenticated user.

The permissions.

To support the scenario, you need to grant the Monitoring Metrics Publisher permission on the user or identity to send requests to Application Insights.

In the Azure Portal, you would go to your App Insights instance -> Access Control (IAM) and add the Monitoring Metrics Publisher role to any identity that should be allowed to send requests to this resource.

The code.

The required NuGet packages are:

Here's a small code sample to demonstrate the required bits.

class Program
{
    // Read the blog: https://zimmergren.net/enforce-authentication-when-sending-application-insights-telemetry/ 

    // For demo purposes.
    // I am deliberetly disclosing my telemetry key here since my Application Insights only accept authenticated and authorized requests.
    private const string appiKey = "d85d8694-484c-4f8e-a261-ac9dd232146e";
    private const string appiEndpoint = "https://westeurope-5.in.applicationinsights.azure.com/";

    static void Main(string[] args)
    {
        //
        // Preparations.
        //

        // Construct our connection string - or just copy it from the Application Insights instance entirely.
        var appiConnectionString = $"InstrumentationKey={appiKey};IngestionEndpoint={appiEndpoint}";

        //
        // Wire up a new Application Insights client
        //
        // - Define the TelemetryConfiguration with a connections tring
        // - Define the identity to use. For development purposes, I'm using DefaultAzureCredentials(). In production/cloud workloads, this would be connected using ManagedIdentityCredentials in most of my scenarios.
        // -- Make sure your currently signed in user to VS has access to Application Insights, else it will not work, as it requires an authenticated user.
        // - Set up a new TelemetryClient, use our configuration, and send some traces.
            

        // required nuget: Microsoft.ApplicationInsights
        var appiConfig = new TelemetryConfiguration
        {
            ConnectionString = appiConnectionString
        };

        // required nuget: Azure.Identity
        var credential = new DefaultAzureCredential(); // For dev: Your VS user - make sure it has RBAC configured with the "Monitoring Metrics Publisher" role for your Application Insights instance.
        appiConfig.SetAzureTokenCredential(credential);

        var telemetryClient = new TelemetryClient(appiConfig);

        // Send some telemetry, to ensure they end up in the logs as we expect.
        telemetryClient.TrackTrace($"I code. Therefore I am.");
        telemetryClient.TrackException(new System.Exception("Unicorns does not exist"));
        telemetryClient.Flush();

        Console.WriteLine("Done sending some telemetry...");
    }
}

The main point in the code is TelemetryClient where we define the identity to use for the request. There's not any rocket science involved.

Running the code.

Running the code should ship the logs to your Log Analytics / Application Insights instance if things work as expected.

Reviewing the ingested logs in Application Insights that were sent as authenticated users.

However, it does not always work as expected when we do things. That is why the next section will come in handy to troubleshoot what's wrong, in case you don't see your requests being ingested after a few minutes.

Tip: Using Fiddler to understand if ingestion works.

To verify that our ingestion works, I'm using Fiddler. Upon sending the request (e.g., hitting F5 in my dev environment, in this particular case), Fiddler will log the request and anything that goes with it - including the headers and any issues in submitting the requests.

Verify the requests are returning 200 OK.

I have put an active filter on only requests from my Visual Studio process to discover the requests I am making more easily. In doing this, I can discover that I get my 200 OK results on my request, which is expected.

Receiving a 200 OK when sending Authenticated requests to Application Insights.

Verify that the requests do not contain errors.

Further, drilling into the actual request, I can see in the raw JSON of the request that it also gives this information:

{
   "itemsReceived":2,
   "itemsAccepted":2,
   "errors":[
      
   ]
}

Explore the JWT token to figure out which identity is being used.

These requests are made as authenticated users. To verify, you can grab the Bearer token from the request and check out the contents. I like to use https://jwt.ms or https://jwt.io to decode my tokens for non-production use.

I can see that the Bearer token is using my account, which has the required access to send authenticated requests into Application Insights.

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "<redacted>",
  "kid": "<redacted>"
}.{
  "aud": "https://monitor.azure.com/",
  "iss": "https://sts.windows.net/6134e5d3-a0f6-415f-8086-81b9ea6d93c5/",
  "iat": 1628111020,
  "nbf": 1628111020,
  "exp": 1628114920,
  "acr": "1",
  "aio": "<redacted>",
  "amr": [
    "pwd",
    "mfa"
  ],
  "appid": "<redacted>",
  "appidacr": "0",
  "deviceid": "<redacted>",
  "family_name": "Zimmergren",
  "given_name": "Tobias",
  "groups": [
    "<redacted>"
  ],
  "ipaddr": "<redacted>",
  "name": "Tobias Zimmergren",
  "oid": "<redacted>",
  "puid": "1003BFFD81D50E28",
  "rh": "<redacted>",
  "scp": "user_impersonation",
  "sub": "<redacted>",
  "tid": "<redacted>",
  "unique_name": "tobias@<redacted>.net",
  "upn": "tobias@<redacted>.net",
  "uti": "<redacted>",
  "ver": "1.0",
  "wids": [
    "<redacted>"
  ]
}.[Signature]

Verify and troubleshoot requests.

Understanding what happens with unauthenticated requests can be important if you need to troubleshoot further why this doesn't work in your case.

Let's try to send our telemetry with a few angles:

  • Without an unauthenticated user - no authentication at all.
  • With an authenticated user for a different tenant or resource.
  • With an authenticated user that is a Global Administrator for this tenant but does not have the specific Monitoring Metrics Publisher role assigned.

Verify what happens with an unauthenticated request.

First up, I'm running the code without an authenticated user. I have the telemetry key and the connection string, but I shouldn't ship any logs to this Application Insights because of the configuration server side.

Great Scott! It seems to work. Fiddler tells me Application Insights rejected the request:

{
   "itemsReceived":2,
   "itemsAccepted":0,
   "errors":[
      {
         "index":0,
         "statusCode":400,
         "message":"Authorization not supported"
      },
      {
         "index":1,
         "statusCode":400,
         "message":"Authorization not supported"
      }
   ]
}

Verify what happens with an authenticated user in a different tenant.

I am now running a request with a user authenticated in a different tenant. We have an authenticated identity and a bearer token. Just not for the correct resources.

Similar to the previous error, but slightly different. We are now getting a 401Indicating we need to authenticate.

{
   "itemsReceived":2,
   "itemsAccepted":0,
   "errors":[
      {
         "index":0,
         "statusCode":401,
         "message":"Authentication required"
      },
      {
         "index":1,
         "statusCode":401,
         "message":"Authentication required"
      }
   ]
}

Verify what happens with an authenticated request with an unauthorized user.

In this final test, I am running the request as the Global Admin in my tenant. However, I have removed the permission I mentioned earlier, the Monitoring Metrics Publisher role. This should, in theory, yield a 403because the user no longer is authorized to ship logs to this instance of Application Insights.

Let's see.

{
   "itemsReceived":2,
   "itemsAccepted":0,
   "errors":[
      {
         "index":0,
         "statusCode":403,
         "message":"Unauthorized"
      },
      {
         "index":1,
         "statusCode":403,
         "message":"Unauthorized"
      }
   ]
}

Voila. We have verified that the authentication and authorization work as expected. Only authenticated users that are authorized with RBAC permissions on this specific resource can send logs to it.

Summary

This wraps up another lengthy post about an ever so important topic. Working with monitoring and telemetry is key in our distributed solutions in a rapidly changing technical landscape.

With the requirement on authenticated users for Application Insights, we take a step closer to securing our resources. I have waited to see this capability for a long time. I am glad that it's here, and I will plan to make this the default go-to configuration for all Application Insights instances, where applicable, moving forward.

Thanks for reading - here are a few additional resources that can be helpful.

AzureApplication InsightsSecurity

Tobias Zimmergren

Hi, I'm Tobias. I plan, architect and develop software and distributed cloud services. Nice to meet you!