Azure App Insights 中的异常过多(已恢复)

Posted

技术标签:

【中文标题】Azure App Insights 中的异常过多(已恢复)【英文标题】:Too many (recovered) exceptions in Azure App Insights 【发布时间】:2021-10-26 08:11:06 【问题描述】:

我有一个问题:Application Insights 收到太多误报,并且正在发送有关异常情况的邮件,经过调查,这些异常情况不会对我们的应用程序造成任何问题

总结,TL;DR

这是一个 X->Y 问题。问题 Y 是 AAI 正在记录大量服务器异常,请参阅详细说明,并向我们发送警报。问题 X 是 JWT 身份验证中间件抛出关于不匹配密钥的异常,但正在恢复所有这些异常,并切换到不同的 OIDC 提供程序。结果调用成功。

我可以做些什么来修复这些异常或将这些异常列入白名单?

问题 2:异常何时记录到 AAI?仅当它们未处理或记录器决定处理时?

上下文

我们的应用程序通过经过身份验证的 webhook 从 Twilio Sendgrid 接收电子邮件数据。它还允许我们的 B2C 租户用户访问应用程序并浏览数据/统计信息。

B2C 不允许客户端凭据流动,并且 Sendgrid 不支持范围。 最终我们最终使用了两个 OIDC 提供程序:用于交互用户的 Azure AD B2C,以及内存中的 OpenIddict 向我们验证 Sendgrid 服务。

一些代码

    public void ConfigureServices(IServiceCollection services)

        services.AddLogging(
            configuration => configuration
                .AddApplicationInsights()
                .SetMinimumLevel(LogLevel.Trace)
                .AddConsole()
        );

        services.ConfigureOpenIddictAuthentication();

        services
            .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApi(Configuration)
            //.EnableTokenAcquisitionToCallDownstreamApi()
            //.AddInMemoryTokenCaches()
            ;

        services.AddAuthorization(authorization => authorization
            .AddPolicy("AzureSendgridPolicy", policy => policy
                .RequireAuthenticatedUser()
                .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme,
                    OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)
            )
        );
    

    public static IServiceCollection ConfigureOpenIddictAuthentication(this IServiceCollection services)
    
        services.AddDbContext<OpenIddictDbContext>(ef => ef
                // Configure the context to use an in-memory store.
                // This prevents multiple cluster instances from deployment
                .UseInMemoryDatabase(nameof(OpenIddictDbContext))
                // Register the entity sets needed by OpenIddict.
                .UseOpenIddict()
            )
            .AddOpenIddict(options =>
                options.AddServer(server => server
                        .DisableAccessTokenEncryption() //Just for development

                        //Development: no time to waste on certificate management today
                        .AddEphemeralEncryptionKey()
                        .AddEphemeralSigningKey()
                        .RegisterClaims(OpenIddictConstants.Claims.Role)
                        .RegisterScopes(OpenIddictConstants.Scopes.Roles)
                        .SetTokenEndpointUris("/api/v1/Auth/token")
                        .SetAuthorizationEndpointUris("/api/v1/Auth/authorize")
                        .AllowClientCredentialsFlow() //Only one supported by Sendgrid
                        .UseAspNetCore()
                        .EnableTokenEndpointPassthrough())
                    .AddCore(core => core.UseEntityFrameworkCore(ef => ef.UseDbContext<OpenIddictDbContext>()))
                    .AddValidation(validation => validation
                        .UseLocalServer(_ => )
                        .UseAspNetCore(_ => )
                    )
            )
            .AddHostedService<OpenIddictHostedService>()
            .AddAuthentication()
            ;

        return services;
    

Azure 应用程序洞察

在AAI上,我发现最常抛出的异常是SecurityTokenUnableToValidateException

它被抛出很多次,比真正的401s 要多得多。由于开发环境中的临时密钥,每次重新启动应用程序时,OpenIddict 都会重新生成 JWK。

但仔细观察一些痕迹,我发现这不是错误

这是我的发现:

服务器返回 204 作为涉及的数据库,100% 向数据库写入数据(401 不涉及 EF 的数据库访问) 异常分析发现异常中指定的JWK

异常分析

查看抛出的异常,这是来自 AAI 的描述性文本

IDX10516: Signature validation failed. Unable to match key: 
kid: 'RMHQYNQ4TV9KUHI2EI-INM-XYSOF_1RETVYMWQGE'.
Exceptions caught:
 ''. 
