Перейти к основному содержимому

Авторизация в GitHub Actions через Workload Identities

· 5 мин. чтения

В старых примерах использования экшенов, я для авторизации предлагал складывать в секреты авторизованный ключ сервисного аккаунта. На тот момент это было единственным способом авторизоваться. Однако в IAM появился новый способ. Я дописал экшены. А теперь давайте разберем, как работает это механизм, как его настроить и как использовать.

Принцип работы

Workload Identities Federation позволяет вам обмениваться токены выпущенные другими провайдерами идентификации, такими как GitHub, на IAM-токены. Это позволяет вам использовать IAM для авторизации в Yandex Cloud без необходимости хранить секреты или ключи доступа в вашем репозитории. Вместо этого вы можете использовать временные токены, которые выдаются GitHub при каждом запуске вашего экшена. Это позволяет вам безопасно управлять доступом к ресурсам Yandex Cloud, не беспокоясь о компрометации ваших секретов.

Настройка

Для начала вам нужно создать сервисный аккаунт, который будет использоваться для авторизации в Yandex Cloud. Затем вам нужно создать федерацию сервисных аккаунтов, которая будет использоваться для обмена токенами между GitHub и Yandex Cloud.

Создание федерации сервисных аккаунтов

  • Значение Issuer (iss) https://token.actions.githubusercontent.com
  • Допустимые значения Audience (aud) — ваш адрес на GitHub. У меня это https://github.com/nikolaymatrosov
  • Адрес JWKS https://token.actions.githubusercontent.com/.well-known/jwks

Теперь нужно на странице «Привязанные сервисные аккаунты» привязать сервисный аккаунт к федерации.

Типы Subject в GitHub

В зависимости от того, как был запущен ваш экшен, в токене будет разное значение Subject (sub). В примерах ниже %user% и %repo% — это ваш логин и репозиторий на GitHub.

Если в job'е указано значение environment, то в sub токена будет указано значение repo:%user%/%repo%:environment:%environment%. Таким образом вы можете гибко управлять доступом на уровне job'ов. Например, если вы хотите, чтобы job с именем build имел доступ к сервисному аккаунту, а job с именем deploy не имел, то вы можете указать в workflow следующее:

jobs:
build:
environment: build
runs-on: ubuntu-latest
steps:
- name: Build
run: echo "Building..."
deploy:
environment: deploy
runs-on: ubuntu-latest
steps:
- name: Deploy
run: echo "Deploying..."

В Облаке вы указываете значение Subject (sub) как repo:%user%/%repo%:environment:build, чтобы разрешить доступ к сервисному аккаунту только для job'а build. Таким образом, job deploy не сможет получить доступ к сервисному аккаунту.

Если же в job'е не указано значение environment, но workflow запускается по событию pull_request, то в sub токена будет указано значение repo:%user%/%repo%:pull_request. Таким образом вы можете ограничить доступ к сервисному аккаунту только для pull request'ов.

Если оба условия выше не подходят, то в sub токена будет указано значение repo:%user%/%repo%:ref:refs/heads/%branch%, что означает, что токен будет действителен для запуска на ветке %branch% в репозитории %user%/%repo%.

Подробнее можно почитать в документации

Привязка сервисного аккаунта к федерации

Для использования в примере ниже я привязал сервисный аккаунт с указанием environment.

Использование

Теперь вы можете использовать этот сервисный аккаунт в своих экшенах. Для этого вам нужно указать ID сервисного аккаунта в вашем экшене. Например, если вы используете экшен yc-actions/yc-coi-deploy, то вам нужно указать ID сервисного аккаунта в параметре yc-sa-id:

name: Push To Yandex Cloud CR

permissions:
id-token: write # Нужно для получения JWT токена

# Описание, когда запускать этот workflow
on:
push:
branches: [ main ]
release:
types: [ created ]

# Позволяет запускать workflow вручную
workflow_dispatch:

jobs:
# Этот workflow содержит две задачи, называемые "build" и "deploy"
build:
# Тип runner, на котором будет выполняться задача
runs-on: ubuntu-latest
# Указываем environment, чтобы сформировать токен с нужным Subject (sub)
environment: Production
# Шаги представляют собой последовательность этапов, которые будут выполнены в рамках задачи
steps:
# Важно первым шагом выполнить checkout, чтобы получить доступ к коду
- uses: actions/checkout@v4

# Теперь мы можем используя ID сервисного аккаунта, который мы создали ранее, выпустить IAM токен для него,
# путем обмена GitHub токена на IAM токен.
- name: Get Yandex Cloud IAM token
id: get-iam-token
uses: docker://ghcr.io/yc-actions/yc-iam-token-fed:1.0.0
with:
yc-sa-id: ajep6pdp79imlp76gidn

# Используем полученный IAM токен для авторизации в Yandex Cloud Container Registry
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: cr.yandex
username: iam
password: ${{ steps.get-iam-token.outputs.token }}

# После того как мы авторизовались в Yandex Cloud Container Registry, мы можем собрать и запушить образ
- name: Build, tag, and push image to Yandex Cloud Container Registry
env:
CR_REGISTRY: crpk28lsfu91rns28316
CR_REPOSITORY: yc-coi-github-action
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG .
docker push cr.yandex/$CR_REGISTRY/$CR_REPOSITORY:$IMAGE_TAG
# Вторая задача, которая будет выполняться после завершения первой
# Задача "deploy" зависит от задачи "build"
deploy:
needs:
- build
runs-on: ubuntu-latest
# Указываем environment, чтобы сформировать токен с нужным Subject (sub)
environment: Production
steps:
# В новом job'е нам нужно снова выполнить checkout, чтобы получить доступ к коду
- uses: actions/checkout@v4

# Теперь мы можем использовать полученный IAM токен для авторизации в Yandex Cloud Container Registry
# передав его в `yc-iam-token`. Или же передав `yc-sa-id`, как в примере ниже, снова обменять GitHub токен на IAM токен.
- name: Deploy COI VM
id: deploy-coi
uses: yc-actions/yc-coi-deploy@v3
env:
IMAGE_URL: cr.yandex/crpk28lsfu91rns28316/yc-coi-github-action:${{ github.sha }}
SSH_KEY: ${{ secrets.SSH_KEY }}
with:
yc-sa-id: ajep6pdp79imlp76gidn
folder-id: b1gbmkj63hnlfu8877sn
vm-name: yc-action-demo
vm-service-account-id: ajepa75j7t2ki57l5i9m
vm-cores: 2
vm-memory: 2 GB
vm-core-fraction: 100
vm-zone-id: ru-central1-a
vm-subnet-id: e9bobc1ueq7aue8csi6m
vm-disk-type: network-hdd
vm-disk-size: 64GB
user-data-path: './user-data.yaml'
docker-compose-path: './docker-compose.yaml'

Отдельно стоит отметить, что чтобы в экшене был доступ GitHub токену, нужно указать permissions как на строках 3 и 4.

Полный пример можно найти в репозитории.