目の前に僕らの道がある

勉強会とか、技術的にはまったことのメモ

Terraformのsopsプロバイダーを使用するだけで機密情報は守られるのか

qiita.com

この記事は、3-shake Advent Calendar 2023の9日目の記事となります。

sops プロバイダーとは

sopsプロバイダーはMozilla sopsを使用して暗号化されたファイルから機密情報を取り出して、terraform上で使用できるようにしたものです。 暗号化の鍵をAWS KMS等を使うことにより、KMSキーを使う権限を持つ人だけ機密情報にアクセスできるようにするものです。 sopsで機密情報を暗号化することにより、平文で機密情報をgitレポジトリに保存することがなくなり安全ということになります。

機密情報を管理したい。でも平文では保存したくない。そういう用途にこちらは使用されます。

本当に安心?

SOPSを使って機密情報を暗号化することによりgitレポジトリには機密情報が平文で残らない。

これで安心と言われていますが、よく考えると機密情報をterraform実行時にはリソースに対して平文で与えているはずです。 つまり、tfstate上は機密情報が平文で保存されています。

例えば、tfstateがS3に保存されているとして、KMSキーへの権限がない人でもS3バケットにアクセスする権限があれば、平文の機密情報が見れてしまいます。 あまりないと思いますが、tfstateをlocalに保存するようにしていてそれをgit管理していてらなんのために暗号化しているのか。。。。ということになります。

こう考えると組織のポリシーによるが、sopsプロバイダーによる暗号化では不十分ではないかという疑問が生まれます。

ドキュメントを調べる

まずプロバイダードキュメントを当たってみます。

Docs overview | carlpett/sops | Terraform | Terraform Registry

To prevent plaintext secrets from being written to disk, you must use a secure remote state backend. See the official docs on Sensitive Data in State for more information.

これが意味してるのはバックエンドをlocalにした場合平文で機密情報が書かれるので、安全なリモートバックエンドを利用すべきということだと思います。

State: Sensitive Data | Terraform | HashiCorp Developer

参照しろと言われたドキュメントの該当部分を読んでみましょう。

ローカルディスクにtfstateを保存した場合は、機密情報が平文で保存されます。リモートにtfstateを保存する場合、保存時に暗号化されるかはバックエンドに依存します。

基本的にリモートステートを使うことを推奨しています。 例えば、Terraform Cloudを使う場合、tfstateは暗号化され、転送時もTLSで暗号化されます。 S3を使う場合もSSE-S3やSSE-KMS等でサーバサイド暗号化を有効にしておくことで、保管時の暗号化がされます。バケットポリシーでHTTPSを強制することで通信時の暗号化も保証することができます。

参考: 暗号化によるデータの保護 - Amazon Simple Storage Service

参考: Amazon S3 のセキュリティのベストプラクティス - Amazon Simple Storage Service

ところがですね。保存時、通信時の暗号化をしても、terraform state pullすると平文でtfstateが手に入ってしまうんですよ。。。後述します。

挙動を実験する

以下のような設定ファイルを作ります。sopsで暗号化したdb_userとdb_passwordをパラメータストアに設定するものになります。

tools-versions

terraform 1.5.5
sops 3.7.3

main.tf

terraform {
  required_version = "~> 1.5.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.15"
    }
    sops = {
      source  = "carlpett/sops"
      version = "~> 0.7.2"
    }
  }
  backend "s3" {
    region  = "ap-northeast-1"
    bucket  = "xxxxxxxxxx"
    key     = "test.tfstate"
  }
}

provider "sops" {
}

provider "aws" {
  region = "ap-northeast-1"
}

data "sops_file" "secrets" {
  source_file = "secrets.yaml"
}

resource "aws_ssm_parameter" "db_user" {
  type     = "String"
  name     = "/test/db_user"
  value    = data.sops_file.secrets.data.db_user
}

resource "aws_ssm_parameter" "db_password" {
  type     = "SecureString"
  name     = "/test/db_password"
  value    = data.sops_file.secrets.data.db_password
}

暗号化前の secrets.yaml

db_user: user
db_password: password

apply結果がこちらとなります。

terraform apply