token: '"alg":"RS256","kid":"RMHQYNQ4TV9KUHI2EI-INM-XYSOF_1RETVYMWQGE","typ":"at+jwt"."sub":"SendgridWebhook","name":"Sendgrid Webhook API","oi_prst":"SendgridWebhook","client_id":"SendgridWebhook","oi_tkn_id":"8d0d5f94-2094-4a21-b84d-304d1d99e3fb","exp":1629910230,"iss":"https://****.azurewebsites.net/","iat":1629906630'. Valid Lifetime: 'True'. Valid Issuer: 'False' 

堆栈跟踪

Microsoft.IdentityModel.Tokens.SecurityTokenUnableToValidateException:
   at Microsoft.IdentityModel.Tokens.InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedJwt (Microsoft.IdentityModel.Tokens, Version=6.10.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature (System.IdentityModel.Tokens.Jwt, Version=6.10.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken (System.IdentityModel.Tokens.Jwt, Version=6.10.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler+<HandleAuthenticateAsync>d__6.MoveNext (Microsoft.AspNetCore.Authentication.JwtBearer, Version=5.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)

我确定会发生以下情况

    应用收到 JWT 由于注册了多个 OIDC 提供程序,运行时会同时获取 B2C 和自身应用程序的 JWK 根据 B2C 密钥验证 JWT,失败 已根据自身密钥验证 JWT,成功 已授予访问权限

我相信在框架中的某个地方,代码结构良好,如下所示。由于要尝试多个提供程序,因此只有当所有提供程序都失败时才会引发异常。否则,简单的 for 循环将作为对异常的恢复

object principal = null;
Exception toThrow = null;
for (IAuthenticationProvider provider: GetProviders) 
    try 
        principal = provider.Authenticate(jwt);
     catch(SomeKindOfJwtException ex) 
        toThrow = ex;
    

if (principal == null) //and perhaps the exception is not null
    throw toThrow;

看看那个 JWK RMHQYNQ4TV9KUHI2EI-INM-XYSOF_1RETVYMWQGE,我可以通过浏览器导航 https://***.azurewebsites.net/.well-known/jwks 轻松找到它


  "keys": [
    
      "kid": "RMHQYNQ4TV9KUHI2EI-INM-XYSOF_1RETVYMWQGE",
      "use": "sig",
      "kty": "RSA",
      "alg": "RS256",
      "e": "AQAB",
      "n": "rMhqYnq4tv9kuHi2Ei-Inm-xysof_1retVymwqGeQ4hnlCRgrMAODGD4qxybhnpufuitEQRckCb4P49O_qafSQ0ocgRRIIuQJc-vLhLJHGp681_9cZT-jGxHnGw5Jdr0NZxH8RwV6cXcmpRN6f2WupujyhLLNwuu8aaTrucHA3JXshib9ad9R96OacT1r6X77HHXdSzURLRWH-f2JFwpBjOvVfJPxW4dzPY0BT7CzP3lxVvGiNXOp4-E8kVz1jER2EP5wO0Ho2qjlIbGUvGF1ui7GxLItldDs-PkZOGGvsO7yS7aeQHSiMTJt7EO-w-ffCJYv-ZColAiHO9jNL0NmQ"
    
  ]

我也做得太多,偷看微软的资源。 Here 应该是抛出异常的地方,可能 here 应该是记录异常的地方

【问题讨论】:

【参考方案1】:

我可以做些什么来修复这些异常或将这些异常列入白名单?

添加telemetry filter。根据异常遥测,您可以决定放弃遥测。

问题 2:异常何时记录到 AAI?仅当它们未处理或记录器决定处理时?

当未处理时,或当指示这样做时。例如,当使用 ILogger 记录异常时,在使用 AAI ILogger (see docs) 时也会将其记录到 AAI 中

【讨论】:

从我链接的微软代码来看,JWTHandler 将失败的尝试记录到记录器,记录器配置为记录到 AAI。我将尝试过滤遥测数据,并希望在答案上打一个绿色复选标记 不认为您可以发布一个示例过滤器来实现这一点?我有完全相同的场景,我怀疑过滤器是减少 AAI 噪声的唯一方法。谢谢!

以上是关于Azure App Insights 中的异常过多(已恢复)的主要内容,如果未能解决你的问题,请参考以下文章

text Azure App Insights Kubernetes日志

从 Azure Application Insights 连续导出的 ParsedStack 异常的流分析查询

用于 Python 应用程序的 Azure Application Insights 日志记录 - 显式设置异常属性

如何使用 Pyspark 提取 Azure Application Insights 事件?

Azure App Service Application Insights 不显示依赖的 sql 命令文本

Azure App Insights REST API 获取内存使用情况