#やりたいこと
- terraformは リソースの作成/消去を伴うので 強い権限をもたさないといけない
- そのような強い権限は、リソース操作を定常的に行わければ不要なので、個別ユーザーに直接権限をもたせたくない
そのような要件を実現するための方法になります
.. というのを実現するために 以下の 手順を見つけたのですが、そのままではうまくいかなかったため、
工夫して実用できるようにした、という記事になります
参考
また、 このような方法によらず、本来terraformの実行を人の手を介さないで CI/CDを利用するアプローチのほうが理にかなっているかもしれません
用途
VPCServiceControlを設定する場合は SAを通じてteerraform実行が必要なので、
ユーザーからVPC ServiceControlのリソースを操作したい場合は、 このような実行方法を要することになると思います (※未検証)
準備
準備作業及び設定までは 強い権限を持ったユーザーで行う必要があります
また、プロジェクトにクォータを追加しないとエラーになるので、予め設定しておきます
myuser@mypc gcloud auth application-default set-quota-project myproject
API [cloudresourcemanager.googleapis.com] not enabled on project
[548159814557]. Would you like to enable and retry (this will take a
few minutes)? (y/N)? y
Enabling service [cloudresourcemanager.googleapis.com] on project [548159814557]...
Operation "operations/acf.p2-511111-111111-111-111-11111111" finished successfully.
Credentials saved to file: [/Users/myuser/.config/gcloud/application_default_credentials.json]
These credentials will be used by any library that requests Application Default Credentials (ADC).
Quota project "myproject" was added to ADC which can be used by Google client libraries for billing and quota. Note that some services may still bill the project owning the resource.
ちゃんと設定できていないと以下のように怒られます
WARNING:
Cannot add the project "my-project" to ADC as the quota project because the account in ADC does not have the "serviceusage.services.use" permission on this project. You might receive a "quota_exceeded" or "API not enabled" error. Run $ gcloud auth application-default set-quota-project to add a quota project.
ちなみに serviceusage.services.use は roles/serviceusage.serviceUsageConsumer に含まれています
tfstateの用意
backendとして gcs を 簡易的に用意しておきまます
SAではなく ユーザーに backendにアクセスするための権限が必要になります
project_id=myproject_id
locale=us-west1
gsutil mb -p ${project_id} -c standard -l ${locale} -b on gs://tfstate00000
gsutil iam ch user:myaccount@mydomain.com:roles/storage.admin gs://${project_id}-tfstate
cat << EOF > life-cycle.json
{
"lifecycle": {
"rule": [
{
"action": {
"type": "Delete"
},
"condition": {
"numNewerVersions": 5
}
}
]
}
}
EOF
gsutil lifecycle set life-cycle.json gs://tfstate00000
rm life-cycle.json
設定
端的に terraformでの設定を示します
# set backend
terraform {
backend "gcs" {
bucket = "tfstate00000"
prefix = "terraform/state"
}
}
# prepare provider for token
provider "google" {
alias = "tokengen"
}
data "google_client_config" "default" {
provider = google.tokengen
}
# sa settings
resource "google_service_account" "sa-tfexc" {
provider = google.tokengen
account_id = "sa-tfexec"
project = var.basic.project
}
# give strong role for SA
resource "google_project_iam_member" "sa-tcexec-role" {
provider = google.tokengen
project = var.basic.project
for_each = toset([
"roles/editor",
])
role = each.value
member = "serviceAccount:${google_service_account.sa-tfexc.email}"
depends_on = [
google_service_account.sa-tfexc
]
}
# my user account
# gives minimum role for initial access to gcs backend
resource "google_project_iam_member" "my-minimum-role" {
project = var.basic.project
for_each = toset([
"roles/serviceusage.serviceUsageConsumer",
"roles/iam.serviceAccountUser",
"roles/viewer",
])
role = each.value
member = "user:myaccount@mydomain.com"
}
# roles impersonate
resource "google_service_account_iam_member" "sa-tfexec-member" {
provider = google.tokengen
service_account_id = google_service_account.sa-tfexc.name
role = "roles/iam.serviceAccountTokenCreator"
member = "user:myuser@mydomain.com"
depends_on = [
google_service_account.sa-tfexc
]
}
# myuser@mydomain.com の利用時にコメントを外す
## create token with lifetime for inpersonate user
#data "google_service_account_access_token" "sa" {
# provider = google.tokengen
# target_service_account = google_service_account.sa-tfexc.email
# lifetime = "600s"
# scopes = ["cloud-platform"]
#}
#
## set defauilt provider with token
#provider "google" {
# access_token = data.google_service_account_access_token.sa.access_token
# project = var.basic.project
# region = var.basic.region
#}
variable "basic" {
type = map(any)
default = {
project = "my_project",
region = "us-west1"
zone = "us-west1-b"
}
}
最初のリソース作成時は tokenを利用しないのでコメントアウトしていますが、tokenを利用する際にコメントを外します
リソースを作り終えたら、 権限の借用を行うユーザーに切り替えてlogin
gcloud auth application-default login
実行例
mypc@myuser gcloud auth application-default login
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxx
Credentials saved to file: [/Users/myuser/.config/gcloud/application_default_credentials.json]
These credentials will be used by any library that requests Application Default Credentials (ADC).
Quota project "myproject" was added to ADC which can be used by Google client libraries for billing and quota. Note that some services may still bill the project owning the resource.
構成検証
provider を2段で構成しており、最初の provider は SA を含む token を発行するために用意する
2つ目の providerは通常のもの (こちららで リソースを作っていく)
検証としてバケットを作ってみます
resource "google_storage_bucket" "buket0000" {
name = "test-bucket"
location = "us-west1"
}
-> 成功するはずです
tokenは有効期限が切れれば使えなくなるので、terraformを実行するときだけ強い権限を持つということが実現できました。
設定は以上ですが、ここまで気を配ったのであれば 誰が権限の借用を利用したのか、Logを取得し、意図しないアクセスに対してはalrtを上げるなどの設定もしたほうがよいと思います