TerraformでGitHub Actions OIDC with AWSを定義し、EKSに安全にアクセスする
LAPRAS株式会社でエンジニアをしてます@yktakaha4ともうします🐠
この記事は、LAPRAS Advent Calendar 2021 2日目の記事です
先日、GitHub ActionsがOIDCに対応しましたが、ネットを探すとベータとして公開されていた時期の情報が残っていて若干動かしづらい部分があったため、備忘メモとして残すこととしました✍
なににアクセスするかについては丁度いいネタが思いつかなかったので、
直近でTerraformでサッと使える検証用EKS環境を作ったという記事を書いていたため、
この時作ったEKSクラスタにGitHub Actionsからアクセスし、マニフェストを適用するワークフローを書いてみました
KubernetesのデプロイについてはGitOpsでおこなっている方も多いかもしれませんが、
kubectl
に限らず、CI/CDの過程でなにがしかのコマンドをクラスタに対して打ちたい時に使えるはず…
作ったもの
GitHubにて公開しています
試し方は上記リンクのUsageに記載していますのでご確認ください🍨
うまく動かせると、Sock Shop をEKS上で立ち上げられます👢
ポイント
肝心のOIDC関連の設定を抜粋します
2021/11/21時点ではこの設定で動きました
まずはOIDCプロバイダーを定義します
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"]
}
これを参照するロールを定義します
github_repository_name
については、 yktakaha4/study-eks-github-oidc
などの、GitHubのリポジトリ名が入るようにしてください
resource "aws_iam_role" "github_actions" {
name = "${var.resource_prefix}-github-actions"
path = "/"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "sts:AssumeRoleWithWebIdentity"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = [
"repo:${var.github_repository_name}:*"
]
}
}
}]
})
}
このロールに対して、適切な権限を付与します
今回はEKSクラスタに対するアクセスするために必要な update-kubeconfigが実行できれば充分なので、
作成したEKSクラスタに対して eks:DescribeCluster
が許可されるようにします
resource "aws_iam_policy" "github_actions" {
name = "${var.resource_prefix}-github-actions"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "eks:DescribeCluster"
Resource = module.eks.cluster_arn
}]
})
}
resource "aws_iam_policy_attachment" "github_actions" {
name = "${var.resource_prefix}-github-actions"
roles = [
aws_iam_role.github_actions.name,
]
policy_arn = aws_iam_policy.github_actions.arn
}
ここからはEKS側の設定になります
aws-auth ConfigMapに対して作成したIAMロールを紐付けます
今回はAWS EKS moduleを使ってEKSクラスタを作成していますが、同等のConfigMapを適用できれば別の方法でもよいものとおもいます
admin-group
については、K8s側のロールに対応させる必要があるので覚えておきます
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "17.0.0"
cluster_name = var.resource_prefix
cluster_version = "1.21"
map_roles = [{
rolearn = aws_iam_role.github_actions.arn
username = "github-actions"
groups = ["admin-group"]
}]
# 略
}
マニフェスト側では、RoleとRoleBindingを作成します
今回は、 sock-shop
ネームスペース上でのみ任意の操作を実行できるようにしました
Deploymentのrolloutのみ許可したい…など、ケースに応じて権限を限定できるとよいものとおもいます
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: sock-shop
name: sock-shop-admin
rules:
- apiGroups:
- "*"
resources:
- "*"
verbs:
- "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: sock-shop
name: admin-group
subjects:
- kind: Group
name: admin-group
namespace: sock-shop
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: sock-shop-admin
最後に、GitHub Actions側の設定になります
まずは、PR作成時に kubectl diff
の実施&結果のコメントを投稿するようにしました
注意点としては、configure-aws-credentialsが、確認時だとv1タグでは動かなかったので、
差し当たって記載時点の最新のリビジョン fcd8bb1
で固定することとしました
記事の本旨からはそれますが、コマンドの実行結果をいい感じに投稿するためには色々おまじないが必要かつちょいちょいdeprecatedになってるので、今回書いている内容はそこそこ新しい書き方になってるはず…
必要な環境変数はIAMロールのARNとEKSクラスタ名のみです
AWSのアクセスキーやGitHubのPATは不要です
on: pull_request
name: Check
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
check:
runs-on: ubuntu-20.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: aws-actions/configure-aws-credentials@fcd8bb1e0a3c9d2a0687615ee31d34d8aea18a96
with:
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_NAME }}
- run: |
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.22.0/bin/linux/amd64/kubectl
chmod +x ./kubectl
./kubectl version
- run: ./kubectl diff -f ./manifests/sock-shop.yml 2>&1 | tee diff.log
- run: |
details="$(cat diff.log)"
details="${details//'%'/'%25'}"
details="${details//$'\n'/'%0A'}"
details="${details//$'\r'/'%0D'}"
echo "::set-output name=details::$details"
id: diff_details
- uses: actions/github-script@v5
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `### kubectl diff result\n\n<details>\n\n\`\`\`\n${process.env.DETAILS}\n\`\`\`\n\n</details>`
})
env:
DETAILS: "${{ steps.diff_details.outputs.details }}"
また、mainへのマージ時には kubectl apply
します
こちらはPR作成時とほとんど同じなので、あまり言うことはないです
on:
push:
branches:
- main
workflow_dispatch:
name: Release
permissions:
id-token: write
contents: read
jobs:
release:
runs-on: ubuntu-20.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: aws-actions/configure-aws-credentials@fcd8bb1e0a3c9d2a0687615ee31d34d8aea18a96
with:
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER_NAME }}
- run: |
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.22.0/bin/linux/amd64/kubectl
chmod +x ./kubectl
./kubectl version
- run: ./kubectl apply -f ./manifests/sock-shop.yml
一応、権限が適切に効いているか確認してみます
例えば別のnamespaceに何かを作ろうとすると、権限がないので失敗します
リポジトリ名を変更すると、AWSへの接続自体が拒否されます
よさそう👞
ほっとくとお金がかかるので、確認し終わったら確実に削除しましょう
おわりに
明日は同じくエンジニアの@chanmoroさんです
お楽しみに!
Discussion