Служба приложений правильно управляет удостоверением личности и хранилищем ключей

#azure #azure-web-app-service #azure-keyvault #azure-managed-identity #azure-bicep

Вопрос:

В настоящее время я пытаюсь развернуть группу ресурсов с помощью azure bicep, однако у меня возникла проблема с использованием хранилища ключей для моей службы приложений azure. Я хотел бы знать, действительно ли я делаю это правильно. У меня есть основной файл бицепса, который соответствует:

 // params removed for brevity...

targetScope = 'subscription'

resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
  name: 'rg-${appName}-${region}'
  location: 'centralus'
}

module appServicePlan 'appplan.bicep' = {
  params: {
    sku: appServicePlanSku
    appName: appName
    region: region
  }
  scope: rg
  name: 'AppServicePlanDeploy'
}

module keyVault 'keyvault.bicep' = {
  params: {
    keyVaultName: keyVaultName
    sqlPassword: sqlServerPassword
    webSiteManagedId: webSite.outputs.webAppPrincipal
  }
  scope: rg
  name: 'KeyVaultDeploy'
  dependsOn: [
    webSite
  ]
}

module ai 'ai.bicep' = {
  scope: rg
  name: 'ApplicationInsightsDeploy'
  params: {
    name: appName
    region: region
    keyVaultName: keyVault.outputs.keyVaultName
  }
  dependsOn: [
    keyVault
  ]
}

resource kv 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
  name: keyVaultName
  scope: rg
}

module sql 'sqlserver.bicep' = {
  scope: rg
  name: 'SQLServerDeploy'
  params: {
    appName: appName
    region: region
    sqlPassword: kv.getSecret('sqlPassword')
    sqlCapacitity: sqlCapacitity
    sqlSku: sqlSku
    sqlTier: sqlTier
  }
  dependsOn: [
    keyVault
  ]
}

module webSite 'site.bicep' = {
  params: {
    appName: appName
    region: region
    keyVaultName: keyVaultName
    serverFarmId: appServicePlan.outputs.appServicePlanId
  }
  scope: rg
  name: 'AppServiceDeploy'
  dependsOn: [
    appServicePlan
  ]
}

 

Мой вопрос связан с реализацией сайта.бицепс, я начал с передачи секретного uri из экспортируемых переменных и создания веб-приложения в качестве последнего в app insights, sql и т. Д… все они должны быть настроены и в keyvault, прежде чем мы будем использовать их экспортированный секретный uri для создания конфигурации. У меня было что-то вроде:
сайт.бицепс (раньше):

   properties: {
    serverFarmId: serverFarmId
    keyVaultReferenceIdentity: userAssignedId
    siteConfig: {
      appSettings: [
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: '@Microsoft.KeyVault(SecretUri=${appInsightsConnectionString})'
        }
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: '@Microsoft.KeyVault(SecretUri=${appInsightsKey})'
        }
      ]
      netFrameworkVersion: 'v5.0'
    }
  
}

 

Единственная проблема с этой реализацией заключается в том, что хранилище ключей ДОЛЖНО быть создано до веб-сайта, поскольку sql, ai и другие службы будут хранить свои значения внутри хранилища ключей, чтобы веб-приложение могло использовать их соответствующие uri. Проблема в том, что KeyVault по праву не имеет представления, какой службе azure разрешить доступ к своим ключам.

Мой вопрос заключается в том, что решение создания веб-приложения перед хранилищем ключей является единственным способом решения этой проблемы? Я использую управляемые удостоверения в веб-приложении и хотел бы продолжать делать это, если это возможно. Мое окончательное решение закончилось примерно так:

site.bicep (final)

 // params removed for brevity...
resource webApplication 'Microsoft.Web/sites@2020-12-01' = {
  name: 'app-${appName}-${region}'
  location: resourceGroup().location
  tags: {
    'hidden-related:${resourceGroup().id}/providers/Microsoft.Web/serverfarms/appServicePlan': 'Resource'
  }
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: serverFarmId
    siteConfig: {
      appSettings: [
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
        }
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
        }
        {
          name: 'AngularConfig:ApplicationInsightsKey'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
        }
      ]
      netFrameworkVersion: 'v5.0'
    }
  }
}

