Learn (Almost) Anything
КаталогСкачать курс

Автоматизация в GitHub

Security-версия OIDC: claims, subject templates, cloud trust policies, environment protection, short-lived tokens и замена статических ключей.

ruЭнциклопедия33 урока6 модулей9
GitHub actionsАвтоматизация в GitHubМодель workflowсостояние и артефактыматрицы и runtimeрелизы и environmentsПереиспользование и собственные actionsgovernance и эксплуатацияgithubactions

OIDC и секреты облаков

Security-версия OIDC: claims, subject templates, cloud trust policies, environment protection, short-lived tokens и замена статических ключей.

OIDC и секреты облаков

OIDC в GitHub Actions — это способ дать workflow доступ к облаку без долгоживущего cloud key в secrets. Вместо AWS_ACCESS_KEY_ID, AZURE_CLIENT_SECRET или JSON key от service account job запрашивает у GitHub короткоживущий OIDC JWT, отдаёт его cloud provider’у, а cloud provider проверяет claims и выдаёт временные credentials.

Это не «секрет стал безопаснее храниться». Секрета в GitHub больше нет. Security-модель переносится в cloud trust policy: какому repo, branch, environment, workflow или reusable workflow можно получить роль. Поэтому OIDC тесно связан с Permissions и GITHUB_TOKEN, Environments и protection rules, Deployment workflows и Secure use и threat model.

sequenceDiagram participant Job as GitHub Actions job participant GitHub as GitHub OIDC provider participant Cloud as Cloud provider STS/IAM participant Resource as Cloud resource Job->>GitHub: Request OIDC JWT (id-token: write) GitHub-->>Job: Signed JWT with iss/aud/sub/claims Job->>Cloud: Exchange JWT for cloud credentials Cloud->>Cloud: Validate signature, aud, sub, environment/ref Cloud-->>Job: Short-lived access token Job->>Resource: Deploy using temporary credentials
sequenceDiagram
    participant Job as GitHub Actions job
    participant GitHub as GitHub OIDC provider
    participant Cloud as Cloud provider STS/IAM
    participant Resource as Cloud resource

    Job->>GitHub: Request OIDC JWT (id-token: write)
    GitHub-->>Job: Signed JWT with iss/aud/sub/claims
    Job->>Cloud: Exchange JWT for cloud credentials
    Cloud->>Cloud: Validate signature, aud, sub, environment/ref
    Cloud-->>Job: Short-lived access token
    Job->>Resource: Deploy using temporary credentials
OIDC убирает долгоживущий cloud secret из GitHub: доступ выдаётся только после проверки claims в cloud trust policy.

Что происходит при deploy

Минимальный поток такой: job получает право id-token: write, cloud login action просит у GitHub OIDC token, cloud provider валидирует подпись и claims, затем выдаёт короткоживущий access token для конкретной роли или service account.

name: deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-24.04
    environment: production
    steps:
      - uses: actions/checkout@v6
      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-prod-deploy
          aws-region: eu-central-1
      - run: ./scripts/deploy.sh

id-token: write выглядит тревожно, но это не write-доступ к репозиторию и не доступ к облаку сам по себе. Это только разрешение job запросить OIDC JWT у GitHub. Реальный доступ появляется позже, если cloud trust policy решит: «да, этот JWT соответствует моей роли».

Claims: что именно проверяет cloud

OIDC token — это signed JWT с claims о запуске workflow. Ключевые поля:

  • iss: issuer, для GitHub Actions это https://token.actions.githubusercontent.com;
  • aud: audience, например sts.amazonaws.com для AWS или api://AzureADTokenExchange для Azure;
  • sub: subject — главная строка, по которой обычно ограничивают repo, ref или environment;
  • repository, repository_id, repository_owner, ref, workflow_ref, job_workflow_ref, environment, runner_environment и другие claims.

Практически важный вывод: policy не должна доверять всему issuer’у GitHub. GitHub — общий identity provider для огромного количества репозиториев. Если cloud role принимает любой token от token.actions.githubusercontent.com, вы построили публичную дверь. Нужны условия на aud и sub, а для больших org — ещё на reusable workflow, repository id или custom properties.

Для AWS trust policy обычно выглядит так:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:octo-org/octo-repo:environment:production"
        }
      }
    }
  ]
}

Такой sub означает: роль можно получить только job из octo-org/octo-repo, который ссылается на environment production. Если job не указывает environment: production, claim не совпадёт и AWS STS не выдаст credentials.

Почему environment важнее branch-only policy

Можно ограничить роль branch’ем:

"token.actions.githubusercontent.com:sub": "repo:octo-org/octo-repo:ref:refs/heads/main"

Это лучше, чем wildcard, но для production часто слабее, чем связка с environment. Branch говорит только «запуск пришёл из main». Environment добавляет GitHub-side gate: required reviewers, wait timer, branch/tag restrictions, environment secrets и deployment history. В результате cloud trust policy проверяет sub, а GitHub environment решает, можно ли job вообще дойти до sensitive deploy stage.

Хороший production-паттерн:

jobs:
  deploy-prod:
    if: github.ref == 'refs/heads/main'
    environment: production
    permissions:
      id-token: write
      contents: read
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v6
      - uses: azure/login@8c334a195cbb38e46038007b304988d888bf676a
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - run: az webapp deploy --name app-prod --resource-group rg-prod

