我們可以使用 Terraform 在 AWS 上建立一個可以使用 OIDC 的 IAM Role,同時完成 GitHub 的設定

简而言之

Terraform不仅可以管理AWS等云服务,还可以管理GitHub资源。
在本文中,我们将使用Terraform在GitHub Actions中创建可以通过OpenID Connect进行Assume Role的IAM Role。
此外,我们将一并使用Terraform将创建的Role的arn注册到GitHub Actions的秘钥中。

请在中国方面进行参考。

    • https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws

 

    • https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services

https://zenn.dev/miyajan/articles/github-actions-support-openid-connect

TerraformでOpenID Providerを作成する部分はこの記事を参考に作成しました

檢証所使用的程式碼可在以下儲存庫中找到。

 

使用中的环境

    • terraform

v1.4.7

创建Terraform

在这里介绍了代码示例,但省略了变量的定义。
由于命名可以推测到一定程度,请将其作为传递信息的参考。
在上面的链接仓库中,包含了包含变量定义的代码,请也参考那里。

这次要制作的结构

为了使用GitHub Actions的Environments功能,我们将使用现有的GitHub存储库。
我们将创建Environments以便使用GitHub Actions的Environments功能。
我们将在其中作为secret加入IAM角色的arn,并作为变量加入地区。

我只将IAM角色附加到GitHub Actions中,而无需任何策略,只能通过Assume Role。

使用的服务提供商

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    github = {
      source  = "integrations/github"
      version = "~> 5.0"
    }
  }
}

为了创建IAM角色,我们将使用AWS的Provider以及GitHub的Provider来创建GitHub资源。
由于GitHub的Provider中包含了一些过时的选项,因此请确保指定正确的Provider来使用source。

GitHub提供商的文档在这里。
https://registry.terraform.io/providers/integrations/github/latest/docs

我们已按照各个供应商的设定进行了如下操作。

provider "aws" {
  region = var.aws_region
}

provider "github" {
  token = var.gh_token
  owner = var.gh_owner
}

使用GitHub CLI所提供的Token可以从GitHub CLI获取。
通过在执行时使用”terraform plan -var “gh_token=$(gh auth token)”进行插入。
虽然文档中表示owner是可选的,但由于没有设置owner的值导致陷入了困境,因此需要指定。
将要操作的仓库的所有者填入。
例如,对于k-kojima-yumemi/potential-palm-tree仓库,”k-kojima-yumemi”部分就是owner。

当将某功能设为模块时,需要注意的事项

在将内容转化为模块以实现再利用性时,可能会遇到Provider设置的内容并未反映的问题。

如果在模块中未指定Provider并引用或创建GitHub资源,则会使用已弃用的hashicorp/github。
在这种情况下,首次设置的Provider内容将被视为另一个Provider的信息,因此在模块内设置的项目将不会被反映。
因此,通过在定义模块资源的文件中指定以下元素,可以解决Provider错位的问题。

terraform {
  required_providers {
    github = {
      source  = "integrations/github"
      version = "~> 5.0"
    }
  }
}

当执行terraform providers时,如果出现github的Provider已被弃用的消息,则可能发生了这种现象。

生成Environments

可以使用github_repository_environment资源来创建Environments。

resource "github_repository_environment" "gh_env" {
  repository = var.gh_repo_name
  environment = var.gh_repo_env_name
}

存储库必须是不包含所有者的字符串。
看起来这里的字符串和提供者端持有的所有者被连接成/{owner}/{repository}的形式来调用API。
环境应该是环境的名称,并且需要与后续指定的IAM角色匹配。

创建AWS的ID提供者

这个ID提供者是根据https://zenn.dev/miyajan/articles/github-actions-support-openid-connect进行创建的。

# Ref: https://zenn.dev/miyajan/articles/github-actions-support-openid-connect

data "http" "github_actions_openid_configuration" {
  url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
}

data "tls_certificate" "github_actions" {
  url = jsondecode(data.http.github_actions_openid_configuration.response_body).jwks_uri
}

resource "aws_iam_openid_connect_provider" "github_actions" {
  url = "https://token.actions.githubusercontent.com"

  client_id_list = ["sts.amazonaws.com"]

  thumbprint_list = data.tls_certificate.github_actions.certificates[*].sha1_fingerprint
}

output "id_provider_arn" {
  value = aws_iam_openid_connect_provider.github_actions.arn
}

我将ID Provider的arn添加到Output中,以便在创建IAM Role时使用。

创建IAM角色

