GitHub AppsとAWS Secrets Managerを連携してTerraformのGitHub Providerをセキュアにする

# はじめに
TerraformのGitHubプロバイダーには、Personal Access Token(PAT)を利用する方法と、GitHub AppsのJWTを使う方法の2通りがある。

https://registry.terraform.io/providers/integrations/github/latest

PATにも、Classic版とFine-grained版があり、Classic版ではトークン期限を無制限にできるものの、将来はdeprecatedになる可能性がある。また、Fine-grained版はローテーションを前提にしているようで、無制限の設定はできない。セキュリティを考慮すると、定期的なローテーションをすることが重要であるため、思想としては正しいが、自動でローテートするためのAPIが存在しないように見える[^1]。

ということで、定期的なローテーションが容易にできる仕組みを備えている、GitHub Appsの設定を今回はしていく。また、設定をするにあたり、秘密鍵を保持する必要があるため、ここはAWS Secrets Managerを使ってローカルで管理をしないようにする。

なお、PATを利用したアクセス方法については、以下の記事に記載している。

:::note warn
なお、GitHub AppsのJWTの方式の場合、権限を付与してもリポジトリの作成ができない[^2]ようであるため、該当操作がある場合は、面倒でも手動でローテーションをしよう。
:::

https://qiita.com/neruneruo/items/b802bfe9fe32f021c50f

:::note alert
今回の記事では、tfstateに秘密鍵が平文で書かれてしまうため、AWS ProviderのバックエンドをS3にして、暗号化設定をして厳重にパーミッション管理をすることを強く推奨する。
:::

[^1]: 方法をご存知の方がいたら是非教えていただきたい。
[^2]: こちらについても方法をご存知の方がいたら是非教えていただきたい。

# GitHub Appsの設定
GitHub Appsは以下の記事の「GitHub Appsのセットアップ」の章に従って実施をしよう。

https://zenn.dev/tmknom/articles/github-apps-token#github-apps%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97

なお、権限は以下のように設定する。

|区分|権限名|権限|必要な処理|
|:---|:---|:---|:---|
|Repository permissions|Administration|Read and write|リポジトリの変更(作成はできないが変更は可能)|
|Repository permissions|Metadata|Read-only|デフォルトで付与(外せない)|

# AWS Secrets Managerの設定
上記で作成した秘密鍵は、他のHCLのファイルと同じパスにsecretkey.pemという名前で配置することを前提にしている。プレーンテキストでは、秘密鍵中の改行が消えてしまうため、バイナリで保持をする。バイナリ保持時はBase64エンコードが必要なので、組み込みの関数でエンコードしておこう。

それ以外特筆することは特にないが、秘密鍵に誰でもアクセスできるのは良くないため、必要に応じて```aws_secretsmanager_secret_policy```を使ってアクセス制限をしておこう。

```HCL
resource "aws_secretsmanager_secret" "example" {
  name = local.secrets_manager_secrets_name
}

resource "aws_secretsmanager_secret_version" "example" {
  secret_id     = aws_secretsmanager_secret.example.id
  secret_binary = base64encode(file("./secretkey.pem"))
}

resource "aws_secretsmanager_secret_policy" "example" {
  secret_arn = aws_secretsmanager_secret.example.arn
  policy     = data.aws_iam_policy_document.example.json
}

data "aws_iam_policy_document" "example" {
  statement {
    effect = "Allow"

    principals {
      type = "AWS"
      identifiers = [
        data.aws_iam_role.example.arn
      ]
    }

    actions = [
      "secretsmanager:GetSecretValue",
      "secretsmanager:PutSecretValue",
    ]

    resources = [
      aws_secretsmanager_secret.example.arn,
    ]
  }
}
```

# GitHub Providerの設定
さて、AWS Secrets Managerへの設定が終わったら、次は利用するためのProviderの設定を行う。

リポジトリを分けている場合は以下のようにデータソースで秘密鍵を読み出せるようにしておく。

```HCL
data "aws_secretsmanager_secret" "example" {
  name = local.secrets_manager_secrets_name
}

data "aws_secretsmanager_secret_version" "example" {
  secret_id = data.aws_secretsmanager_secret.example.id
}
```

あとは、公式のドキュメントに従い、設定を行う。
```id```はGitHub Appsの作成後に表示されるが、```installation_id```は分かりにくく、以下の画面遷移を辿った後のURLバーに表示される(上記の参考記事ではJWTを自分で作って取得しているが、この方法であれば不要)。

![キャプチャ1_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/0b07c7da-e156-c89e-53d3-4774d39904f1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/7ba9e1d2-3097-d3d0-7a72-2fc89a391521.png)


https://registry.terraform.io/providers/integrations/github/latest/docs

```HCL
terraform {
  required_providers {
    github = {
      source = "integrations/github"
    }
  }
}

provider "github" {
  owner = local.github_owner
  app_auth {
    id              = local.github_app_id
    installation_id = local.github_app_installation_id
    pem_file        = data.aws_secretsmanager_secret_version.example.secret_binary
  }
}
```

# GitHubのリポジトリを管理したい場合
上述した通り、リポジトリの新規作成はGitHub Appsからはできないと思われる。
ただし、更新はできるため、リポジトリのガワだけ作り、Terraformのインポート機能を使えば取り込んですぐに属性を変更することは可能だ。

```HCL
import {
  id = "example"
  to = github_repository.example
}

resource "github_repository" "example" {
  name        = "example"
  description = "GitHubをGitHub Appsのトークンで設定するサンプル"

  visibility = "private"
}
```

これで、より安全にGitHubに接続することができるようになった！