はじめに
みなさんは、DBユーザーの管理をどのように行っているでしょうか。
Create User
やGrant
などの生のSQL文をそのまま管理していたり、何かしらのツールを使って管理しているのではないでしょうか。
弊社では今まではgratanというツールを使って管理してましたが、昨年にHashiCorp Vaultを使った管理に移行しました。
背景と課題
チャットサービスならではの要件
「Chatwork」ではチャットという特性上、秘匿情報を持ったDBテーブルが多く存在します。
そのため、各エンジニアは秘匿情報にアクセスできないようにカラムレベルで権限制御を行っています。
また、監査という観点からもAuditログをしっかりと記録する必要があるため、エンジニア単位でDBユーザーを作成する必要があります。
(ここまで細かくユーザーや権限を管理しているところは意外と珍しいのではないでしょうか)
既存の管理方法と課題
エンジニア単位での管理は、エンジニアの増加に伴い管理コストも上がっていくため、
先程も紹介したgratanというツールを使ってIaC化して管理をしていました。
ただし、ユーザー作成には強い権限が必要な関係から、CI上での実行などが実現できておらず、トイルとなっていました。
また、gratan自体にもいくつか課題がありました。
gratanの課題
- ユーザー作成(権限付与)時の問題点
- 毎回ユーザー毎に
FLUSH PRIVILEGES
を実行していた- ユーザー数が多いとDBへの負荷が上昇してしまう
- 本来は
Grant
文でのユーザー作成、権限付与の場合にはFLUSH PRIVILEGES
は不要
- 回避策として、dry-runで生成されたSQLから
FLUSH PRIVILEGES
を除いて実行する運用になっていた
- 毎回ユーザー毎に
- MySQL8.0以降への対応
- MySQL8.0以降では
Create User
でのユーザー作成が必須 - gratanはアーカイブ化されており更新されていない
- Aurora MySQL V3(MySQL8.0互換)への移行時に利用不可
- MySQL8.0以降では
これらの理由から、gratanをやめて別のツールや仕組みに移行することを決断しました。
HashiCorp Vaultを選定した理由
Vaultに決めた理由は実は最初からほぼ決まっていました。
というのも、過去にあるプロジェクトのチームでテスト的に運用していた実績があり、
問題なく利用できそうというフィードバックを得ていたからです。
そのフィードバックも踏まえて選定した理由は以下の通りです。
- AWS SSO(AWS IAM Identity Center)での認証による効率化
- エンジニア単位ではなくグループ単位での管理が可能
- 運用コストの大幅な削減
- gratanでの課題解決
- 手動対応の削減
- MySQL8.0への対応
- セキュリティの向上
- 動的なDBユーザー作成によるパスワード漏えいリスクの軽減
全体構成
それでは具体的な構成や仕組みについて説明したいと思います。
まずは、全体構成と認証フローは以下の図のようになります。
エンジニアがDBにログインする際の手順
各エンジニアは以下の手順でAurora(DB)にログインします。
- AWS SSOログイン
- AWS認証でVault Serverにログイン
- Vault ServerにアクセスしてDB Credential作成
- 事前にSSM Session Managerを使い踏み台サーバー(db-bastion)経由でAuroraとポートフォワードしておく
- 3で発行したDB Credentialを使ってDBにログイン
Vaultの仕組み
次にVaultの説明です。
Vaultの認証方法
Vaultで利用できる認証方法はいくかあるのですが、今回はAWS認証を選択しています。
https://developer.hashicorp.com/vault/docs/auth/aws
弊社ではOktaとAWS SSOを連携しているため、Vaultの認証をAWS SSO認証にすることでDBユーザーの管理もOktaで管理できるようになることからAWS認証を選択しました。
ハマりポイント
この部分で少しハマったポイントとしては、vaultコマンドがAWSプロファイルの指定(AWS_PROFLE
)に対応しておらず、セッショントークンを渡す必要があったことです。
そのため、上に記載の図で示した通りsso login
したあとにsso get-role-credentials
でセッショントークンを取得する必要があります。
この部分が煩雑なため、aws2-wrapというツールを使うことでこのステップを省略しています。
実際のコマンド例
aws sso login --profile db-read-access export VAULT_ADDR=https://vault.example.com export AWS_PROFILE=db-read-access aws2-wrap vault login -method=aws role=db-read
Vault Secrets Engine
今回はDatabase Secret Engineを使ってシークレットを管理します。
図で言うところのDB Credential
の発行をする部分です。
https://developer.hashicorp.com/vault/docs/secrets/databases
また、Aurora MySQLを利用しているため、プラグインとしてはmysql-aurora-database-plugin
を利用します。
https://developer.hashicorp.com/vault/docs/secrets/databases/mysql-maria
権限の設計
Vaultにはポリシー、ロールという概念があり、
それぞれ、Vaultが管理するシークレットのどこにアクセスできるか、認証したユーザーはどのポリシーを割り当てるかを設定します。
そのため、誰にどの権限を付与するかを考える必要があります。
以下が考えた各権限の組み合わせの一例です。
権限 | AWS SSO ロール名 | ポリシー名 | Vault ロール名 |
---|---|---|---|
管理 | DbAdminAccess | database-admin-access | db-admin |
書き込み | DbWriteAccess | database-write-access | db-write |
読み込み | DbReadAccess | database-read-access | db-read |
また、このロール以外にもDatabase Secret Engineにもロールと言う概念があり、こちらは実際に作成するユーザーにどういった権限をもたせるかを設定するものです。
具体的にはCreate User
やGrant
を設定することになります。
これを先程の表に付け加えると以下の表になります。
権限 | AWS SSO ロール名 | ポリシー名 | Vault ロール名 | DB ロール名 |
---|---|---|---|---|
管理 | DbAdminAccess | database-admin-access | db-admin | - admin-db - write-db - read-db |
書き込み | DbWriteAccess | database-write-access | db-write | - write-db - read-db |
読み込み | DbReadAccess | database-read-access | db-read | - read-db |
上から権限が強いものとして下の権限にもなれるような設計にしました。
これは管理者であっても行うオペレーションによっては読み込み権限だけで作業したい場合にVaultのログインをし直さずに済むように手間を省きたかったからです。
これを踏まえて実際にエンジニアがDB Credential
を作成するためには、先程の認証方法の際に記載したコマンドを実行後に以下を実行することで作成されます。
vault read database/creds/read-db
実行結果としては以下のような出力になります。
Key Value --- ----- lease_id database/creds/read-db/O1k6orQQ8aVIV521swotBrVD lease_duration 1h lease_renewable true password 4ipA0osb744Kcf85-Awp username xxxx-r-xqWc7
運用
最後に実際にどういう運用をしているかを説明します。
Vaultの管理
Vaultの設定はterraformを使って管理することにしました。
また、Vault ServerはEKS上で稼働させました。
今までは新規テーブルが追加されたり、削除されたりする度に全エンジニアのDBユーザーの権限を更新する必要があり、かなり大変だったのですがコードの修正一つでできるようになりました。
ちなみに、先ほどの権限の設計のところで紹介したポリシーやロールは以下のようなコードになります。
# policy resource "vault_policy" "database_read_access" { name = "database-read-access" policy = <<EOT path "database/roles" { capabilities = ["list"] } path "database/creds/read-*" { capabilities = ["read"] } EOT } # role resource "vault_aws_auth_backend_role" "db_read" { backend = vault_auth_backend.aws.path role = "db-read" auth_type = "iam" bound_iam_principal_arns = [ data.aws_iam_role.db-read-access.arn, ] token_ttl = var.database_role_default_ttl token_max_ttl = var.database_role_max_ttl token_policies = [vault_policy.database_read_access.name] } # DB role # 実際にはテーブル単位でカラムレベルの制限を入れています resource "vault_database_secret_backend_role" "read_db" { backend = vault_mount.database.path name = "read-db" db_name = vault_database_secret_backend_connection.db.name default_ttl = var.database_admin_role_default_ttl max_ttl = var.database_admin_role_max_ttl creation_statements = [ "CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';", "GRANT SELECT ON *.* TO '{{name}}'@'%';", ] }
エンジニアのアクセス管理
エンジニアの権限管理は前述したように大元はOktaでの管理となるためOktaにAWS SSOと紐付くグループを作り、そのグループに該当するエンジニア個別や組織のグループを追加することでエンジニアはDBへアクセスできるようになります。
今までは一人のエンジニアを追加するためには、毎回すべてのデータベース、テーブルの権限を付与する必要があるため、何個もGrant文を手動で実行する必要がありましたが既に権限は準備されているため、Oktaグループへのメンバー追加だけでできるようになりこの部分の作業の軽減が一番大きかったです。
まとめ
Vaultへの移行の経緯や仕組み、運用などについて簡単に説明しました。
これによって大幅に作業が軽減することができました。
今までは依頼が来た際に手順書を作成、レビューしてペアオペという作業をしていましたが、Vaultにしてからは毎日の朝会の少しの時間で作業してしまえるほどに短縮できました。
エンジニア用のDBユーザーの管理はこれで改善できましたが、アプリケーション用のDBユーザーの管理は未だにGratanを利用しています。
実は課題があったMySQL8.0への対応もして利用しているのですがこの話はまたの機会にしたいと思います。
お知らせ
最後にお知らせです。
7月11日、12日に開催される「SRE NEXT 2025」にプラチナムスポンサーとしてブース出展します🎉
二日目の12日にはスポンサーセッションも行いますのでぜひご参加ください!
SRE NEXT 2025 詳細
- 公式サイト:https://sre-next.dev/2025/
- 会場:TOC有明
- 日時:2025年7月11日(金)、12(土)