03Get started
導入
tenant-pass は、Go で書かれた 1 つの実行ファイル(静的バイナリ)です。依存するライブラリは、データベースのドライバー程度です。まず手元の PostgreSQL で、パスが認証情報に変わるところまで 5 つの手順で試せます。本番に導入するまでの流れは、その後にあります。
v0.1.0(1.0 より前)。設定や API の細部は変わる可能性があります。
Try it locally
手元で試す
この手順は平文の接続を使うので、手元での評価専用です。設定ファイルの全文と、わざと失敗させて動きを確かめる方法は、 はじめに(ローカルで試す) にあります。
- 1 インストール
go install github.com/kobesoft-labs/tenant-pass/cmd/tenant-pass@latest go install github.com/kobesoft-labs/tenant-pass/cmd/tenant-pass-gateway@latest # ゲートウェイを使う場合
- 2 鍵とトークンを作る
tenant-pass gen-gateway-key -private gw.key -public gw.pub # 秘密鍵はゲートウェイにだけ置く tenant-pass gen-rotation-key -out rotation.key # tenant-pass にだけ置く tenant-pass gen-app-token # token はアプリへ、token_sha256 は設定へ
- 3 データベースにロールを用意する
CREATE DATABASE tenant_pass; REVOKE ALL ON DATABASE tenant_pass FROM PUBLIC; CREATE ROLE tenant_pass_admin LOGIN CREATEROLE PASSWORD '...'; GRANT CONNECT ON DATABASE tenant_pass TO tenant_pass_admin; CREATE ROLE tenant_pass_managed NOLOGIN; CREATE ROLE t_acme LOGIN IN ROLE tenant_pass_managed; GRANT t_acme TO tenant_pass_admin WITH ADMIN OPTION, INHERIT FALSE, SET FALSE;
- 4 設定を検査して起動する
tenant-pass check-config -config config.json -connect tenant-pass serve -config config.json
- 5 パスを認証情報に交換してみる
PASS=$(tenant-pass sign-pass -key gw.key -kid gw1 -aud billing -host acme.example.com) curl -s http://127.0.0.1:8443/v1/credentials \ -H "Authorization: Bearer $APP_TOKEN" -H 'Content-Type: application/json' \ -d "{\"pass\":\"$PASS\"}"
{
"tenant": "acme", "store": "postgres",
"host": "127.0.0.1", "port": 5432, "database": "app",
"username": "t_acme", "password": "lRG6msAkg88kIIjJU-N6yWZ6wzx1gQj62GYG71hcgjE",
"expires_at": "2026-11-03T12:08:59Z", "refresh_at": "2026-11-03T12:03:59Z"
} Requirements
対応するもの
PostgreSQL
16 以降
- テナントのアカウント
- ロール
- 送るもの
- SCRAM-SHA-256 ベリファイア
- 有効期限
- データベースが強制する(VALID UNTIL)
セットアップの手順 →
MySQL
8.0.27 以降、8.4、9.x
- テナントのアカウント
- ユーザー
- 送るもの
- caching_sha2_password のハッシュ
- 有効期限
- tenant-pass が定期的に削除する(デュアルパスワードを使う)
セットアップの手順 →
Redis / Valkey
Redis 7.0 以降、Valkey 7.2 以降
- テナントのアカウント
- ACL ユーザー
- 送るもの
- SHA-256 の ACL ハッシュ
- 有効期限
- tenant-pass が定期的に削除する(複数のパスワードを使う)
セットアップの手順 →
ゲートウェイ: 付属の tenant-pass-gateway を、Caddy・nginx(どちらも動作確認済み)・Traefik・Envoy の forward-auth として使えます。独自のゲートウェイに組み込む場合は、Go の pass パッケージか、PASETO v4 のライブラリを使います。
ディレクトリ: テナントとアカウントの対応を設定ファイルに直接書くか、すでにテナントの情報を持つ PostgreSQL / MySQL に独自の SQL クエリで問い合わせます。クエリは読み取り専用のトランザクションで実行し、起動時に検査します。
鍵管理(任意): HashiCorp Vault / OpenBao の Transit に、ローテーションキー(hmac)と署名鍵(ed25519)を置けます。
Rolling out
本番に導入するまで
ゲートウェイの鍵 ローテーションキー アプリトークン ここで作るのは、ゲートウェイの鍵、ローテーションキー、アプリトークンの 3 つです。秘密にする側は、それぞれゲートウェイ、tenant-pass、アプリケーションにだけ置きます。
-
STEP 1
ストアを準備する
tenant-pass がログインに使う最小権限の管理者アカウントと、「管理対象」の印(PostgreSQL のグループロール、MySQL の印となるロール、Redis のチャネルルール)を用意します。
ストアごとの手順 → -
STEP 2
ローテーションキーを作る
tenant-pass gen-rotation-key -out rotation-YYYY-MM.key で作ります。この鍵は tenant-pass のレプリカにだけ配ります(できれば Vault / OpenBao の Transit に置きます)。
鍵管理 → -
STEP 3
ゲートウェイの鍵を作る
tenant-pass gen-gateway-key で作ります。秘密鍵はゲートウェイに、公開鍵は tenant-pass の設定の gateway_keys に置きます。
tenant-pass-gateway → -
STEP 4
アプリトークンを作る
アプリケーションごとに tenant-pass gen-app-token で作ります。トークンはアプリケーションのシークレットに、ハッシュは設定ファイルに書きます。クライアント証明書(URI SAN)で認証することもできます。
apps の設定 → -
STEP 5
設定を検査する
tenant-pass check-config -config config.json -connect を実行します。起動時と同じ検査をすべて行い、ディレクトリとすべてのノードに実際に接続して確かめます。
設定リファレンス → -
STEP 6
テナントごとに切り替える
tenant-pass を起動し、アプリケーションをテナントごとに切り替えます。最初に使われたときに、tenant-pass は各アカウントの既存のパスワードを置き換えます。切り替えが済んだら、これまで保存していた古いパスワードを、自社のシステムから消してください。
最初の構築 →
Application side
アプリケーションで変えること
アプリケーションですることは、ヘッダーで届いたパスを tenant-pass に渡し、返ってきた認証情報をメモリにキャッシュすることです。そのうえで、次の 6 点を守ってください。
認証情報はメモリの中だけに置く
認証情報を、ディスク、共有キャッシュ(Redis・memcached)、データベース、ログ、エラーレポート、メトリクスのラベルに書かないでください。
refresh_at の時刻に取り直す
refresh_at の時刻から、tenant-pass は次のウィンドウのパスワードを返します。早めに取り直しても害はありません(同じパスワードが返ります)。PostgreSQL では、refresh_at を実質の期限として扱ってください。
認証エラーなら repair を付けて 1 回だけ取り直す
PostgreSQL の 28P01、MySQL の 1045、Redis の WRONGPASS を受けたら、"repair": true を付けて認証情報を取り直し、1 回だけ再接続します。それでも失敗したら、繰り返さずにエラーにします。
接続プールはテナントごとに分ける
テナント A のプールをテナント B に使わないでください。使われなくなったテナントのプールは閉じて、認証情報をメモリから消します。
テナントは応答から取る
どのテナントかは、認証情報の応答の tenant から取ります。Host ヘッダーからは取らないでください。
エラーの種類で扱いを分ける
400・401・403 は、同じパスで何度試しても成功しない失敗です。429・503 は、間を空けてリトライしてかまいません。
# リクエストごと(疑似コード) credential = cache.get(tenant) if credential is None or now >= credential.refresh_at: credential = tenant_pass.fetch(pass) # POST /v1/credentials cache.put(tenant, credential) conn = connect(credential) if conn failed with authentication error: credential = tenant_pass.fetch(pass, repair=True) cache.put(tenant, credential) conn = connect(credential) # 再接続は 1 回だけ。繰り返さない
パスのないリクエストには、テナントのデータに触れる権限がありません。そのようなリクエストはエラーにしてください。後から動く処理には ジョブチケット を使います。Go の実装例は、 クライアント連携 にあります。
Production
本番の構成
- ・内部ネットワークのロードバランサーの後ろで、2 台以上のレプリカを動かします。tenant-pass に届くのは、アプリケーションと監視だけにします。
- ・コンテナイメージ(distroless、root 以外のユーザー、シェルなし)を、読み取り専用のルートファイルシステムで、追加の特権(ケイパビリティ)を与えずに動かします。シークレットはファイルとしてマウントします。
- ・すべてのレプリカで rotation・nodes[].name・ディレクトリの設定を同じにします。
- ・時計は NTP で同期します。ずれは分ではなく、秒の単位に収めてください。
- ・ヘルスチェックには、GET /healthz(動いているか)と GET /readyz(リクエストを受け付けられるか)を使います。
- ・停止するときの猶予時間を、shutdown.drain + shutdown.timeout(既定で合わせて 20 秒)より長くします。Docker の既定の 10 秒では足りません。
# Docker Compose の例 services: tenant-pass: image: ghcr.io/kobesoft-labs/tenant-pass:<version> command: ["serve", "-config", "/etc/tenant-pass/config.json"] read_only: true cap_drop: [ALL] security_opt: ["no-new-privileges:true"] stop_grace_period: 20s volumes: - ./config.json:/etc/tenant-pass/config.json:ro secrets: [tenant-pass.key, rotation-2026-11.key, pg1-admin-password]
シークレットや、信頼の起点になるファイル(公開鍵や証明書など)は、root かコンテナを動かすユーザー(イメージでは uid 65532)の所有にし、グループやほかのユーザーが書き込めないようにしてください。イメージはタグではなくダイジェストで指定し、デプロイの前に署名を確かめます(リリースされたイメージの確かめ方)。