azure - Azure Key Vault 事件 - 是否可以订阅事件网格主题(不是系统事件网格主题)?
问题描述
我可以在 Azure Key Vault 创建事件订阅,但它只允许系统事件网格主题,而不是自定义事件网格主题。我的首选是自定义事件网格主题,因为我可以分配托管标识并将必要的 RBAC 授予托管标识。
是否可以将 Azure Key Vault 配置为将事件发送到自定义事件网格主题?
这是一个示例自定义事件网格主题:
{
"name": "demoeventsubscription",
"properties": {
"topic": "/subscriptions/my-subscription-id/resourceGroups/EventGrids/providers/Microsoft.EventGrid/topics/kvTpoic",
"destination": {
"endpointType": "AzureFunction",
"properties": {
"resourceId": "/subscriptions/my-subscription-id/resourceGroups/aspnet4you/providers/Microsoft.Web/sites/afa-aspnet4you/functions/EventsProcessor",
"maxEventsPerBatch": 1,
"preferredBatchSizeInKilobytes": 64
}
},
"filter": {
"includedEventTypes": [
"Microsoft.KeyVault.SecretNewVersionCreated"
],
"advancedFilters": []
},
"labels": [],
"eventDeliverySchema": "EventGridSchema"
}
}
解决方案
@MayankBargali-MSFT - 感谢您在此处签入 GitHub 参考。您是对的,Azure 资源(即密钥保管库、存储等)当前未设计为使用自定义事件网格主题。这些资源是为系统事件网格主题预先配置的,不允许客户附加身份并且没有身份客户无法保护目标或目标资源(事件的订阅者)免受未经授权的访问。
正如客户所期望的那样,Azure 将允许自定义事件网格主题或修改系统事件网格主题的功能以附加托管标识。我们将能够在目标资源上使用 RBAC(在我的例子中是 azure 函数)来控制托管身份的安全性。
为了大家的信息,我使用以下azure函数来验证系统事件网格主题在调用azure函数(webhook)时没有在请求头中传递任何身份-
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.EventGrid;
using Microsoft.Azure.EventGrid.Models;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AzureFunctionAppsCore
{
public static class SampleEventGridConsumer
{
[FunctionName("SampleEventGridConsumer")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req, ILogger log)
{
log.LogInformation($"C# HTTP trigger function begun");
string response = string.Empty;
try
{
JArray requestHeaders = Utils.GetIpFromRequestHeadersV2(req);
log.LogInformation(requestHeaders.ToString());
string requestContent = req.Content.ReadAsStringAsync().GetAwaiter().GetResult();
log.LogInformation($"Received events: {requestContent}");
// Get the event type dynamically so that we can add/update custom event mappings to EventGridSubscriber
// Keep in mind, this is designed for Key Vault event type schema only.
KeyVaultEvent[] dynamicEventsObject = JsonConvert.DeserializeObject<KeyVaultEvent[]>(requestContent);
EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();
foreach(KeyVaultEvent kve in dynamicEventsObject)
{
eventGridSubscriber.AddOrUpdateCustomEventMapping(kve.eventType, typeof(KeyVaultEventData));
}
EventGridEvent[] eventGridEvents = eventGridSubscriber.DeserializeEventGridEvents(requestContent);
foreach (EventGridEvent eventGridEvent in eventGridEvents)
{
if (eventGridEvent.Data is SubscriptionValidationEventData)
{
var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
log.LogInformation($"Got SubscriptionValidation event data, validationCode: {eventData.ValidationCode}, validationUrl: {eventData.ValidationUrl}, topic: {eventGridEvent.Topic}");
// Do any additional validation (as required) such as validating that the Azure resource ID of the topic matches
// the expected topic and then return back the below response
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
log.LogInformation($"Sending ValidationResponse: {responseData.ValidationResponse} and HttpStatusCode : {HttpStatusCode.OK}.");
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(responseData), Encoding.UTF8, "application/json")
};
}
else if (eventGridEvent.Data is StorageBlobCreatedEventData)
{
var eventData = (StorageBlobCreatedEventData)eventGridEvent.Data;
log.LogInformation($"Got BlobCreated event data, blob URI {eventData.Url}");
}
else if (eventGridEvent.Data is KeyVaultEventData)
{
var eventData = (KeyVaultEventData)eventGridEvent.Data;
log.LogInformation($"Got KeyVaultEvent event data, Subject is {eventGridEvent.Subject}");
var connectionString = Config.GetEnvironmentVariable("AzureWebJobsStorage");
// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
// Retrieve a reference to a container.
CloudQueue queue = queueClient.GetQueueReference("eventgridqueue");
// Create the queue if it doesn't already exist
await queue.CreateIfNotExistsAsync();
// Create a message and add it to the queue.
CloudQueueMessage message = new CloudQueueMessage(JsonConvert.SerializeObject(eventGridEvent));
await queue.AddMessageAsync(message);
}
}
}
catch(Exception ex)
{
log.LogError(ex.ToString());
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Ok", Encoding.UTF8, "application/json")
};
}
}
public class KeyVaultEvent
{
public string id { get; set; }
public string topic { get; set; }
public string subject { get; set; }
public string eventType { get; set; }
public DateTime eventTime { get; set; }
public KeyVaultEventData data { get; set; }
public string dataVersion { get; set; }
public string metadataVersion { get; set; }
}
public class KeyVaultEventData
{
public string Id { get; set; }
public string vaultName { get; set; }
public string objectType { get; set; }
public string objectName { get; set; }
public string version { get; set; }
public string nbf { get; set; }
public string exp { get; set; }
}
}
辅助功能:
public static JArray GetIpFromRequestHeadersV2(HttpRequestMessage request)
{
JArray values = new JArray();
foreach(KeyValuePair<string, IEnumerable<string>> kvp in request.Headers)
{
values.Add(JObject.FromObject(new NameValuePair(kvp.Key, kvp.Value.FirstOrDefault())));
}
return values;
}
共享我的 .net core 3.1 的项目依赖项,以防您想在本地运行:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.6" />
<PackageReference Include="Microsoft.Azure.EventGrid" Version="3.2.0" />
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="1.34.0" />
<PackageReference Include="Microsoft.Azure.Management.ResourceManager.Fluent" Version="1.34.0" />
<PackageReference Include="Microsoft.Azure.OperationalInsights" Version="0.10.0-preview" />
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.5.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventGrid" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.SendGrid" Version="3.0.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens.Saml" Version="5.6.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
那么,系统事件网格主题在调用 azure 函数时传递的标头是什么?
[{
"name": "Accept-Encoding",
"value": "gzip"
}, {
"name": "Connection",
"value": "Keep-Alive"
}, {
"name": "Host",
"value": "afa-aspnet4you.azurewebsites.net"
}, {
"name": "Max-Forwards",
"value": "10"
}, {
"name": "aeg-subscription-name",
"value": "MYSUBSCRIPTION2"
}, {
"name": "aeg-delivery-count",
"value": "0"
}, {
"name": "aeg-data-version",
"value": "1"
}, {
"name": "aeg-metadata-version",
"value": "1"
}, {
"name": "aeg-event-type",
"value": "Notification"
}, {
"name": "X-WAWS-Unencoded-URL",
"value": "/api/SampleEventGridConsumer?code=removed=="
}, {
"name": "CLIENT-IP",
"value": "52.154.68.16:58880"
}, {
"name": "X-ARR-LOG-ID",
"value": "1e5b1918-9486-4b41-ab1d-7a644bc263d2"
}, {
"name": "DISGUISED-HOST",
"value": "afa-aspnet4you.azurewebsites.net"
}, {
"name": "X-SITE-DEPLOYMENT-ID",
"value": "afa-aspnet4you"
}, {
"name": "WAS-DEFAULT-HOSTNAME",
"value": "afa-aspnet4you.azurewebsites.net"
}, {
"name": "X-Original-URL",
"value": "/api/SampleEventGridConsumer?code=removed=="
}, {
"name": "X-Forwarded-For",
"value": "52.154.68.16:58880"
}, {
"name": "X-ARR-SSL",
"value": "2048|256|C=US, O=Microsoft Corporation, CN=Microsoft RSA TLS CA 01|CN=*.azurewebsites.net"
}, {
"name": "X-Forwarded-Proto",
"value": "https"
}, {
"name": "X-AppService-Proto",
"value": "https"
}, {
"name": "X-Forwarded-TlsVersion",
"value": "1.2"
}
]
Azure 函数使用 api 密钥,但它是共享的,在生产系统中不被视为真正的安全性。系统主题没有发送任何我们可以用来查找任何身份的标头。
此处显示的 azure 函数处理的几个事件 -
[{
"id": "2ff9617d-e498-4527-8467-d36eeff6b94b",
"topic": "/subscriptions/truncated/resourceGroups/key-vaults/providers/Microsoft.KeyVault/vaults/aspnet4you-keyvault",
"subject": "TexasSnow",
"eventType": "Microsoft.KeyVault.SecretNewVersionCreated",
"data": {
"Id": "https://aspnet4you-keyvault.vault.azure.net/secrets/TexasSnow/106b1d8450e6404bb323abf650c81496",
"VaultName": "aspnet4you-keyvault",
"ObjectType": "Secret",
"ObjectName": "TexasSnow",
"Version": "106b1d8450e6404bb323abf650c81496",
"NBF": null,
"EXP": null
},
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2021-02-20T06:50:08.6207125Z"
}
]
[
{
"id": "996c900f-b697-4754-916c-67e485e141f9",
"topic": "/subscriptions/b63613a2-9fc8-47ad-a65c-e1d1eba108be/resourceGroups/key-vaults/providers/Microsoft.KeyVault/vaults/aspnet4you-keyvault",
"subject": "EventTestKey",
"eventType": "Microsoft.KeyVault.KeyNewVersionCreated",
"data": {
"Id": "https://aspnet4you-keyvault.vault.azure.net/keys/EventTestKey/22f3358955174cff9f5d5f24f5f2ecb0",
"VaultName": "aspnet4you-keyvault",
"ObjectType": "Key",
"ObjectName": "EventTestKey",
"Version": "22f3358955174cff9f5d5f24f5f2ecb0",
"NBF": null,
"EXP": null
},
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2021-02-20T20:08:00.4520828Z"
}
]
下一个是什么?正如@MayankBargali-MSFT 所说,天蓝色正在按设计工作。我已向feedback.azure.com发送功能请求。
推荐阅读
- sql-server - 如果我有条件地选择更多列,为什么这条 SQL 语句的执行时间会增加?
- batch-file - 跳到批处理程序的下一步
- excel - VBA 格式化后 SAS/CSV 数据消失
- github - 不知道如何关闭 MD 文件中的变量
- python - 我正在尝试分析 Scantron 表单并为 OMR 项目创建密钥
- java - Log4j2 - 无法访问 ListAppender
- json - 无法使用 JSON 创建 VNet
- matlab - 在Matlab中从另一个具有多列的矩阵的行构造单列矩阵
- swift - 聊天 Swift 中的实时用户计数
- hybris - 如何访问 Hybris 中的登录信息?