目次
みんな大好きTerraform。知名度も上がって様々なResourceのProvisioningに利用されるようになりました。OfficialのProviderはたくさんあります。GCP,AWS,Azure… 中でも最近便利だなーと思ったVaultとKubernetes Providerについてご紹介します。
Vault Provider
Vault構築する人には絶対使って欲しいProvider。
Vaultの設定は次の理由により煩雑な処理が必要で、手順を準備したりshell化していても誰でも設定できるようなシロモノではない。
- 構築時のみ実行することが多い
- Policy,Auth Backend,PKIなど様々な設定が存在する
- CLIやcURLでの操作はTokenを設定したり出力をParseするのが大変
- なんなら出力は別のリソースで利用したい
Policy
次の例はPolicyをtfファイルに記述したものです。
provider vault {
version = "~> 2.9.0"
address = var.vault.addr
token = var.vault.token
skip_tls_verify = true
}
/*
Setup Vault policies.
*/
resource vault_policy consul-template {
name = "consul-template"
policy = file("./policies/consul-template.hcl")
}
上記で参照しているPolicyは次のとおり。
# Update internal certificates.
path "pki_int/issue/default-dot-svc-dot-cluster-dot-local"
{
capabilities = ["create", "update"]
}
# renew
path "auth/token/renew-self"
{
capabilities = ["create", "update"]
}
つまりこれを利用すれば
- Policyのコード化
- Policyの適用手順をコード化
が可能になり、誰が実行しても同じ設定が可能になります。policiesディレクトリを環境ごとに作成し、環境ごとにtfvarsを用意するかworkspaceを利用することで別環境への設定が容易になるでしょう。またVaultのTechnical Transferとしても有効でしょう。
AppRole Auth Backend
次の例はAppRole Auth Backendをtfに記述したものです。
/*
AppRole Auth Backend.
*/
resource vault_auth_backend approle {
type = "approle"
}
resource vault_approle_auth_backend_role pxc {
backend = vault_auth_backend.approle.path
role_name = "pxc"
token_policies = [
"pxc",
]
token_ttl = 600
token_max_ttl = 1800
token_no_default_policy = true
token_bound_cidrs = [
"10.31.128.0/18",
]
secret_id_num_uses = 1
secret_id_ttl = 3600
secret_id_bound_cidrs = [
"10.31.128.0/18",
]
}
Kubernetes Auth Backend
次の例はKubernetes Auth Backendをtfに記述したものです。先にKubernetes Provider使ってしまってますが、気にしないでください。
/*
Generate Service Account and Cluster Role Binding for the Vault Auth Backend.
*/
resource kubernetes_service_account vault-auth {
metadata {
name = "vault-auth"
}
}
resource kubernetes_cluster_role_binding vault-auth {
depends_on = [kubernetes_service_account.vault-auth]
metadata {
name = "vault-tokenreview-binding"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "system:auth-delegator"
}
subject {
kind = "ServiceAccount"
name = "vault-auth"
namespace = "default"
}
}
/*
Retrive k8s secrets
*/
data external k8s-secrets {
depends_on = [kubernetes_cluster_role_binding.vault-auth]
program = ["./scripts/k8s-secrets.sh"]
query = {
service_account = "vault-auth"
}
}
/*
Kubernetes Auth Backend.
*/
resource vault_auth_backend kubernetes {
type = "kubernetes"
}
resource vault_kubernetes_auth_backend_config vault-primary {
depends_on = [
data.external.k8s-secrets,
vault_auth_backend.kubernetes,
]
backend = vault_auth_backend.kubernetes.path
kubernetes_host = var.kubernetes.host
kubernetes_ca_cert = data.external.k8s-secrets.result["cacert"]
token_reviewer_jwt = data.external.k8s-secrets.result["jwt"]
}
上記でData External Provider参照している./scripts/k8s-secrets.sh
は次のとおり。jqやらkubectlを利用していてキモいですが、かなり柔軟に値を取得することができるので重宝します。
#! /usr/bin/env bash
set -xe
set -o pipefail
# Extract args into shell variables
eval "$(jq -r '@sh "SERVICE_ACCOUNT=\(.service_account)"')"
K8S_API_CERT=$(kubectl get secrets -o yaml | \
yq -r ".items[] | select(.metadata.annotations[\"kubernetes.io/service-account.name\"] == \"${SERVICE_ACCOUNT}\").data | .[\"ca.crt\"]")
K8S_API_TOKEN_JWT=$(kubectl get secrets -o yaml | \
yq -r ".items[] | select(.metadata.annotations[\"kubernetes.io/service-account.name\"] == \"${SERVICE_ACCOUNT}\").data.token" | \
base64 -D)
jq -n \
--arg cacert "${K8S_API_CERT}" \
--arg jwt "${K8S_API_TOKEN_JWT}" \
'{"cacert": $cacert, "jwt": $jwt}'
Policyのとこで書きましたが、手順書やshellを書くよりもずっといいですよ。
こんなノリでVaultに設定したいtfを用意してやればいいのです。便利ですよね?
Kubernetes Provider
Kubernetesのリソースデプロイに関してはHelm,Kustomize,jsonnet,Replicated Ship…などなどいろいろありいまして、正直言ってこれの利用価値わかりませんでしたが、利用してみると他のツールとは一味違ったことが可能になります。
例えばHelmはValuesを指定したSolid Stateなデプロイに向いていますが、Kubernets Providerを使うと動的なデプロイが可能になります。揮発性の高いSecretとは親和性が高いと思います。Operatorを利用した自動運用化と、静的デプロイの間ぐらいの印象です。
Secrets
Vaultの設定を実施したら、別のtfでVaultから読み取ったデータをKubernetes Secretに埋め込めます。
provider vault {
version = "~> 2.9.0"
address = var.vault.addr
token = var.vault.token
skip_tls_verify = true
}
data vault_approle_auth_backend_role_id migration-db {
backend = "approle"
role_name = "pxc"
}
resource vault_token db-migrator {
policies = ["db-migrator"]
no_parent = true
no_default_policy = true
renewable = true
ttl = "768h"
explicit_max_ttl = "92160h"
}
resource vault_token job-generator {
policies = ["job-generator"]
no_parent = true
no_default_policy = true
renewable = true
ttl = "768h"
explicit_max_ttl = "92160h"
}
provider kubernetes {
config_context_auth_info = var.vault.auth_info
config_context_cluster = var.vault.cluster
}
resource kubernetes_secret db-migrator {
metadata {
name = "db-migrator"
}
data = {
role-id = data.vault_approle_auth_backend_role_id.migration-db.role_id
vault-token = vault_token.db-migrator.client_token
}
type = "kubernetes.io/opaque"
}
resource kubernetes_secret job-generator {
metadata {
name = "job-generator"
}
data = {
vault-token = vault_token.job-generator.client_token
}
type = "kubernetes.io/opaque"
}
上記applyすると次のようにSecretが作成されています。
$ kubectl get secrets
NAME TYPE DATA AGE
db-migrator kubernetes.io/opaque 2 5s
default-token-ndnzq kubernetes.io/service-account-token 3 6d
job-generator kubernetes.io/opaque 1 5s
vault-auth-token-5tnhn kubernetes.io/service-account-token 3 122m
これはかなり強力な機能です。
実を言うとこれがやりたくて理由でTerraformでVaultと一緒にVault ProviderとKubernetes Providerを使いたくなったのです。もちろんGCPのリソースもTerraformで構築しています。Nomadごめんよ・・・
See u next time!
株式会社grasys(グラシス)は、技術が好きで一緒に夢中になれる仲間を募集しています。
grasysは、大規模・高負荷・高集積・高密度なシステムを多く扱っているITインフラの会社です。Google Cloud (GCP)、Amazon Web Services (AWS)、Microsoft Azureの最先端技術を活用してクラウドインフラやデータ分析基盤など、ITシステムの重要な基盤を設計・構築し、改善を続けながら運用しています。
お客様の課題解決をしながら技術を広げたい方、攻めのインフラ技術を習得したい方、とことん技術を追求したい方にとって素晴らしい環境が、grasysにはあります。
お気軽にご連絡ください。