В Azure при OIDC всё ещё часто хранятся client-id, tenant-id, subscription-id как GitHub secrets или vars. Это не cloud password: это идентификаторы Entra application или managed identity. Секрет, который раньше назывался AZURE_CLIENT_SECRET, больше не нужен, если настроена federated identity credential.

Subject templates и reusable workflows

В enterprise-сценариях repo-level policy быстро становится шумной: десятки сервисов, одинаковый deploy, разные environments. Здесь помогают Reusable workflows. GitHub добавляет claim job_workflow_ref, по которому cloud может проверить не только «какой repo запустил deploy», но и «через какой централизованный deploy workflow он прошёл».

Пример идеи для subject template:

repo:octo-org/payments-api:environment:production:job_workflow_ref:octo-org/platform/.github/workflows/deploy.yml@refs/heads/main

Это сильнее, чем разрешить каждому repo писать свой cloud login как угодно. Platform team может держать pinning actions, audit logging, artifact validation и deployment summary в одном reusable workflow. Команды вызывают его через workflow_call, а cloud trust policy требует именно этот путь.

Есть нюанс: cloud providers отличаются. AWS в официальной GitHub docs отмечен как provider без поддержки custom claims, поэтому там чаще приходится работать через aud и sub. Google Cloud и некоторые secret managers позволяют более гибкие attribute mappings. В любом случае порядок внедрения одинаковый: сначала настройте cloud condition, потом меняйте workflow или subject template. Иначе новые tokens начнут выпускаться в формате, который cloud ещё не принимает.

Миграция со статических ключей

Безопасная миграция не начинается с YAML. Сначала выпишите, где сейчас лежат cloud secrets: repo secrets, org secrets, environment secrets, secrets в reusable workflow, переменные CI в старой системе. Затем для каждого deploy создайте отдельную cloud role или service account с минимальными правами. Не переносите один «super deploy key» в одну «super OIDC role».

Практический порядок:

1. Создать OIDC identity provider или workload identity federation в cloud.

2. Создать роль/service account с минимальными permissions.

3. Добавить trust condition на aud и конкретный sub.

4. В GitHub job добавить permissions: id-token: write и cloud login action.

5. Привязать production job к environment с protection rules.

6. Запустить dry run или non-prod deploy.

7. Удалить старый static secret и отозвать ключ в cloud provider.

Не оставляйте старый ключ «на всякий случай» в repository secrets. Если он остаётся рабочим, реальная поверхность атаки почти не уменьшилась.

Типовые ошибки

Самая частая ошибка — слишком широкий subject:

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:octo-org/*"
}

Иногда это выглядит удобно для monorepo или org-wide deploy, но фактически любой repo в org может получить роль, если совпадёт остальная логика. Для production лучше сузить до repo + environment или repo + reusable workflow.

Вторая ошибка — давать OIDC job untrusted code. Если workflow запускается на PR из fork и затем получает cloud credentials, вы вернулись к проблеме из pull_request_target и untrusted PR, только вместо GITHUB_TOKEN теперь рискуете cloud role. Deploy с OIDC должен идти из trusted ref, после merge, release tag или manual approval.

Третья ошибка — считать OIDC заменой всех secrets. OIDC хорошо заменяет cloud access keys. Но Docker registry password, API token стороннего SaaS, signing key или private npm token могут оставаться обычными secrets, если сервис не поддерживает federation. Для них всё ещё нужны Variables, env и secrets, masking, rotation и минимальный scope.

ИнтерактивПроверка OIDC policy#int-1
  1. Что реально разрешает `permissions: id-token: write`?
  2. Какой `sub` обычно безопаснее для production deploy?
  3. Что нужно сделать со старым `AWS_SECRET_ACCESS_KEY` после успешной миграции на OIDC?
Короткая проверка: какие настройки действительно сужают cloud-доступ, а какие только выглядят безопасно.

See also

Внешние справки: GitHub Docs по OpenID Connect, OIDC reference, OIDC в AWS/Azure/GCP и OIDC with reusable workflows; AWS IAM docs по trust policy для shared OIDC providers; Microsoft Learn по Azure Login with OIDC.

Быстрое повторение #rc-1

Почему OIDC в GitHub Actions уменьшает риск по сравнению со статическим AWS_ACCESS_KEY_ID или AZURE_CLIENT_SECRET в secrets?

Показать ответ

В GitHub больше не хранится долгоживущий cloud key. Job получает короткоживущий OIDC JWT, а cloud provider выдает временные credentials только если trust policy пропускает claims.

Источники

  1. GitHub Docs — OpenID Connect
  2. GitHub Docs — OpenID Connect reference
  3. GitHub Docs — Configuring OpenID Connect in Amazon Web Services
  4. GitHub Docs — Configuring OpenID Connect in Azure
  5. GitHub Docs — Configuring OpenID Connect in Google Cloud Platform
  6. GitHub Docs — Using OpenID Connect with reusable workflows
  7. AWS IAM User Guide — Create a role for OpenID Connect federation
  8. Microsoft Learn — Use the Azure Login action with OpenID Connect