Доступ в Azure — это система, которая отвечает на один вопрос: «кто и что может делать». Хотите прочитать файл из хранилища, запустить машину, отправить сообщение в очередь — каждый раз Azure сверяется: а разрешено ли вам это.

Звучит как скучная бюрократия, но именно тут происходит большинство аварий в облаке. Чаще всего ломают не сложным взломом, а потому что кому-то выдали слишком много прав или ключ попал в открытый репозиторий. Полчаса на понимание доступа выгоднее, чем потом разбирать инцидент.

Entra ID: кто ты Azure RBAC: что можно токен управляемого удостоверения назначение роли на область

Две системы и мост между ними: Entra ID подтверждает личность и выдаёт токен, RBAC сверяет назначения ролей на области. Токен без назначения это «вошёл, но не разрешено», а назначение без токена не сработает никогда.

Обязательно

Entra ID против RBAC

В Azure доступ делится на две связанные, но разные системы, и новички их путают.

Microsoft Entra ID (бывший Azure AD) отвечает на вопрос «кто ты» — это каталог удостоверений: пользователи, группы, приложения. Через него входят люди и регистрируются программы. Он про аутентификацию — подтверждение личности.

Azure RBAC (role-based access control) отвечает на вопрос «что тебе можно делать с ресурсами». Он про авторизацию — выдачу прав. Entra ID говорит «это точно ты», а RBAC — «тебе разрешено создавать виртуальные машины в этой группе ресурсов».

Но «Entra ID — это только про личность» — упрощение, из-за которого потом долго ищут не там. Роли в Azure бывают двух видов, и это две независимые системы. Роли Entra ID управляют самим каталогом: заводить пользователей, регистрировать приложения, менять группы — сюда относится, например, «глобальный администратор». Роли Azure RBAC управляют ресурсами: Owner, Contributor, Reader и вся сервисная мелочь. Списки не пересекаются: искать администратора каталога среди ролей RBAC бесполезно, его там нет, и наоборот. Тот, у кого все права в каталоге, по умолчанию не имеет прав ни на одну виртуальную машину.

Роли и назначения

В RBAC права выдаются через пару «роль + назначение».

Роль (role definition) — набор разрешений. Есть широкие встроенные роли:

  • Owner — всё, включая раздачу прав другим;
  • Contributor — создавать и менять ресурсы, но не раздавать доступ;
  • Reader — только смотреть.

И множество узких сервисных ролей вроде «Storage Blob Data Contributor» (доступ к данным в Blob Storage) или «Key Vault Secrets User» (читать секреты). Узкие роли — то, что нужно на практике.

И тут же самая частая причина отказа у новичка. Права в Azure разделены на два слоя: управлять ресурсом и работать с тем, что внутри него. Contributor на учётную запись хранилища — это первый слой: можно поменять настройки, добавить контейнер, удалить всю учётную запись. А прочитать файл — нельзя, это второй слой, и для него нужна роль семейства Storage Blob Data. Разделение сделано нарочно: администратор инфраструктуры не обязан видеть данные клиентов. Так что «у меня же Contributor, почему AuthorizationFailed» — не баг, а ровно эта граница.

Назначение роли (role assignment) — «кому — какую роль — на каком уровне». Уровней четыре: группа управления, подписка, группа ресурсов или отдельный ресурс. Права наследуются вниз: роль на группу ресурсов действует на все ресурсы внутри неё, роль на группу управления — на все подписки под ней. Обычно доступ выдают на уровне группы ресурсов — удобная граница.

Здесь же — три вещи, которые ломают ожидания, если вы пришли из другого облака.

Права складываются. Назначений может быть сколько угодно, и действующие права — это их объединение. Дали человеку Reader на группу ресурсов, а потом Contributor на подписку — он Contributor, и узкое назначение ничего не ограничивает.

Запрета внутри роли нет. В определении роли перечислены разрешённые действия и, при необходимости, вычитаемые из них исключения — но нет строчки «запретить». Если вы ждали явного «deny», как в правилах доступа AWS, его придётся искать в другом месте: это отдельные запрещающие назначения, которые делает не человек, а механизмы управления средой. Практический вывод: сузить чьи-то права нельзя, добавив ещё одно назначение, — можно только убрать лишнее.

Права применяются не мгновенно. Между «выдал роль» и «оно заработало» проходит время: назначение расходится по системе (это может занять до получаса), а уже выданный токен доступа не переписывается — в нём права такие, какими были на момент выдачи. Отсюда классическое «выдал роль, а всё равно отказ»: чаще всего помогает выйти и войти заново или подождать, а не выдавать ещё одну роль пошире.

