Adminがいないクリスマス・イブでも安心してインフラ構築するためのTerraform, Vault, AWS

Adminがいないクリスマス・イブでも安心してインフラ構築するためのTerraform, Vault, AWS:

この記事はエイチームブライズ/エイチームコネクト/エイチーム引越し侍 Advent Calendar 2018 24日目の記事です。


はじめに

TerraformはIaC(Infrastructure as Code)を実現するための最も重要なツールの一つといっても

過言ではないと思います。AWS, GCPはもちろんのこと様々なクラウドサービスの機能をコード化する

ことができます。

Terraformを習得すれば、AWS上に数百台のインスタンスをあっと言う間に起動することだって

いとも簡単にできます。

当然、TerraformでAWS上のリソースを操作するためには、AWSのアクセスキーとシークレットキー、

そして、リソースを操作するためのIAMの権限が必要です。アクセスキーとシークレットキーは定期的

に交換することや、IAMの権限は定期的に見直すことが望ましいですが、特にインフラエンジニアが

増えてくるとこれらの管理は苦痛を伴うものになってきます。

そこで今回はTerraformとVaultを用いて、インフラ管理者以外にアクセスキーとシークレットキーを

配布しないでTerraformの実行環境を整えたいと思います。


Vaultの準備

まずはVaultの環境を構築します。ローカルでVaultを構築したい場合には、Hashicorp Vault 1.0で追加されるauto-unsealを試してみたを参考に

環境を構築することをおすすめします(これはお試し用の環境ですので、本番環境には適用しないで下さい)。

Terraformと連携する場合にはコンテナの外からVaultにアクセスする必要がありますので、上記の記事を参考

にする場合にdemo.hclのlistnerの部分を以下の用に変更してください。

demo.hcl
listener "tcp" { 
  address     = "0.0.0.0:8200" 
  tls_disable = 1 
} 


管理者、作業者用のトークンの発行とポリシーの設定

terraformからVaultに接続するためのトークンをVaultのコマンドで作成します。

管理者用のTerraformと作業者用のTerraform用に2つトークンが必要になりますが、まずは

管理者用のトークンを作成します。ポリシーはrootと同じものでいいと思いますが念の為ttlを設定して

作成しました。

# vault token create -ttl=168h 
Key                  Value 
---                  ----- 
token                s.4hD6KBn9U20TxwpUMvzoKdzm 
token_accessor       4RpO0YsQVM7PHG7FS3jnPgoJ 
token_duration       168h 
token_renewable      true 
token_policies       ["root"] 
identity_policies    [] 
policies             ["root"] 
次に作業者用のトークンを作成します。さすがにrootポリシーのトークンを発行するのはよろしくないことですので、

作業者に必要なポリシーを作成しました。

aws.hcl
path "aws/*" { 
  capabilities = ["read"] 
} 
 
path "auth/token/*" { 
  capabilities = ["update"] 
} 
上記の内容をaws.hclというファイルに保存して、以下のコマンドを実行してポリシーを作成します。

# vault policy write aws aws.hcl 
Success! Uploaded policy: aws 
 
# vault policies 
aws 
default 
root 
そして作成したポリシーを使用した作業者用のトークンを作成します。

# vault token-create -policy="aws" -ttl=24h 
Key                  Value 
---                  ----- 
token                s.12jFUtLq811lJDWLFQnevcTs 
token_accessor       33V4nGl9EK3aTE0WYndFLNaO 
token_duration       24h 
token_renewable      true 
token_policies       ["aws" "default"] 
identity_policies    [] 
policies             ["aws" "default"] 


インフラ管理者用のTerraformの作成

Vaultと連携してAWSのアクセスキーとシークレットキーを管理するTerraformプロジェクトを作成します。

簡略化のためにバックエンド等の設定はしていませんが、以下のように構成にします。

├── admin 
│   ├── 01_vault.tf 
│   ├── terraform.tfvars 
│   └── variable.tf 
variables.tf
variable "aws_access_key" {} 
variable "aws_secret_key" {} 
variable "vault_addr" {} 
variable "vault_token" {} 
terraform.tfvars
aws_access_key = "AWS_ACCESS_KEY" 
aws_secret_key = "AWS_SECRET_KEY" 
vault_addr = "VAULT_ADDR" 
vault_token = "s.4hD6KBn9U20TxwpUMvzoKdzm" 
Vaultをローカル環境で立ち上げている場合はvault_addrはhttp://127.0.0.1:8200

にしておけばいいでしょう。

vault_tokenは先程の作業で発行した管理者用のトークンを指定します。

これらの情報を元にTerraformからVaultのプロバイダーを使う設定を行います。

01_vault.tf
provider "vault" { 
  address = "${var.vault_addr}" 
  token = "${var.vault_token}" 
} 
 
resource "vault_aws_secret_backend" "aws" { 
  access_key = "${var.aws_access_key}" 
  secret_key = "${var.aws_secret_key}" 
  region = "ap-northeast-1" 
 
  default_lease_ttl_seconds = "120" 
  max_lease_ttl_seconds     = "240" 
} 
resource "vault_aws_secret_backend_role" "ec2-admin" { 
  backend = "${vault_aws_secret_backend.aws.path}" 
  name    = "ec2-admin-role" 
policy = <<EOF 
{ 
  "Version": "2012-10-17", 
  "Statement": [ 
    { 
      "Effect": "Allow", 
      "Action": [ 
        "iam:*", "ec2:*", "rds:*" 
      ], 
      "Resource": "*" 
    } 
  ] 
} 
EOF 
} 
 
上記の状態でterraform applyを完了すると、VaultにAWSのadmin roleの情報が保存されます。

念の為Vault側で確認してみると以下の情報が見れると思います。

