Github Actionsで使用するAWSのクレデンシャルをGithub Appsで安全にキーローテーションする
プライベートのAWSアカウントをGithub Actions上のTerraformで管理するようにしました。 その過程でIAMユーザーのクレデンシャルをGithubのSecretsに保存しつつ、安全にキーローテションする方法をGithub Actionsで実装しました。
Github ActionsでAWSのキーローテーションをする場合はRotate AWS Access Keysが有名です。
READMEでも触れられているように、Github Actionsで使える GITHUB_TOKEN
にはSecretsを更新する権限が付与されていません。Personal Access Tokenを発行してセットする方法が推奨されていますが、このトークンはアカウントがアクセス可能なすべてのリポジトリに権限を付与してしまうため安全ではありません。
今回はPersonal Access Tokenの替わりに専用のGithub Appsを作成し、特定のリポジトリだけにインストールすることで 安全にSecretsを更新できるようにしました。
Github Appsを作成する
まずはCreating a GitHub Appを参考に作成します。
- 与えるパーミッションは “Secrets” - “Read & Write” のみでOK
- このAppは自分専用なので “Only on this account” でOK
作成できたら、Appの設定画面から秘密鍵を生成し、App IDを控えます。
作成したAppはInstalling GitHub Appsを参考に、このAppで管理したいリポジトリへインストールします。
インストールできたら、リポジトリの “Settings” - “Integration” からインストール済みAppの設定画面へ移動します。この時のURL https://github.com/settings/installations/XXXXX
の XXXXX
の部分が Installation ID
になるので、これを控えます。
ここで得た値をリポジトリのSecretsへ設定します。
APP_ID
: AppのApp ID
を設定APP_INST_ID
: AppのInstallation ID
を設定APP_GITHUB_PRIV_KEY
: Appの秘密鍵を設定AWS_ACCESS_KEY_ID
: 対応するAWSの初期クレデンシャルAWS_SECRET_ACCESS_KEY
: 同上
Github Actionsを作成する
あとはいつも通り .github/workflows/aws_key_rotate.yml
としてActionsをインストールします。
例:
Github Appsの GITHUB_TOKEN
を取得する処理は拙作のnabeken/go-github-appsのGithub Actionsで処理しています。
これで、毎週月曜日の日本時間9時に自動的にキーが更新されるようになりました。
動作確認時または即時更新したい場合は actions/aws_key_rotate
ブランチにpushしてください。
2021/02/21更新:
Gistの例で kneemaa/github-action-rotate-aws-secrets@v1.0.3
に渡すAWSのクレデンシャルをIAM userからローテーション用のIAM roleに変更しました。
万が一アクションに悪意のあるコードが混入された場合、IAM userが持つ権限あるいはAssume Role先の権限への昇格を許してしまうための対策です。
ローテーション用のIAM roleの例(Terraform):
resource "aws_iam_role" "key-rotate" {
name = "example-key-rotate-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "${var.deployment_user_arn}" },
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
EOF
}
resource "aws_iam_role_policy" "iam" {
name = "key-rotation"
role = aws_iam_role.key-rotate.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"iam:CreateAccessKey",
"iam:DeleteAccessKey",
"iam:GetUser",
"iam:ListAccessKeys",
]
Effect = "Allow"
Resource = var.deployment_user_arn
},
]
})
}