使用先前创建的ID提供程序进行认证,创建IAM角色。
另外,限定认证来源为创建的GitHub环境。

resource "aws_iam_role" "iam" {
  name = var.role_name
  assume_role_policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Principal" : {
          "Federated" : var.id_provider_arn
        },
        "Action" : "sts:AssumeRoleWithWebIdentity",
        "Condition" : {
          "StringEquals" : {
            "token.actions.githubusercontent.com:aud" : "sts.amazonaws.com",
            "token.actions.githubusercontent.com:sub" : "repo:${var.repo_full_name}:environment:${var.repo_env_name}"
          },
        }
      }
    ]
  })
}

在token.actions.githubusercontent.com:sub上,通过指定存储库和环境来进行设置。
这里的存储库名称需要指定为{所有者}/{仓库}的格式。

在环境中注册秘密和变量

我将IAM角色的ARN和AWS区域注册到GitHub的Environment中。
在创建环境机密时,使用github_actions_environment_secret。
在变量中使用github_actions_environment_variable。

resource "github_actions_environment_secret" "gh_env_secret_arn" {
  repository = var.gh_repo_name
  environment = var.gh_repo_env_name
  secret_name = var.gh_repo_env_secret_name
  # Actually, arn is not sensitive value so just use plain text.
  plaintext_value = var.aws_iam_role_arn
}

resource "github_actions_environment_variable" "gh_env_variable_region" {
  repository = var.gh_repo_name
  environment = var.gh_repo_env_name
  variable_name = var.gh_repo_env_variable_name
  value = var.aws_region
}

正如文件中所述,秘密内容本应进行加密。
在这种情况下,我们将IAM角色的ARN写入Secret,并选择了指定明文值,因为其保密性较低。
以明文值方式输入的值将以明文形式保存在terraform.tfstate文件中。
在实际处理高度保密信息时,请考虑加密或使用其他方法来输入值。

计划

我将介绍在此前内容中创建的Terraform中执行terraform plan的示例。
这是针对用于验证的GitHub仓库进行的操作。
由于环境中已存在ID提供程序,因此不会再创建ID提供程序。
此外,ID提供程序的arn已被隐藏。

module.gh_repo.data.github_repository.repo: Reading...
module.gh_repo.data.github_repository.repo: Read complete after 0s [id=potential-palm-tree]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.aws_iam.aws_iam_role.iam will be created
  + resource "aws_iam_role" "iam" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRoleWithWebIdentity"
                      + Condition = {
                          + StringEquals = {
                              + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
                              + "token.actions.githubusercontent.com:sub" = "repo:k-kojima-yumemi/potential-palm-tree:environment:env1"
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
                          + Federated = "<Arn of ID Provider>"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + description           = "Test role created in terraform"
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "k_kojima_terraform_with_github"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)
    }

  # module.gh_env.github_repository_environment.gh_env will be created
  + resource "github_repository_environment" "gh_env" {
      + environment = "env1"
      + id          = (known after apply)
      + repository  = "potential-palm-tree"
    }

  # module.gh_env_values.github_actions_environment_secret.gh_env_secret_arn will be created
  + resource "github_actions_environment_secret" "gh_env_secret_arn" {
      + created_at      = (known after apply)
      + environment     = "env1"
      + id              = (known after apply)
      + plaintext_value = (sensitive value)
      + repository      = "potential-palm-tree"
      + secret_name     = "test_secret"
      + updated_at      = (known after apply)
    }

  # module.gh_env_values.github_actions_environment_variable.gh_env_variable_region will be created
  + resource "github_actions_environment_variable" "gh_env_variable_region" {
      + created_at    = (known after apply)
      + environment   = "env1"
      + id            = (known after apply)
      + repository    = "potential-palm-tree"
      + updated_at    = (known after apply)
      + value         = "ap-northeast-1"
      + variable_name = "AWS_REGION"
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + gh_repo_name = "k-kojima-yumemi/potential-palm-tree"

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

申请后将创建这些资源。

Environments.png

执行GitHub Actions

为了验证,我在下面定义了工作流程。

name: Test AWS

on:
  push:

jobs:
  access:
    runs-on: ubuntu-latest
    environment: env1
    permissions:
      id-token: write
    steps:
      - name: access
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-region: ${{vars.AWS_REGION}}
          role-to-assume: ${{secrets.TEST_SECRET}}

这是一个在现有环境中只需要进行Assume Role操作的工作流程。可以确认它能够正常运行。

Workflow.png
bannerAds