Авторизация в GitHub Actions через Workload Identities
В старых примерах использования экшенов, я для авторизации предлагал складывать в секреты авторизованный ключ сервисного аккаунта. На тот момент это было единственным способом авторизоваться. Однако в 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.
Полный пример можно найти в репозитории.