az role assignment create --assignee <id> \
  --role "Storage Blob Data Contributor" \
  --scope /subscriptions/<sub>/resourceGroups/my-rg

Права выдают группам, а не людям

Назначение роли конкретному человеку работает, пока людей трое. Дальше начинается то, что видно в любой организации: сотрудник пришёл — надо вспомнить, какие ему нужны права; ушёл — надо вспомнить, где они у него были; перешёл в другую команду — надо найти всё старое.

Поэтому в организациях права назначают группам Entra ID, а людей добавляют в группы:

az ad group create --display-name "orders-developers" --mail-nickname orders-developers
az role assignment create --assignee-object-id <id-группы> --assignee-principal-type Group \
  --role Contributor --scope /subscriptions/<sub>/resourceGroups/rg-orders-dev

Что это меняет на практике. Приём сотрудника становится одним действием — добавить в группы своей команды; уход — одним, убрать из групп; список прав человека виден как список его групп, а не как поиск по всем подпискам. И назначений становится в разы меньше: одна роль на группу вместо роли на каждого.

Как обычно раскладывают группы: по команде и среде (orders-developers-dev, orders-developers-prod с разными ролями), отдельно дежурные с расширенными правами, отдельно читатели для тех, кому нужно только смотреть. Членство в группах, если в компании есть кадровая система, часто назначается правилами (динамические группы) по атрибутам сотрудника — тогда даже добавление становится автоматическим.

И дисциплина, без которой это не работает: поимённые назначения запрещены как правило, а не как пожелание. Проверить легко — назначения на людей видны в списке назначений ролей, и их там не должно быть, кроме редких обоснованных исключений.

Managed identity: доступ без ключей

Главное правило: ваш код не должен носить с собой пароли и ключи. В Azure это решает управляемое удостоверение (managed identity) — учётная запись для сервиса, которой платформа сама выдаёт временные токены.

Бывает двух видов:

  • System-assigned — привязана к конкретному ресурсу (виртуальной машине, функции) и живёт вместе с ним;
  • User-assigned — отдельная сущность, которую можно навесить на несколько ресурсов.

Приложение с managed identity получает токен прямо из платформы, а библиотека Azure (SDK) находит его автоматически. В коде — ни одного секрета. Это тот же принцип, что instance profile в AWS.

Для сценариев вне Azure (например, CI, живущий не в облаке) заводят service principal — учётную запись приложения. Раньше ей всегда выдавали секрет или сертификат, и дальше начиналась знакомая морока: секрет долгоживущий, его надо где-то хранить и вовремя менять.

Сегодня для конвейеров доставки рекомендуют другой путь — федерацию удостоверений (federated credentials), она же вход без секрета вовсе. Работает так: вы один раз говорите Entra ID «доверяй токенам, которые выдаёт вот этот конвейер вот для этого репозитория», и дальше конвейер предъявляет свой собственный токен, а Entra ID меняет его на доступ к Azure. Хранить нечего, ротировать нечего, утечь нечему. Секрет у service principal остаётся вариантом для случаев, где федерация недоступна, — и тогда его держат в защищённом хранилище CI, а не в git, и регулярно меняют.

Least privilege — минимум прав

Главный принцип всей безопасности: давать ровно столько прав, сколько нужно, и ни ролью больше.

Самый частый дефект новичка — выдать сервису Contributor или Owner на всю группу ресурсов «чтобы просто заработало». Так делают, чтобы не возиться, — и именно это потом превращается в инцидент: одна утёкшая учётка открывает всю группу.

На практике least privilege — итеративный процесс: начните узко (только нужные сервисные роли), запустите, посмотрите по отказам, чего не хватает, добавьте недостающую роль. Дольше, чем выдать всё сразу, зато утёкший токен такого удостоверения открывает ровно один контейнер хранилища, а не всю подписку.

Как читать отказ

«Посмотрите по отказам» требует продолжения: где именно смотреть. В Azure есть три места, и они дают разную информацию.

Текст самой ошибки. Отказ на уровне управления ресурсами выглядит так:

AuthorizationFailed: The client 'app-orders' with object id '...' does not have
authorization to perform action 'Microsoft.App/containerApps/write' over scope
'/subscriptions/.../resourceGroups/rg-orders-prod/providers/Microsoft.App/containerApps/orders'
or the scope is invalid.

Здесь сказано всё нужное: кто обращался, какое действие не разрешено и на какой области. Дальше остаётся найти роль, в которой есть это действие, и назначить её на нужной области — а не выдавать Contributor наугад.

