Bitbucket Pipelinesでもアクセスキー無しにOIDCでTerraformを実行する
この記事は、下記アドベントカレンダー5日目の記事です。
- 【初心者優先枠】corp-engr 情シスSlack(コーポレートエンジニア x 情シス)#2 Advent Calendar 2022 - Adventar
- リアズ Advent Calendar 2022 - Adventar
BitbucketでもOpenID Connectを使ってIAMアクセスキー無しにAWSへデプロイ出来ます
公式ドキュメント
- Bitbucket Pipelines OpenID Connect を使用して AWS にデプロイする | Bitbucket Cloud | Atlassian サポート
- OIDC を使用して Pipelines とリソース サーバーを統合する | Bitbucket Cloud | Atlassian サポート
上記公式を見たら終わる話なんですが、Terraform文脈でBitbucket PipelinesもOIDC対応していることに言及されている記事が少なかったので書いてみます。
世の中GitHub Actionsの記事ばっかりですがBitbucket派もいるんや!😭
なぜOIDCを使いたいかというと、TerraformでAWSの様々なリソースを作成することから AdministratorAccess
など強力な権限を渡しがちです。そんな絶大な権限をもったアクセスキー/シークレットキーなど正直管理したくありません。ローテーションも面倒なのでしたくないです。OIDCを使うとアクセスキー無しに一時的な認証情報でAWSにアクセス出来るのでよりセキュア運用できます。Terraform Cloudも検討したのですがOIDCが使えないという点で断念しました。信用できないというよりは、管理出来る自信が無いという理由です。
1. Bitbucketリポジトリから情報を取得する
Repository settings -> OpenID Connect
リポジトリ設定から「OpenID Connect」を選んで Identity provider URL
と Audience
をメモします。
2. AWSのIAMにてIDプロバイダーを設定する
IAM -> ID プロバイダ
AWSマネジメントコンソールのIAMにて「ID プロバイダ」を選択し、「プロバイダを追加」を押します。「プロバイダのURL」欄にさきほどの Identity provider URL
を貼り付けて「サムプリントを取得」を押します。
「対象者」欄にさきほどの Audience
を貼り付けて「プロバイダを追加」を押します。
3. AWSのロールを割り当てる
作成されたIDプロバイダをクリックして詳細を開き、右上の「ロールの割り当て」を押して「ロールを作成」を押す。「ウェブアイデンティティ」を選び「アイデンティティプロバイダー」でさきほど設定したBitbucketを選択する。Audienceも同様に選択する。許可する権限は AdministratorAccess
など適宜設定します。
作成されたロールのARNをメモします。
IAM -> ロール -> ロールを作成
4. Bitbucket Pipelinesを設定する
Bitbucketリポジトリの設定「Repository variables」にてリポジトリ変数設定出来ますので、AWS IAMロール名の変数を作成します。
変数名(例) | 値(例) |
---|---|
AWS_ROLE_NAME | arn:aws:iam::1234567890:role/your-role-name |
次にリポジトリルートに bitbucket-pipelines.yml
を設定して挙動を設定します
# Terraform docs
# https://developer.hashicorp.com/terraform/docs
# Bitbucket Pipelines OpenID Connect guide
# https://support.atlassian.com/ja/bitbucket-cloud/docs/deploy-on-aws-using-bitbucket-pipelines-openid-connect/
image: hashicorp/terraform:1.3.4
pipelines:
pull-requests:
'**': #this runs as default for any branch not elsewhere defined
- parallel:
- step:
name: Terraform Plan
oidc: true
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/stg/exampleA
- terraform init -input=false
- terraform validate
- terraform plan -input=false
- step:
name: tflint
image: ghcr.io/terraform-linters/tflint-bundle:latest
oidc: true
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/stg/exampleA
- tflint --init --config .tflint.hcl
- tflint
- step:
name: tfsec
image: aquasec/tfsec-ci:latest
oidc: true
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/stg/exampleA
- tfsec
custom:
staging:
- step:
name: Terraform Plan
oidc: true
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/stg/exampleA
- terraform init -input=false
- terraform validate
- terraform plan -input=false
- step:
name: Deploy to Staging
oidc: true
deployment: staging
trigger: manual
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/stg/exampleA
- terraform init -input=false
- terraform apply -input=false --auto-approve
production:
- step:
name: Terraform Plan
oidc: true
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/prod/exampleA
- terraform init -input=false
- terraform validate
- terraform plan -input=false
- step:
name: Deploy to Production
oidc: true
deployment: production
trigger: manual
script:
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- cd $BITBUCKET_CLONE_DIR/prod/exampleA
- terraform init -input=false
- terraform apply -input=false --auto-approve
上記はTerraformのモジュールを使ってステージング環境とプロダクション環境にapply(デプロイ)する時の設定です。
OIDC: true
でOIDC有効にします。
- export AWS_ROLE_ARN=$OIDC_ROLE_ARN
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
の3行ではさきほどの変数も使いAWSロールを引き受けワンタイムな認証情報をセットします。
あとはお好みですが、プルリクエスト時にはterraform planやtflint/tfsecなどツールを使いチェックを行います。Mainブランチにコミットしたら自動で本番applyする設定をする場合もありますが、ここでは custom
セクションを使って手動でパイプラインを動かすようにしています。
5. Pipelinesを動かしてTerraformが動くことを確認する
プルリクエストを作ったり、手動でステージング環境へapplyして確認します。
TerraformというかIaCの好きなところ
OIDCを使うメリットは冒頭に書きましたが、せっかくなのでTerraformとCI/CDの組み合わせで好きなところも書いておきます。
各ユーザーに強い権限渡さなくて良くなる
これはCI/CD(ここではBitbucket Pipelines)に権限設定しておけば、Terraformコード書いてコミットしたらAWSリソースが作れるので個々のユーザーに権限を必要に応じて追加などしなくても済みます。むしろ運用に乗せる上で、マネジメントコンソールで手動で変更できないようにしていきます。
プルリクエストが申請/承認フローに出来る
IAMユーザーやロールは勝手に作られては困るものもプルリクエストの形でワークフローが作れて統制が出来ます。
リソースの命名規則のチェックも出来ます。
コミットログやCI/CDのログが変更履歴になる
これはAWS側にもCloudtrailやConfigもありますが、個人的にはリポジトリ側のログの方が誰がいつ一連のリソースを変更したか見やすくて便利です。
AWS側のログは理由/背景までは分かりませんが、コミットログにWhyを書くルールにしていると後から把握しやすいですね。チケット番号書くともっといいですね。Bitbucketの場合はJIRAの課題番号を書くと自動でリンクされます。
tfsecでセキュリティ的に良くない設定も作る前に指摘してくれる
暗号化していない、セキュリティグループが甘い、ログ保存設定していない等漏れている場合はCIで指摘してくれるので、いちいち指摘しなくて良いですし角も立たないので便利です。
というわけでTerraform + OIDCはクレデンシャル管理としてもAWS統制の上でも良いぞ、Bitbucketでも十分使えるぞという話でした。
そのうち書く
- 1passwordとGoogle WorkspaceをSCIM Bridgeを立てて自動プロビジョニングした話
- Mailgunで迷惑メール報告を受けたらSlackに通知している話
Discussion