Развертывание учетных данных AWS в секреты GitHub


Цель

Развертывание произвольных проектов с использованием AWS CDK из действия GitHub.

GitHub рекомендует использовать OIDC для обеспечения связи между действиями GitHub и AWS. Но это не было бы приключением, если бы инструкции работали, не так ли? Моя компания заблокировала создание провайдеров AWS OpenID, поэтому нам нужен новый способ обойти эту проблему.

Создайте функцию Lambda для ротации учетных данных

GH Personal Access Token

Первое, что вам понадобится, — это персональный токен доступа с GitHub. Мы собираемся сохранить этот PAT как секрет в AWS. Это предполагает, что вы сохранили свой секрет в виде пары ключ-значение, которая выглядит следующим образом

{
  "token": "thisismytokentherearemanylikeitbutthisoneismine"
}
Войти в полноэкранный режим Выход из полноэкранного режима
const SECRET_NAME = "my-github-pat";

export async function getPersonalAccessToken(): Promise<string> {
  const secretsManager = new AWS.SecretsManager();
  const value = await secretsManager.getSecretValue({ SecretId: SECRET_NAME }).promise();
  const obj = JSON.parse(value || "{}");
  return obj.token;
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Принять роль

Принятие роли позволит нам сгенерировать временные учетные данные для этой роли.

async function assumeGitHubRole() {
  const sts = new AWS.STS();

  const assumeRoleParams: AWS.STS.AssumeRoleRequest = {
    RoleArn: process.env.ROLE_ARN,
    RoleSessionName: process.env.ROLE_SESSION_NAME,
  };

  return await sts.assumeRole(assumeRoleParams).promise();
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Сохранить секреты на GitHub

Секреты должны быть зашифрованы с помощью открытого ключа репозитория. Мы используем функцию libsodium crypto_box_seal для шифрования значений.

interface SealFunction {
  (message: Uint8Array, publicKey: Uint8Array): Uint8Array;
}

function encryptMessage(sealer: SealFunction, message: string, publicKey: Buffer): string {
  const messageBuffer = Buffer.from(message);
  const encryptedMessage = sealer(messageBuffer, publicKey);
  return Buffer.from(encryptedMessage).toString("base64");
}
Вход в полноэкранный режим Выход из полноэкранного режима

Обновление учетных данных

Теперь у вас есть все необходимое для обновления учетных данных в вашем репозитории GitHub.

export async function updateCredentials(): Promise<void> {
  try {
    const token = await getPersonalAccessToken();

    const repos = await request("GET /orgs/{org}/teams/{team_slug}/repos", {
      org: "my-org",
      team_slug: "my-team",
      headers: {
        authorization: `token ${token}`,
      },
    });

    const assumeRole = await assumeGitHubRole();

    // Ensure that the libsodium library has loaded and is ready to be called.
    await sodium.ready;

    // Loop through each repository, updating secrets as you go.
    const promises = [] as Promise<unknown>[];
    for (const repo of repos.data) {
      // Get the public key for the repo.
      const publicKeyResponse = await request("GET /repos/{owner}/{repo}/actions/secrets/public-key", {
        owner: "my-org",
        repo: repo.name,
        headers: {
          authorization: `token ${token}`,
        }
      };

      const publicKey = Buffer.from(publicKeyResponse.data.key, "base64");

      const encryptedAccessKeyId = encryptMessage(sodium.crypto_box_seal, assumeRole.Credentials.AccessKeyId, publicKey);
      const encryptedSecretAccessKey = encryptMessage(sodium.crypto_box_seal, assumeRole.Credentials.SecretAccessKey, publicKey);
      const encryptedSessionToken = encryptMessage(sodium.crypto_box_seal, assumeRole.Credentials.SessionToken, publicKey);

      promises.push(updateRepositorySecret(repo.name, "AWS_ACCESS_KEY_ID", getPublicKeyResponse.data.key_id, encryptedAccessKeyId));
      promises.push(updateRepositorySecret(repo.name, "AWS_SECRET_ACCESS_KEY", getPublicKeyResponse.data.key_id, encryptedSecretAccessKey));
      promises.push(updateRepositorySecret(repo.name, "AWS_SESSION_TOKEN", getPublicKeyResponse.data.key_id, encryptedSessionToken));
    }

    await Promise.all(promises);
    process.exit(0);
  } catch (error) {
    console.error("Error updating credentials:", JSON.stringify(error));
    process.exit(1);
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Продолжение следует…

Оцените статью
Procodings.ru
Добавить комментарий