% export SOPS_KMS_ARN=arn:aws:kms:ap-northeast-1:xxxxxxxxx:key/yyyyyyyyyyyyyyyyyy
% terraform apply
data.sops_file.secrets: Reading...
data.sops_file.secrets: Read complete after 1s [id=-]

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_ssm_parameter.db_password will be created
  + resource "aws_ssm_parameter" "db_password" {
      + arn            = (known after apply)
      + data_type      = (known after apply)
      + id             = (known after apply)
      + insecure_value = (known after apply)
      + key_id         = (known after apply)
      + name           = "/test/db_password"
      + tags_all       = (known after apply)
      + tier           = (known after apply)
      + type           = "SecureString"
      + value          = (sensitive value)
      + version        = (known after apply)
    }

  # aws_ssm_parameter.db_user will be created
  + resource "aws_ssm_parameter" "db_user" {
      + arn            = (known after apply)
      + data_type      = (known after apply)
      + id             = (known after apply)
      + insecure_value = (known after apply)
      + key_id         = (known after apply)
      + name           = "/test/db_user"
      + tags_all       = (known after apply)
      + tier           = (known after apply)
      + type           = "String"
      + value          = (sensitive value)
      + version        = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_ssm_parameter.db_password: Creating...
aws_ssm_parameter.db_user: Creating...
aws_ssm_parameter.db_user: Creation complete after 0s [id=/test/db_user]
aws_ssm_parameter.db_password: Creation complete after 0s [id=/test/db_password]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
terraform apply  8.91s user 0.78s system 124% cpu 7.811 total

state showするとパラメータストアなのでsensitive扱いになっていて、見れません。これはいけるか?

terraform state show

% terraform state show aws_ssm_parameter.db_password
# aws_ssm_parameter.db_password:
resource "aws_ssm_parameter" "db_password" {
    arn       = "arn:aws:ssm:ap-northeast-1:xxxxxxxxx:parameter/test/db_password"
    data_type = "text"
    id        = "/test/db_password"
    key_id    = "alias/aws/ssm"
    name      = "/test/db_password"
    tags_all  = {}
    tier      = "Standard"
    type      = "SecureString"
    value     = (sensitive value)
    version   = 1
}

% terraform state show aws_ssm_parameter.db_user    
# aws_ssm_parameter.db_user:
resource "aws_ssm_parameter" "db_user" {
    arn       = "arn:aws:ssm:ap-northeast-1:xxxxxxxxx:parameter/test/db_user"
    data_type = "text"
    id        = "/test/db_user"
    name      = "/test/db_user"
    tags_all  = {}
    tier      = "Standard"
    type      = "String"
    value     = (sensitive value)
    version   = 1
}

ここで、terraform state pullをしてみて、tfstateファイルをローカルにダウンロードします。

そのtfstateファイルの中の該当部分はこちらとなります。

    {
      "mode": "managed",
      "type": "aws_ssm_parameter",
      "name": "db_password",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "allowed_pattern": "",
            "arn": "arn:aws:ssm:ap-northeast-1:xxxxxxxxx:parameter/test/db_password",
            "data_type": "text",
            "description": "",
            "id": "/test/db_password",
            "insecure_value": null,
            "key_id": "alias/aws/ssm",
            "name": "/test/db_password",
            "overwrite": null,
            "tags": null,
            "tags_all": {},
            "tier": "Standard",
            "type": "SecureString",
            "value": "password",
            "version": 1
          },
          "sensitive_attributes": [
            [
              {
                "type": "get_attr",
                "value": "value"
              }
            ]
          ],
          "private": "bnVsbA==",
          "dependencies": [
            "data.sops_file.secrets"
          ]
        }
      ]
    },

tfstateファイルの中身をよく確認するとしっかり平文で見えています。残念。

"value": "password",

結論

sopsプロバイダーを使用することによりgitレポジトリ上に機密情報を平文で保存することはなくなります。 しかしながら、tfstateのデータ上では設定値が平文で保存されることを防ぐことはできません。terraform state pullする権限があれば、機密情報が見れてしまいます。

運用組織のポリシーで、tfstateへのアクセス権限を適切に権限管理することができるのであれば、選択肢としては取りうります。 暗号化のためのKMSキー、tfstateを保存するS3バケットを機密情報をアクセス可能な人のみ権限を与えることが徹底できればよいです。 しかしながら、機密情報をいかなる場合でもローカルに平文で保存することが許容されない組織であれば、機密情報は手動で設定することを選択したほうが望ましいと思います。

どうしても機密情報をterraformで管理したのであれば、クライアントサイドで暗号化した機密情報をterraformで管理し、アプリ等で使用時にクライアントサイドで復号を行う形も考えられます。

安全かどうかは、tfstateの保存場所、tfstateへのアクセス権限、暗号化鍵のアクセス権限それぞれが適切に設定されているかどうかが鍵となります。

他に何かうまい方法で機密情報を管理しているという方がいらっしゃれば、ご意見ください。

ワークアラウンド

これは自分がよく使う手段となります。