output webAppPrincipal string = webApplication.identity.principalId
 

И KeyVault, который займет веб-сайт dependsOn

Кейволт.бицепс(финал):

 resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' = {
  name: keyVaultName
  location: resourceGroup().location
  properties: {
    enabledForDeployment: true
    enabledForTemplateDeployment: true
    enabledForDiskEncryption: true
    enableRbacAuthorization: true
    tenantId: subscription().tenantId
    sku: {
      name: 'standard'
      family: 'A'
    }
    accessPolicies: [
      {
        tenantId: subscription().tenantId
        objectId: webSiteManagedId
        permissions: {
          keys: [
            'get'
          ]
          secrets: [
            'list'
            'get'
          ]
        }
      }
    ]
  }
}
 

Ответ №1:

Просто рассматривайте свой accessPolicies ресурс как отдельный и добавляйте его при создании Хранилища ключей и службы приложений. То же самое относится к разделу конфигурации и строкам подключения. Ознакомьтесь с документацией здесь.

В шаблонах ARM вы можете добиться того же эффекта, используя вложенные шаблоны. В Bicep это примерно то же самое, но вы объявляете их как отдельный ресурс, который обычно содержит имя родителя (например name: '${kv.name}/add' , name: '${webSite.name}/connectionstrings' )

Образец

Шаг 1. Создайте службу приложений без раздела конфигурации

  resource webSite 'Microsoft.Web/sites@2020-12-01' = {
      name: webSiteName
      location: location
      properties: {
        serverFarmId: hostingPlan.id
        siteConfig:{
          netFrameworkVersion: 'v5.0'
        }
      }
      identity: {
        type:'SystemAssigned'
      }
    }
 

Шаг 2. Создайте хранилище ключей без политик доступа

 resource kv 'Microsoft.KeyVault/vaults@2019-09-01' = {
  name: keyVaultName
  location: location
  properties:{
    sku:{
      family: 'A'
      name: 'standard'
    }
    tenantId: tenantId
    enabledForTemplateDeployment: true
    accessPolicies:[
    ]
  }
}
 

Шаг 3. Создайте новую политику доступа и ссылку на управляемую идентификацию веб-приложений

 resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = {
  name: '${kv.name}/add'
  properties: {
      accessPolicies: [
          {
              tenantId: tenantId
              objectId: webSite.identity.principalId
              permissions: {
                keys: [
                  'get'
                ]
                secrets: [
                  'list'
                  'get'
                ]
              }
          }
      ]
  }
}
 

Шаг 4. Обновите раздел конфигурации приложения Webb

 resource webSiteConnectionStrings 'Microsoft.Web/sites/config@2020-06-01' = {
  name: '${webSite.name}/connectionstrings'
  properties: {
    DefaultConnection: {
      value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
      type: 'SQLAzure'
    }
  }
}
 

Ответ №2:

Одним из решений может быть использование идентификатора Назначенного пользователя вместо назначенного системой. Затем вы развернете следующее:

  1. Развертывание идентификатора, назначенного пользователю
  2. Хранилище ключей и назначение разрешений для удостоверения, присвоенного пользователю
  3. Развертывание веб-приложения с присвоенным пользователем идентификатором и секретами чтения / записи

Назначенный пользователь не зависит от ресурсов, и поэтому вы избегаете проблем с курицей и яйцом.

Еще:

Ответ №3:

Вам следует разделить три части вашего развертывания на отдельные ресурсы:

  1. Разверните хранилище ключей — без каких-либо политик доступа!
  2. Разверните службу приложений — с назначенным системой идентификатором, но без настроек приложения
  3. Разверните политику доступа к хранилищу ключей для MSI
  4. Разверните настройки приложения