Журнал действий (Activity Log) — все операции уровня управления ресурсами, включая неудачные. Фильтр по состоянию «Failed» и по времени показывает, что именно пытались сделать и чем это кончилось. Это то место, куда идут, когда отказ случился не у вас, а у конвейера или у коллеги.

az monitor activity-log list --status Failed --offset 1h \
  --query "[].{time:eventTimestamp, op:operationName.value, who:caller, msg:statusMessage}" -o table

Журналы входов и доступа Entra ID — если проблема не в роли, а в самом входе: отключённая учётная запись, не пройденный второй фактор, политика условного доступа. Отказ тогда выглядит иначе, и в журнале действий его не будет вовсе.

Проверка прав заранее. Не обязательно ждать отказа: у портала есть проверка доступа (Check access) для ресурса, а в командной строке — список назначений для конкретного участника:

az role assignment list --assignee <id> --all -o table       # что назначено и где
az role definition list --name "Storage Blob Data Reader" --query "[].permissions" -o json

И практический порядок разбора, который работает почти всегда: прочитать текст отказа целиком → найти в нём действие → проверить, есть ли оно в назначенных ролях на нужной области → проверить, не путаете ли вы роль управления с ролью данных. Четвёртый пункт закрывает половину случаев.

Где это применяется

Доступ — это слой «кто что может делать» поверх сетевого слоя «кто до кого вообще может достучаться». Вместе они дают полноценную защиту: минимальная сеть плюс минимальные права.

Где вы столкнётесь с этим сразу:

  • Любой ваш сервис (виртуальные машины, функции, контейнеры) получает доступ к другим сервисам через managed identity.
  • Конвейеры доставки кода ходят в Azure через федерацию удостоверений (а где она недоступна — через service principal), а не через зашитые ключи.
  • Секреты и шифрование — соседняя тема: где хранить пароли, в безопасности и наблюдаемости.

Типичные ошибки новичков:

  • Класть ключи или секрет service principal в код или git вместо managed identity.
  • Выдавать Contributor/Owner широко «чтобы заработало», а потом забыть сузить.
  • Путать: аутентификация (Entra ID) и авторизация (RBAC) — это разные вещи; «вошёл» не значит «всё разрешено».
Дополнительно: при первом чтении можно пропустить

Глубже: кто может раздавать праварасширенное

Отдельный вопрос, который важнее, чем кажется: право менять права — это тоже право, и оно не должно идти вместе со всем остальным.

Встроенные роли здесь такие:

  • Owner (владелец) — всё, включая раздачу прав. Максимальный набор, и именно поэтому его выдают редко.
  • Contributor (участник) — может создавать и менять ресурсы, но не может раздавать права. Это правильная роль для команды, которая работает с ресурсами.
  • User Access Administrator (администратор доступа) — может раздавать права и не может трогать сами ресурсы.
  • Role Based Access Control Administrator — более узкая современная роль: раздача прав, но с ограничением, какие роли она может выдавать (то есть нельзя выдать владельца).

Практическая схема, которой пользуются: команда получает Contributor на свои группы ресурсов, а раздачу прав держит отдельная небольшая группа с User Access Administrator или с ограниченным администратором доступа. Владельца не выдают никому на постоянной основе — он живёт как временная роль, о которой ниже.

Зачем такое разделение: Contributor не может расширить сам себя. Человек, получивший его, не сможет выдать себе больше или раздать доступ наружу — а значит, компрометация его учётной записи ограничена тем, что он и так мог делать.

Глубже: свои роли, когда встроенных не хватаетрасширенное

Встроенных ролей много (несколько сотен), и всё равно наступает момент, когда Contributor даёт слишком много, а подходящей узкой роли нет. Тогда заводят свою роль (custom role): список разрешённых операций плюс список запрещённых плюс области, где она применима.

{
  "Name": "Orders Deploy Operator",
  "Description": "Перезапуск приложения и чтение журналов, без изменения инфраструктуры",
  "Actions": [
    "Microsoft.App/containerApps/read",
    "Microsoft.App/containerApps/restart/action",
    "Microsoft.Insights/components/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/<sub>/resourceGroups/rg-orders-prod"]
}

Что важно про свои роли. Операции называются по схеме «поставщик/тип/действие», и подсмотреть их можно в списке доступных операций (az provider operation list) — угадывать не нужно. Роль назначается на области из своего списка и не шире. И у своих ролей есть предел числа на подписку — то есть плодить их под каждый случай не стоит; обычно в компании живёт пять-десять своих ролей на весь контур.

Правило выбора: сначала ищут подходящую встроенную (их правда много и они обновляются), и только если ничего не подходит — делают свою. Своя роль — это то, что придётся поддерживать: появился новый тип ресурса, и роль надо дополнить.