リソースの箱だけ作って、作成時にダミーの値を入れておき、実際の値は手動で設定するという手法です。 ignore_changesを入れておくことで、手動で値を変更しても、terraform的には差分ができないようにしています。 これにより、機密情報をterraformの外に追い出しつつも、機密情報を入れるリソース自体は監理するということが実現できます。

resource "aws_ssm_parameter" "db_password" {
  type     = "SecureString"
  name     = "/test/db_password"
  value    =  "Dummy"
  lifecycle {
    ignore_changes = [value]
  }
}

吉祥寺.pm35 でLTしてきました。 #kichijojipm

吉祥寺.pm こと 句会

吉祥寺.pm35 に参加して、LTしてきました。

kichijojipm.connpass.com

資料はこちら。

言いたいこととしてはベストプラクティスなんてないよ。一般的によりよいプラクティスやパターンはあるけど、どんなときには適用できる銀の弾丸的なものはないから、自身の組織とサービスに合わせてくみ上げていきましょうということ。

正解はひとつ!じゃない!!

その上で、ざっくりとどんな選択肢と選択するための観点を述べていきました。まだ全然ブラッシュアップできるのでどこかでまとめてブログに書きたいところです。

ちなみに最後に出てくる あなたらしく○○ は同僚のスライドのパロディです。

毎回時間オーバーするのでトークで申し込んだ方が良いのでは?というツッコミはごもっともです。

懇親会でもTerraformのお悩みとか短いですが話せて楽しかったです。また参加したいですね。

AWS Control Towerを調べる

これは 3-shake Advent Calendar 2022 10日目の記事です

仕事の中でAWSで複数のアカウントを管理したいという要件あり、その中でAWS Control Towerが使えないかなと調べたものをざっくりと書いていきます。

AWS Control Towerとは

AWS Control TowerとはLanding Zoneを実装するためのAWSのマネージドサービスです。そもそもLanding Zoneって何って話になりますね。

Landing Zoneとは

セキュリティとコンプライアンスのベストプラクティスに基づきアーキテクチャ設計とマルチアカウント環境を管理する仕組みを指します。

Landing Zoneは、下記機能から構成されます。

  • アカウントの発行
    • 必要な初期設定の済んだアカウントを作成
  • 管理用権限の発行
    • 対象アカウントを管理するための権限を作成
  • AWS ログの集約
    • 監査用ログをセキュアに一元保存
  • ガードレールの設置
    • 実施してはいけない操作の禁止
    • 危険な設定の監視

Landing Zoneの実装方法

AWS Control Tower

AWSサービスとして提供される Landing Zoneです。容易に利用可能ですが、カスタマイズするには制限があります。(必須のガードレールを外せなかったり)

主にこれからAWSを利用する場合に利用できます。既存アカウントにも適用可能です。

独自実装の Landing Zone

自組織で独自実装するパターンです。自組織の方針に従って自由にカスタマイズできるのが強みです。ただし、自由にカスタマイズはできますが、自身でメンテナンスしないといけないので、コストはかかります。

主に既存アカウントに適用する場合に利用できます。自組織でアカウント発行の仕組みや管理の仕組みができあがってる場合などです。

そもそもなんでマルチアカウントにするのか

AWSをマルチアカウントにする観点として以下のものが考えられます。

  • 環境の分離
    • 開発、テスト、本番を分離することによるセキュリティおよび統制の確保
  • 請求の分離
    • 部門やシステム単位でのコスト明確化
  • 権限の分離
    • 部門間での権限分離およびアカウントへの権限移譲
  • 複雑性の分離
    • アカウントの目的を明確に絞ることで、構成がシンプルになる

AWS Organizationsだけでもできること

マルチアカウント管理するだけならOrganizationだけでもある程度はできます。むしろAWS Control TowerはOrganizationの機能を利用しています。

AWS Control Towerだと何ができるのか

Control Towerで提供される機能として以下のものがあります。

  • Landing Zoneの提供
    • AWS Organizationを使用してマルチアカウントを作成
      • デフォルトでSandbox、SecurityのOUを作成
    • AWS IAM アイデンティティセンターを利用したID管理を提供
  • Account Factory
    • AWSアカウントのプロビジョニングの自動化
    • 設定可能なテンプレートを提供
  • CloudTrailとConfigログの保存
    • Log Archiveアカウント内のS3バケットに一元的に保存される
  • ガードレールの提供
    • 必須と任意の観点の2種類と予防的と発見的の2種類の組み合わせがありControl Towerにより管理下のアカウントに適用される
    • 参考: ガードレールの仕組み
    • 予防的ガードレール(Service Control Policy)
      • 禁止されたアクションの実行が拒否される仕組み
        • Control Tower管理下のアカウントは必須の予防的ガードレールで禁止されているアクションが不可能
    • 発見的ガードレール(Config)
      • 特定のイベントが発生したときにCloudTrailに記録される仕組み
  • ダッシュボード
    • OUやアカウント、ガードレール違反などが一覧表示できる