/ # vault read aws/roles/ec2-admin-role 
Key                 Value 
---                 ----- 
credential_types    [iam_user federation_token] 
default_sts_ttl     0s 
max_sts_ttl         0s 
policy_arns         <nil> 
policy_document     {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["iam:*","ec2:*","rds:*"],"Resource":"*"}]} 
role_arns           <nil> 


作業者用のTerraformの作成

実際にAWSのリソースを操作してインフラ構築を行う作業者用のTerraformは以下の用に作成します。

└── operator 
    ├── 01_vault.tf 
    ├── terraform.tfvars 
    └── variable.tf 
variables.tf
variable "vault_addr" {} 
variable "vault_token" {} 
variable "aws_region" {} 
terraform.tfvars
vault_addr = "VAULT_ADDR" 
vault_token = "s.12jFUtLq811lJDWLFQnevcTs" 
aws_region = "ap-northeast-1" 
vault_tokenには作業者用に作成したトークンを指定します。

管理者用のTerraformプロジェクトではaws_access_keyとaws_secret_keyを定義していましたが、

作業者用のTerraformプロジェクトでは定義しません。これらの情報はVaultから取得出来るように

なるからです。このTerraformプロジェクトでVaultとAWSのproviderを使う設定以下のように

記述します。

01_vault.tf
provider "vault" { 
    address = "${var.vault_addr}" 
    token = "${var.vault_token}" 
} 
 
data "vault_aws_access_credentials" "creds" { 
  backend = "aws" 
  role    = "ec2-admin-role" 
} 
 
provider "aws" { 
  access_key = "${data.vault_aws_access_credentials.creds.access_key}" 
  secret_key = "${data.vault_aws_access_credentials.creds.secret_key}" 
  region  = "${var.aws_region}" 
} 
 
この状態でTerraform applyを行うとVault側から取得したAWSのアクセスキーとシークレットキー

を使用してAWSのリソースを操作することができます。


AWSコンソールでのIAMの確認

これまでで、作業者向けのAWSのアクセスキーとシークレットキーをVault側から取得することが

できましたが、作業者向けのTerraformをApplyしたとき、AWS側ではどのようなIAM情報が作られているのか

AWSのコンソールから確認してみます。ユーザー名でvaultで検索すると今回のTerraformで作成された

IAMのユーザーを見ることができます。
スクリーンショット 2018-12-19 20.33.32.png

ユーザー詳細画面でユーザーに付与されたポリシーを確認するとインラインポリシーで、管理者用のTerraform

で定義したポリシーが付与されていることが確認できます。


スクリーンショット 2018-12-19 20.34.17.png


また、このユーザーは永久に残されるわけではなく、default_lease_ttl_secondsに従って自動的に削除されます。しばらく時間が経ってからユーザー名をvaultで検索しても何もヒットしないことが確認出来ると思います。

vault_1  | 2018-12-19T11:31:33.876Z [INFO]  expiration: revoked lease: lease_id=aws/creds/ec2-admin-role/KXEYNFRvp21mgAsVtnaAzfNS 
Vault側のログを確認してみると上記のようなログが残っており、Vault側からIAMのユーザーを削除してくれたことが確認できます。AWS側のリソースがどうなっているかを意識することなくVault側でユーザーの追加と削除を行ってくれるのは素晴らしいことだと思います。


その他の秘密情報もVaultから取得する

AWSのアクセスキーとシークレットキー以外にも、Vaultから情報を取得することを考えてみます。

例えばRDSを作成する際のDBのパスワードを当然必要ですが、作業者はパスワードを知る必要も

ありませんし、それを平文でGitリポジトリにコミットする必要はないでしょう。

TerraformからVaultにこのような情報を登録する際はvault_generic_secretを利用します。

admin/vault_generic_secret.tf
resource "random_id" "db_password" { 
  byte_length = 8 
} 
 
resource "vault_generic_secret" "db" { 
  path = "secret/db" 
 
  data_json = <<EOT 
  { 
    "password": "${random_id.db_password.b64}" 
  } 
  EOT 
} 
下記のようなtfファイルを作成してterraform applyするとVault側にはsecret/dbというキー名で

情報が登録されます。作業者側で呼び出す前にVaultのポリシーを更新する必要があります。

今回の例の場合ではaws.hclの末尾に以下の記述を追加して、再度ポリシーをアップロードします。

aws.hcl
... 
path "secret/*" { 
  capabilities = ["read"] 
} 
作業者側ではTerraformのtfファイルを以下のように記述すれば、管理者側で設定した

パスワードを呼び出すことができます。

operator/02_rds.tf
data "vault_generic_secret" "db" { 
  path = "secret/db" 
} 
 
resource "aws_db_instance" "db" { 
  allocated_storage      = 10 
  storage_type           = "gp2" 
  engine                 = "mysql" 
  engine_version         = "5.7" 
  password               = "${data.vault_generic_secret.db.data["password"]}" 
... 


まとめ

TerraformとVaultを連携させることにより、有効期限の短いAWSのシークレットキーの発行と利用

を透過的の行えるようになりました。この方法であれば、AWSのシークレットキーを平文で記述する

必要がないので、漏洩のリスクを抑えることができます。

またシークレットキーが必要な管理者側のTerraformでもIAMを作る権限さえあればいいため、

AdministratorAccessのような強いポリシーを持つ必要がないのもメリットです。

Vault側のトークンにもポリシーと有効期限

を設定することが出来るので、こちらも適切なポリシーを設定して有効期限を短くすることにより、

漏洩の影響を最低限に留めることが出来ると思います。


お知らせ

エイチームグループでは一緒に活躍してくれる優秀な人材を募集中です。

興味のある方はぜひともエイチームグループ採用ページWebエンジニア詳細ページ)よりお問い合わせ下さい。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)