Глубже: права на время, а не навсегдарасширенное

Раздел про минимум прав просит инструмент, который выдаёт расширенный доступ временно, и в Azure он есть: управление привилегированным доступом (PIM, часть Entra ID уровня P2).

Как это работает: человек не имеет роли постоянно — он имеет право её запросить. Запрос требует подтверждения (второй фактор, обоснование, иногда одобрение коллеги), после чего роль активируется на заданный срок (час, восемь часов) и потом снимается сама. Всё это попадает в журнал: кто, когда, зачем и что делал.

Что это закрывает. Права администратора у дежурного нужны раз в месяц на полчаса — а висят постоянно; временная выдача превращает постоянный риск в разовое событие с обоснованием. И ровно тот же механизм закрывает историю «выдали Owner на время разобраться и забыли снять».

Требует это отдельного уровня лицензии Entra ID, и это надо знать заранее: в подписке без него временных прав нет, и приходится обходиться дисциплиной — отдельной группой «дежурный администратор», в которую добавляют на смену и удаляют после, с журналом изменений членства.

Глубже: как приложение получает токенрасширенное

Управляемое удостоверение существует на стороне платформы, и остаётся вопрос: как им пользуется программа. Ответ короткий: через единую цепочку источников учётных данных в библиотеке Azure.

var credential = new DefaultAzureCredentialBuilder().build();

var client = new SecretClientBuilder()
        .vaultUrl("https://kv-orders-prod.vault.azure.net/")
        .credential(credential)
        .buildClient();

String password = client.getSecret("db-password").getValue();
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
	return err
}
client, err := azsecrets.NewClient("https://kv-orders-prod.vault.azure.net/", cred, nil)
if err != nil {
	return err
}
resp, err := client.GetSecret(ctx, "db-password", "", nil)
if err != nil {
	return err
}
password := *resp.Value
const { DefaultAzureCredential } = require('@azure/identity');
const { SecretClient } = require('@azure/keyvault-secrets');

const credential = new DefaultAzureCredential();
const client = new SecretClient('https://kv-orders-prod.vault.azure.net/', credential);
const { value: password } = await client.getSecret('db-password');
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://kv-orders-prod.vault.azure.net/", credential=credential)
password = client.get_secret("db-password").value

Смысл в том, что код одинаков везде. Эта цепочка по очереди пробует источники: переменные окружения, управляемое удостоверение, вход через инструмент разработчика (командная строка Azure, среда разработки). В облаке срабатывает управляемое удостоверение, на ноутбуке — ваш вход через az login. Ни ключей, ни строк подключения, ни разных сборок для разных сред.

Два момента, о которых стоит знать. У перебора источников есть цена в задержке (подробнее — в статье про подключение SDK), поэтому в боевом контуре часто указывают источник явно. И самое частое недоразумение Azure: права на управление ресурсом и права на его данные — разные. Роль Reader на хранилище объектов позволяет видеть хранилище в списке и не позволяет прочитать из него файл; для данных нужна отдельная роль уровня данных (Storage Blob Data Reader). То же с хранилищем секретов, с очередями, с базами. Симптом узнаваемый: «права выдали, а приложение получает отказ».

Коротко

  • Entra ID отвечает «кто ты», RBAC «что тебе можно с ресурсами»; роли каталога и роли ресурсов это две независимые системы, и «вошёл» не значит «разрешено».
  • Права выдаются парой роль плюс назначение на уровень: Owner, Contributor и Reader широкие, а нужны узкие сервисные роли; Contributor на хранилище не даёт читать данные внутри него.
  • Права складываются из всех назначений, явного запрета внутри роли нет, а новое назначение доезжает до получаса и не переписывает уже выданный токен.
  • Роли назначают группам Entra ID, а не людям: приём и уход сотрудника это одно действие, а поимённые назначения запрещены правилом.
  • Раздачу прав отделяют от работы с ресурсами: команде Contributor, узкой группе User Access Administrator или ограниченный администратор доступа; так утёкшая учётка не расширит сама себя.
  • Свою роль заводят, когда среди сотен встроенных нет подходящей, и поддерживают её; расширенные права выдают на время через PIM, а без него дисциплиной отдельной группы.
  • Managed identity даёт сервису временные токены без секретов, system-assigned живёт с ресурсом, user-assigned навешивается на несколько; конвейеру вне облака вместо секрета service principal делают федерацию удостоверений.
  • Минимум прав это процесс: начать узко, смотреть отказы, добавлять роль; текст отказа называет кто, какое действие и на какой области, журнал действий показывает неудачные операции, Check access проверяет заранее.

Что почитать дальше