AWS Control Towerではできないこと

AWS Control Towerでは提供されてない機能もあります。GuardDutyやSecurity Hubなどのセキュリティ機能を組織全体適用するにはOrganizationsの機能を利用する必要があります。

AWS Control Towerの注意点、制約事項

いろいろ資料を見てみてこの辺注意が必要かなという点を書いていきます。

注意点

制限とクォータ

  • S3へのログの保存期間は、最大15年間保存可能(最近アップデートされた)
  • Security OU の共有アカウントの E メールアドレスは変更可能だが、これらの変更を AWS Control Tower コンソールで確認するには、Landing Zone を更新する必要がある
  • AWS Control Tower Landing zone の OU には、OU あたり5個のSCPの制限が適用される
  • 300超のアカウントを持つ既存の OU は、AWS Control Tower に登録することはできない
    • 300を超える場合はOUを分ける必要がある
  • OUのネストは2段階まで、孫OUを持つことはできない

AWS Control Towerを使うべきなのか

マルチアカウントを展開していくのであれば、AWSのベストプラクティスに乗れるので、使用するのが無難です。

ただし、独自のLanding Zoneをすでに構築しており、Account Factoryの仕組みも独自で構築できているのであれば、移行コストを鑑みてそのままでも問題ないです。

必須の予防的ガードレールが許容できない、OUなどの制限にひっかるなどの運用上の制約がある場合は使えないので、組織のポリシーを見直すか、独自でLanding Zoneを作るかを考える必要があります。

発展

もっと調査したかったが、時間が足りなかったことや今後調べたいことです。

コンソールからAccount Factory実行するとService Catalogの設定項目がありますが、Service Catalog自体の理解不足でどう扱うのかが把握できてないのでこの辺調べたいです。

Account Factory for Terraform(AFT)を使うとアカウント発行そのものもIaC化できるので試したい。

Customization for Control Tower(CfCT)を使うとアカウント発行のイベントをトリガーにCloudFormationを実行できるので、これも実験したい。

まとめ

Control Towerについて調べたことを書いていきました。

実運用自体はまだしてないので、これから触ってみて知見が溜まってきたらまたそれも共有できたらと思います。

2022年10月のふりかえり、まとめ

7年ぶりにふり返りするような気がします。これぶりですかね。

blog.masasuzu.net

10月は思い立って細かいことでも記録に残すようにし始めたのでサブブログの月間投稿数が増えてます。このまま続けたいところです。メインブログは相変わらず0なのでちゃんと書きたいところではあります。

仕事

10月は端境期だったので、技術検証をメインでやってました。

技術

メインブログの方はどちらかというとパブリック向けに書いてます。ただ、この方針だと記事がゆるい記事が書きにくくなってきたので、サブブログを作った経緯があります。

サブブログの技術記事は他の誰かのためではなく未来の自分が思い出すために書くをモットーに書いてます。なのでゆるく、細かい系のことも気軽に書いてます。分からないことは分からないと明示する。途中でも経過を残す。恥も残す。そんな感じです。

以前とくらべてGoogle Cloud回りを10月はいじってた感じですね。

資格

PCA取りました!

11月にはPCAKCNA、年内にCKACKADを取ることを目標に業務とは別に学習してます。なお、業務ではGoogle CloudもKubernetesも今のところ触る余地ないです。が、将来の投資として学習してます。近い未来で使うのが目に見えてるので。

土曜日の午前中に温泉入るのにはまってます。休日の早い時間に行動すると時間の有効活用ができるなとしみじみ感じてます。

生活

寒くなりましたが、がんばります。

11月に向けて

といっても11月始まってますが。

11月は資格の勉強もあるし、新しい固めのお仕事も始まるので、だいぶヘビーになる予感を感じてます。

寒くなる季節なので体調には気を付けつつも、引き続き温泉につかり、ブログ書くのも続けて行きたいですね。

吉祥寺.pm29で久しぶりにLTしてきました #kichijojipm

kichijojipm.connpass.com

久しぶりにLTしてきました。

こんなこと考えてたら良いタイミングできちぴーが開催されるので、LT申し込んでみました。

どうやら7年ぶりだったみたいです。

タイミング的に最終出社日の翌日だったので、キャリアの話をしました。

diary.masasuzu.net

正直、LTにおさまる量じゃなかったのは反省点です。資料ももうちょっとなんとかできたかなあという気持ちがあります。少しずつ登壇回数増やして、勘を取り戻していきたいところ。