有名テック企業の技術ブログを、ひとつのフィードで。
フィード
32件
こんにちは、Infrastructureチームの前多(@kencharos) です。AIの進化におびえながら、電気の資格の勉強でもしようかと考えている日々です。物理はいいですね。 さて、CloudSQLやAlloyDBで初回構築時に設定されるデフォルトユーザーをそのまま使っている方はいらっしゃいますか?いませんよね? 今回の話はAIネタではなくもっと地味なお話です。 データベースの権限設定のつらみ Terraform Postgres Provider の採用 パスワードの扱いについて CI/CDでの実行 1. Argo CD + k8s での Job実行 2. GitHub Actionsから踏み台サーバーに接続して実行する ownerの変更やowner指定には要注意 まとめ おまけ: 第三の選択肢、Crossplane データベースの権限設定のつらみ スキーマの設定を自動マイグレーションするサードパーティのサービスをそのまま使う場合ならともかく、 自分たちで開発しているサービスであれば、マイグレーションとアプリケーションの実行でDBユーザーの権限は分けた方が安全ですし、 複数サービスでDBを共有する場合なら、データベースやスキーマレベルで権限を分けたくなります。 また、AIツールがSQLを実行することも今後はあり得ると思います。 その場合もAIツールが勢い余ってすごいSQLを発行しても大丈夫なように権限を絞ったDBユーザーを使うようにしておきたいものです。 Google CloudのCloudSQLやAlloyDBではDBユーザーやIAMユーザーの作成はできます。 しかし、RDBMS内の権限付与(Grantなど)は結局RDBMSにログインしてSQLを実行する必要があります。 この手のSQLはどうやって管理しているでしょうか? prismaなどのアプリケーションのスキーママイグレーションで管理するのも手段の1つです。 ですが最初にマイグレーション用の権限を持つユーザーは作っておく必要があります。 また何らかの理由で開発サイクルとは別のサイクルでDBユーザーを追加、修正することは多々あります。 レプリケーション用のユーザーが欲しいとか、新しいメンバーが参加したからそのIAMユーザーを追加したいとか、要はスキーマ定義とは独立して管理したいわけです。 筆者らは、このような権限付与を手順書にSQLを書いて手動で実行していました。 苦痛を伴う作業と認識しながら、よい代替手段が当時は思いつきませんでした。 これにはいくつか問題があります。 複数環境で繰り返し実行が必要 変更の都度、現状の状態から細かいGrantやRevokeが必要 手順書の修正などで過去の権限付与の内容の修正などが面倒 などの理由から、微妙に権限付与の内容が環境ごとに異なってしまうことがあります。 意図した通りに権限付与ができているのかが把握しづらくなります。 サービスの拡大に伴ってデータベースの数も増えてきたこともありどうにかしなきゃと思い、解決策を探し始めました。 Terraform Postgres Provider の採用 権限管理のツール化を行うにあたり、要件は次の3つです。 手動ではなくCICDなどから実行して作業を自動化できること 宣言的に権限付与を管理できること PostgreSQLの様々な機能(extension, database, schema, grant, default_privileges, replication slotなど)に対応できること 当初はYAMLに権限付与設定を書いて、自作しようかと思っていましたが、terraform の postgresql provider を見つけたのでこれでいいやとなりました。 ただし、posgresql providerはサードパーティのプラグインですので、扱いには気をつけてください。 要件と概ね一致していましたし、筆者らはTerraformを常用しています。 hclファイルで宣言的に権限設定を管理できるのが大きな理由でした。 (欲を言えば、hclよりも読みやすいフォーマットの方がよかったのですが、それも今ならClaudeなどでサマリできそうだし、まあいいやとなっています) これを使って、以下のような権限設定を作成します。 ownerとなるmigration user、アプリケーション実行用のapplication userを作成する database, schemaは独自のものを作る application userにschema, tableへのgrantとdefault privilegesを設定する。 上記をhclで表現したものが次の通りです。 terraform { required_version = "1.10.5" backend "local" { path = "sample.state" } required_providers { postgresql = { source = "cyrilgdn/postgresql" version = "1.26.0" } } } // プロバイダの設定。ここではデフォルトユーザーやadminユーザーを指定する provider "postgresql" { host = "x.x.x.x" port = 5432 database = "postgres" username = "postgres" password = var.admin_password sslmode = "disable" connect_timeout = 15 } // userの作成 resource "postgresql_role" "migration" { name = "migration" login = true # password_woはstateにパスワードを記録しない。パスワードを変更したい場合は password_wo_versionを変更する password_wo = var.migration_password password_wo_version = "1" bypass_row_level_security = true } resource "postgresql_role" "application" { name = "application" login = true password_wo = var.application_password password_wo_version = "1" } // databaseの作成 resource "postgresql_database" "app_db" { name = "app_db" owner = postgresql_role.migration.name allow_connections = true } // schemaの作成 resource "postgresql_schema" "schema" { name = "app" owner = postgresql_role.migration.name } resource "postgresql_grant" "grant_to_schema" { database = postgresql_database.app_db.name role = postgresql_role.application.name schema = postgresql_schema.schema.name object_type = "schema" privileges = ["USAGE"] } # grant role to table resource "postgresql_grant" "grant_to_application" { database = postgresql_database.app_db.name role = postgresql_role.application.name schema = postgresql_schema.schema.name object_type = "table" # objectsが空の場合は all # objects = [] # privileges は明示する。ここに無いものは revoke 対象になる。 privileges = ["SELECT", "INSERT", "UPDATE", "DELETE", "REFERENCES", "TRIGGER", "TRUNCATE"] } # default privileges を指定して、application userに対して新規テーブルの権限を付与する resource "postgresql_default_privileges" "default_privs" { role = postgresql_role.application.name database = postgresql_database.app_db.name schema = postgresql_schema.schema.name owner = postgresql_role.migration.name objec
こんにちは、Infrastructure Teamの宮本(@m1yam0t0)と申します。 本記事では、キャディの権限昇格システムの取り組みを紹介します。 目次 目次 はじめに 内製システムから Google Cloud PAM への移行 PAM の利用資格の設定 PAM の運用で工夫していること Slack 通知機能の実装 Devin による利用資格設定の自動化 まとめ はじめに みなさんは、パブリッククラウドの権限はどのように管理されていますか? IAMでメンバーに必要な権限を付与していますでしょうか?TerraformでIaC管理されていますでしょうか? キャディでは、最小権限の原則に従って、開発者には閲覧系の必要最低限の権限のみを付与しています。*1 開発・運用で追加の権限が必要になった場合は、Just-In-Time(JIT) Accessの仕組みで一定期間だけ権限昇格できるようになっています。 開発者が権限を申請し、承認者が承認してはじめて権限が付与されます。 最小権限の原則を徹底することで、昨今利用が広がっているAIエージェントを活用した場合にも、本番環境の権限を持っていないため、誤って操作してしまうリスクを低減できます。 本記事ではこのJIT Access Systemを内製システムからGoogle Cloud Priviledged Access Manager(PAM)に移行したお話を紹介いたします。 内製システムから Google Cloud PAM への移行 キャディでは、2022年より内製のJIT Access Systemを運用しておりました。 しかし、この内製のJIT Access Systemについて、認可制御に課題があったため、IAM Condition を組み合わせて権限が付与できるように改修を検討していました。 そんな中、Google Cloud PAM のアップデートで、権限のスコープが設定可能になったことを知り、検証をしてみたところ、内製のシステムを改修しづつけるより、効率良く目的を達成できることがわかりました。 IAM release notes | Identity and Access Management (IAM) | Google Cloud Documentation 上記を踏まえて、以下の理由から、キャディのJIT Access SystemをGoogle Cloud PAMへ移行する価値があると判断し、移行を実施いたしました。 権限のスコープを細かく設定でき、取得する権限を必要最小限に抑えられる Cloud Loggingに監査ログが残るため、通知や監査に活用できる Google Cloudマネージドのサービスであるためメンテナンス不要 PAM の利用資格の設定 Google Cloud PAMでは、取得したい権限のセットを利用資格として定義し、必要な権限に対応する利用資格を選択して申請します。 Terraform Provider が公式で提供されているため、IaCで管理できます。 利用資格はユースケースごとに複数作成するため、 以下のようなTerraform moduleを定義し、変数を入力することで容易に設定できるようにします。 resource "google_privileged_access_manager_entitlement" "entitlement" { provider = google-beta entitlement_id = var.entitlement_id location = var.location parent = var.parent max_request_duration = var.max_request_duration eligible_users { principals = var.eligible_users } privileged_access { gcp_iam_access { resource_type = var.resource_type resource = var.resource dynamic "role_bindings" { for_each = toset(var.roles) content { role = role_bindings.value } } } } approval_workflow { manual_approvals { require_approver_justification = var.require_approver_justification steps { approvers { principals = var.approvers } approvals_needed = var.approvals_needed approver_email_recipients = var.approver_email_recipients } } } additional_notification_targets { admin_email_recipients = var.notification_emails } requester_justification_config { unstructured {} } } 実際に利用する箇所では以下のようにTerraform moduleを呼び出して定義しています。 申請・承認するユーザを設定できるため、チームによって申請可能な利用資格を設定できます。 module "pam_org_gcs_bucket_read_access" { source = "../../modules/pam" # 利用資格名 entitlement_id = "gcs-bucket-read-access" # Organization, Folder, Project 単位で指定可能 parent = "organizations/${local.organization_id}" location = "global" resource_type = "cloudresourcemanager.googleapis.com/Organization" resource = "//cloudresourcemanager.googleapis.com/organizations/${local.organization_id}" # 取得したい role を定義 roles = [ "roles/storage.bucketViewer", "roles/storage.objectViewer", ] # 申請可能なユーザ eligible_users = [ "group:users@caddi.com", ] # 承認可能なユーザ approvers = [ "group:approvers@caddi.com", ] # 最大の申請期間 max_request_duration = "14400s" # 必要な承認の数 approvals_needed = 1 } PAM の運用で工夫していること Slack 通知機能の実装 PAMの通知機能はメール通知のみでSlackへの通知には標準では対応していません。 しかし、既存のJIT Access SystemではSlack通知するようにしていたため、利用者体験が変わらないようにする必要がありました。 そこで、PAMの監査ログの内容をパースしてSlack通知するAPIを実装しました。 以下のような仕組みで動作しています。 Pub/Subを経由して、実装したSlack通知APIに送信 Cloud Loggingに保存されているPAMの監査ログをLog routerでPub/Subに転送 Pub/Subの Push Subscription を使ってCloud Runで動作しているSlack通知APIにHTTP POST 実際の構成図は以下です。 PAM通知機能の構成図 上記のSlack通知APIを利用して、既存システムの使用感はそのままに、SlackでPAMの申請・承認を通知できるようにしました。 PAMのSlack通知 Slack通知の内容についても、利便性を上げるために様々な改善をしています。 承認結果のメッセージを申請したメッセージのスレッドに紐づけて投稿 申請内容に不備がある場合は、申請内容の修正を促すように警告文を自動で投稿 申請のステータスによって、SlackのAttachmentの色を変更 Devin による利用資格設定の自動化 PAMの利用資格の中に取得したい権限が存在しない場合、利用資格を修正する必要があります。 PAMの移行当初は取得できる権限が不足しており、利用者から多く依頼を受けていました。 その度にTerraformの定義を修正しレビューするのは大変です。 そこで、Devinを使って、Terraformの修正からPRの作成を自動でしてもらうようにしました。 Devin Playbook で利用資格を修正するPR作成作業を定型化しています。 利用者がSlack Workflowで追加してほしい権限を入力するとDevinが呼び出され自動でPRを作成します。 あとは、チームメンバーがPRをレビュー