terraformのstateファイルにパスワードを残さない

Terraform

stateファイルにパスワードが平文で出力される

詳解 Terraform 第3版ーInfrastructure as Codeを実現する」の「6章 シークレットを管理する」には次のように記載されています

Terraformはこれらの認証情報をプレーンテキストであるterraform.tfstateファイルに保存してしまいます。これは2014年から指摘されている問題(https://bit.ly/33gqaVe)ですが、最適な方法での解決策に対する何の計画もありません。ステートファイルからシークレットを消すいくつかの回避策はありますが、不安定で、Terraformリリース毎に壊れてしまう可能性が高いので、使用するのはお勧めしません。

例えばSecret Managerから値を取得した場合などでも、取得したパスワードやtokenがstateファイルに平文で出力されます。

dataを使ってGoogle CloudのSecret Managerから値を取得した場合

dataを利用し、Google CloudのSecret Managerから特定の文字列を取得する場合は下記のように記述します

data "google_secret_manager_secret_version" "secret_manager_test" {
  provider = google-beta
  secret   = "SECRET_MANAGER_KEY"
  version  = "latest"
  project  = "xxxx"
}

上記のように記述した場合、stateファイルには次のように出力されます

{
  "version": 4,
  ...
  "resources": [
    {
      "mode": "data",
      ...
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            ...
            "secret": "SECRET_MANAGER_KEY",
            "secret_data": "ここに保存された値が平文で出力される",
            "version": "1"
          },
          ...

data externalを使用しpythonで取得する

stateファイルに出力されないようにパスワードを取得したい場合は、data externalとpythonを利用して次のように取得することができます

data "external" "secret_value" {
  program = ["python3", "get_secret.py", "プロジェクトid", "SECRET_MANAGER_KEY", "latest"]
}

locals {
  secret_by_python = data.external.secret_value.result["secret_value"]
}
# get_secret.py

import json
from google.cloud import secretmanager

def access_secret_version(project_id, secret_id, version_id):
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("UTF-8")

if __name__ == "__main__":
    import sys
    project_id = sys.argv[1]
    secret_id = sys.argv[2]
    version_id = sys.argv[3]    
    secret_value = access_secret_version(project_id, secret_id, version_id)
    print(json.dumps({"secret_value": secret_value}))

上記のように別スクリプトから取得する場合は、stateファイルに出力されることはありません。

http requestのheaderの内容もstateに出力される

無事パスワードを取得してきたので、それを使って例えばGitHubの値を取得した場合どうなるでしょうか。下記のように記載すれば、GitHubから要素を取得できます。

data "http" "github_code" {
  url = "https://api.github.com/repos/xxx/xxx/contents/xxx?ref=main"
  request_headers = {
    Authorization = "token ${data.external.secret_value.result["secret_value"]}"
  }
}

するとstateには下記のように出力されます。

{
  "mode": "data",
  "type": "http",
  ...
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        ...
        "request_headers": {
          "Authorization": "token ここにtokenが平文で出力される"
        },

パスワードを伴う処理は別スクリプトで行う必要がある

パスワードを取得して処理をする場合、その処理も含めて別スクリプトで行う方法が無難なようです。stateファイルに平文で残ってしまう仕様が解消されることを期待したいですね。。

コメント

タイトルとURLをコピーしました