diff --git a/docs/glossary.md b/docs/glossary.md index d6ba9e06..e4129f85 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -94,6 +94,12 @@ up の後に VS Code で dev コンテナを開く(docs/specifications/editor- | region の出所 | — | 参照の AWS_DEFAULT_REGION へ書く値をどこから得たか。参照に既にある・ホストの region・利用者の入力・既定の値(ap-northeast-1)の 4 つ | — | — | `docs/specifications/secret-backend.md` | | 鍵の確認の結果 | — | AWS の方法 3 で、ホストの [default] の鍵を取り込むかを決めた結果。受け入れた・断った・確認しなかった(ホストに鍵が無い)の 3 つ。方針が import なら受け入れた。skip ならホストに鍵があれば断った、無ければ確認しなかったになる | — | — | `docs/specifications/secret-backend.md` | | 鍵ファイルの発見 | — | ~/gcp-credentials/ の *.json を読み、正規化したプロファイル名から鍵ファイルへの対応を作ること。出力を持たない | — | — | `docs/specifications/secret-backend.md` | +| 利用の形 | — | OpenBao サーバの持ち方の 3 つの区分。個人の形(1 人で 1 台)・チームの形(チームで 1 台、チーム共通と個人の置き場を分ける)・既存のサーバの形(組織が運用するサーバへ接続する) | — | — | — | +| ポリシーの雛形 | — | devbase が前提にする権限を OpenBao のポリシー(HCL)として書いた、docs/user/openbao/policies/ のファイル。管理する人が自分のサーバへ書き込んで使う | — | — | — | +| 試用の構成 | — | docs/user/openbao/compose.yml で開発モードの OpenBao を手元に立て、devbase の接続を試して片付けるまでの最小の構成 | — | — | — | +| 利用者の識別子 | — | secrets/backend.yml の openbao.user。サーバの entity 名と同じ値にし、個人単位の置き場のパスに使う | — | — | — | +| コンテナから見た接続先 | — | dev コンテナへ BAO_ADDR として渡す OpenBao の接続先。openbao.url が http の loopback でローカル扱いのときはホストを host.docker.internal に置き換えた値、それ以外(https を含む)は openbao.url のまま | — | — | — | +| 管理する人 | — | 利用者の OpenBao サーバで、マウント・AppRole・ポリシー・entity を作る人。個人の形では利用者本人 | — | — | — | ## スナップショット(`snapshot`) diff --git a/docs/glossary/glossary.json b/docs/glossary/glossary.json index cc36f2a9..73a5a5f5 100644 --- a/docs/glossary/glossary.json +++ b/docs/glossary/glossary.json @@ -954,6 +954,48 @@ "context": "cli", "meaning": "zsh を起動して補完のファイルを読み込み、行を打って補完が実際に出した候補を期待と比べるテスト。一致テストが内容を読んで比べるのに対し、候補そのものを見る", "source": "docs/specifications/cli-argument-resolution.md" + }, + { + "term": "利用の形", + "context": "secret", + "meaning": "OpenBao サーバの持ち方の 3 つの区分。個人の形(1 人で 1 台)・チームの形(チームで 1 台、チーム共通と個人の置き場を分ける)・既存のサーバの形(組織が運用するサーバへ接続する)", + "source": "", + "pending_source": "issues/issue-295-design.md" + }, + { + "term": "ポリシーの雛形", + "context": "secret", + "meaning": "devbase が前提にする権限を OpenBao のポリシー(HCL)として書いた、docs/user/openbao/policies/ のファイル。管理する人が自分のサーバへ書き込んで使う", + "source": "", + "pending_source": "issues/issue-295-design.md" + }, + { + "term": "試用の構成", + "context": "secret", + "meaning": "docs/user/openbao/compose.yml で開発モードの OpenBao を手元に立て、devbase の接続を試して片付けるまでの最小の構成", + "source": "", + "pending_source": "issues/issue-295-design.md" + }, + { + "term": "利用者の識別子", + "context": "secret", + "meaning": "secrets/backend.yml の openbao.user。サーバの entity 名と同じ値にし、個人単位の置き場のパスに使う", + "source": "", + "pending_source": "issues/issue-295-design.md" + }, + { + "term": "コンテナから見た接続先", + "context": "secret", + "meaning": "dev コンテナへ BAO_ADDR として渡す OpenBao の接続先。openbao.url が http の loopback でローカル扱いのときはホストを host.docker.internal に置き換えた値、それ以外(https を含む)は openbao.url のまま", + "source": "", + "pending_source": "issues/issue-295-design.md" + }, + { + "term": "管理する人", + "context": "secret", + "meaning": "利用者の OpenBao サーバで、マウント・AppRole・ポリシー・entity を作る人。個人の形では利用者本人", + "source": "", + "pending_source": "issues/issue-295-design.md" } ] } diff --git a/issues/issue-295-design.md b/issues/issue-295-design.md new file mode 100644 index 00000000..4da1d37a --- /dev/null +++ b/issues/issue-295-design.md @@ -0,0 +1,415 @@ +# OpenBao backend: 管理者のいない個人やチームは、サーバの立て方も devbase が前提にする権限も分からず使えない → 公開の文書とポリシーの雛形だけで自分のサーバを立てて接続でき、手元のサーバでもコンテナの中の `bao` が届く(#295) + +## 目的 + +- **何が壊れているか**: 利用者向けの文書と確定仕様が、ある組織の運用するサーバと非公開の運用の決まりを前提にしている。サーバの用意の手順も、devbase が前提にするポリシーも公開されていない。手元のサーバ(`http://127.0.0.1:8200`)では、コンテナへ渡す `BAO_ADDR` がコンテナ自身を指し、コンテナの中の `bao` が届かない +- **誰が困るか**: 管理者のいない個人と小さなチーム。既存のサーバへ接続している利用者は困っていない +- **直すと何が成り立つか**: 公開の文書と雛形だけで、試用の構成か自分のサーバを立て、`devbase env backend use openbao` から `devbase up` の機密の読み込みまで通せる。手元のサーバでも、dev コンテナの中の `bao kv get` が本人の置き場を読める + +## 適用範囲 + +- **働く範囲**: このリポジトリの利用者向けの文書・確定仕様・ポリシーの雛形・試用の構成と、`devbase up` / `devbase scale` が組む dev コンテナの構成(`BAO_ADDR` と `extra_hosts`)。プラグインのリポジトリは変えない +- **プロジェクトごとに違うもの**: 無し(接続先は端末ごとの `secrets/backend.yml`、グループは今どおりプロジェクトの `env` の宣言で受ける) +- **当たるモード**: `standard` + +## あるべき姿の根拠 + +| 根拠 | 種類 | 何を示すか | +| --- | --- | --- | +| 「OSS として多くの人が利用する場合は、利用者が個人・チームでそれぞれのサーバを立てる前提での運用が必要」(2026-09-26) | 利用者の指示の原文 | 文書と雛形をセルフホストの前提に揃える | +| 「個人の形も `version: 2` にそろえ、宣言するアカウントグループの名前は `personal` にする」「認証は AppRole だけにする」(2026-10-11) | 利用者の指示の原文 | 個人の形の既定と、token の認証を足さないこと | +| OpenBao 2.6.2 の開発モードのサーバで、決定 6・7 のポリシーを書き込み、`member01`・`member02` の 2 人で読み書きを打った(2026-10-12、macOS の Docker Desktop の端末) | 実測 | 他人の個人の置き場の読み取りが 403(`version: 1` / `2` とも)、権限の無い者のチーム共通への書き込みが 403、`version: 2` で別のグループの `team//` の読み取りが 403、`users/` 直下の一覧が 403、本人の置き場の未作成のパスが 404、`cas: 0` の書き込みが 200。`secret_id` を accessor で失効させると、その `secret_id` のログインが 400、同じ利用者の別の `secret_id` は通る | +| 同じ端末で、ホストの loopback(`127.0.0.1:18200`)で待つサーバへ、base イメージ(`devbase-base:latest`)のコンテナから `bao status` を打った | 実測 | `BAO_ADDR=http://127.0.0.1:18200` は届かない。`http://host.docker.internal:18200` は、`--add-host host.docker.internal:host-gateway` の有無によらず届く | +| 同じ端末で、公式イメージ `openbao/openbao:2.6.2` を compose の 1 ファイルで開発モードに起動した | 実測 | `docker compose exec openbao bao …` が、root token を画面に出さずに管理の操作を通す(開発モードのサーバがコンテナの中の `~/.vault-token` へ root token を書く)。ホストへ `127.0.0.1` だけで公開したポートへ、別のコンテナから `host.docker.internal` で届く | +| [OpenBao: AppRole auth](https://openbao.org/docs/auth/approle/) / [Identity](https://openbao.org/docs/concepts/identity/) / [Policies](https://openbao.org/docs/concepts/policies/) | 外部の一次情報 | AppRole の entity alias の名前は `role_id` である。ポリシーの `{{identity.entity.name}}` で本人のパスを絞れる | + +要求と受け入れ条件は #295 の本文にある(コピーは `issues/issue-295-requirements.md`)。この文書は「どう作るか」だけを扱う。 + +## ドメインモデル + +### コンテキスト + +| コンテキスト | 何の語が 1 つの意味に決まるか | +| --- | --- | +| 機密の置き場(`secret`) | 利用の形・ポリシーの雛形・試用の構成・利用者の識別子・コンテナから見た接続先 | +| docker の接続先(`docker-context`) | ローカル扱い・リモート扱い | + +機密の置き場が docker の接続先に順応する(順応者)。コンテナから見た接続先は、docker の接続先が決めた「ローカル扱いかどうか」をそのまま受け取り、自分では判定しない。 + +### 集約 + +| 集約 | 持ち主(書き換えてよいもの) | 根 | エンティティ | 値オブジェクト | +| --- | --- | --- | --- | --- | +| ポリシーの雛形 | `docs/user/openbao/policies/` のファイル | 雛形の組 | 個人の雛形・チームの読み取りの雛形・チームの書き込みの雛形(`version: 1` / `2`) | パスの規則・許す操作(capabilities)・グループの置き換え語 `__GROUP__` | +| 試用の構成 | `docs/user/openbao/compose.yml` | 試用のサーバ | — | OpenBao の版・公開するポート | +| dev コンテナの接続先 | dev 環境の組み立て(`commands/container.py` の `_bao_environment`) | dev サービスへ足す設定 | — | コンテナから見た接続先・`extra_hosts` の 1 行 | + +サーバの上の権限の構成(マウント・AppRole・entity・alias・ロール・`secret_id`)は利用者のサーバの持ち物で、devbase は持たない。文書は管理する人が打つ手順を示すだけで、devbase のコードはサーバの構成を書き換えない(前提 5)。 + +### 不変条件 + +| # | 集約 | 条件 | 破れたときの扱い | +| --- | --- | --- | --- | +| I1 | ポリシーの雛形 | 個人の雛形は、本人(`{{identity.entity.name}}`)の `users/<本人>/` の下だけを読み書きさせる。他人の `users/<名前>/` と `users/` 直下の一覧は許さない | 他人の置き場の読み取りが通る → 実機の確認(受け入れ条件 14)で落ちる | +| I2 | ポリシーの雛形 | チームの読み取りの雛形は読み取りだけを許す。チーム共通へ書けるのは、書き込みの雛形を付けた者だけ | 書き込みの雛形の無い者の書き込みが通る → 受け入れ条件 14 で落ちる | +| I3 | ポリシーの雛形 | `version: 2` の雛形は、`__GROUP__` を置き換えたグループの `team//` の下だけに効く | 別のグループの `team/` が読める → 受け入れ条件 14 で落ちる | +| I4 | ポリシーの雛形 | どの雛形も `sys/` と `auth/` と `identity/` の経路を含まず、KV v2 のマウント `devbase` の `data/` と `metadata/` の下だけを書く。特定の組織の名前を含まない | 雛形の検査(テスト設計)で落ちる | +| I5 | 試用の構成 | OpenBao の版は `compose.yml` の `image` の 1 行だけにあり、base イメージの `BAO_VERSION` と同じ 2.6 系である | 雛形の検査で落ちる | +| I6 | 試用の構成 | サーバのポートはホストの loopback(`127.0.0.1`)にだけ公開する | 雛形の検査で落ちる | +| I7 | dev コンテナの接続先 | `openbao.url` のスキームが `http` でホストが loopback で、docker の接続先がローカル扱いのときだけ、コンテナから見た接続先のホストを `host.docker.internal` に置き換える。スキーム・ポートは保つ。それ以外(`https` を含む)は `openbao.url` のまま | pytest で落ちる | +| I8 | dev コンテナの接続先 | 置き換えたときだけ、dev のインスタンスへ `host.docker.internal:host-gateway` の 1 行を足す。元の dev サービスが `host.docker.internal` の行を持っていれば足さない | pytest で落ちる | +| I9 | dev コンテナの接続先 | ホストの devbase が使う URL(`openbao.url`)・URL の検査・`secrets/backend.yml` の形は変わらない | pytest で落ちる(既存のテストが変わらず通る) | +| I10 | 試用の構成・文書 | 文書の例に実の token・`secret_id`・root token の値を書かず、`secret_id` をコマンド引数で渡す手順と、devbase の接続に root token を使う手順を書かない | 文書の検査(テスト設計)で落ちる | + +### ドメインイベント + +| # | イベント | 発生元 | 受け手 | +| --- | --- | --- | --- | +| E1 | 利用者が利用の形を選んだ | 利用者(サーバの用意の文書の冒頭の表) | 選んだ形の節 | +| E2 | 利用者が OpenBao サーバを起動した | 試用の構成の `docker compose up` か、利用者の自分のサーバ | E3 の手順 | +| E3 | 管理する人が KV v2 を既定の `devbase` にマウントした | `bao secrets enable` | E4 の手順 | +| E4 | 管理する人が AppRole を有効にし、雛形のポリシーを書き込んだ | `bao auth enable` / `bao policy write` | E5 の手順 | +| E5 | 管理する人が利用者の entity・alias・ロールを作った | `bao write identity/entity` ほか | E6 の手順 | +| E6 | 管理する人が端末の `secret_id` を発行した | `bao write … /secret-id` | E7(`--secret-id-stdin` へ直に渡す) | +| E7 | 利用者が `devbase env backend use openbao` で接続した | 利用者 | `secrets/backend.yml` / `secrets/bootstrap.env.age` | +| E8 | 利用者がプロジェクトで `DEVBASE_ACCOUNT_GROUP=personal` を宣言した | 利用者 | 機密のコマンドと `up` のグループの決定(今の振る舞い) | +| E9 | 利用者が `env set` / `env get` で機密を読み書きした | 利用者 | サーバ(ポリシーが通すか拒む) | +| E10 | `devbase up` がコンテナへ機密を入れた | `devbase up` | dev コンテナの環境変数と、コンテナから見た接続先(I7・I8) | +| E11 | コンテナの中の `bao` がサーバへ届いた | コンテナの中の利用者 | サーバ | +| E12 | 管理する人が端末の `secret_id` を失効させた | `bao write … /secret-id-accessor/destroy` | その端末の次のログイン(400 で止まる) | +| E13 | 利用者が試用の構成を片付けた | 利用者 | 試用のサーバ(消える)と、端末の backend の設定(元へ戻る) | + +### 用語 + +| 用語 | 意味 | 用語集への反映 | +| --- | --- | --- | +| 利用の形 | OpenBao サーバの持ち方の 3 つの区分。個人の形・チームの形・既存のサーバの形 | 意味の変更(出所を確定仕様へ) | +| ポリシーの雛形 | devbase が前提にする権限を OpenBao のポリシー(HCL)として書いた、`docs/user/openbao/policies/` のファイル | 意味の変更(置き場と出所を確定する) | +| 試用の構成 | `docs/user/openbao/compose.yml` で開発モードの OpenBao を手元に立て、接続を試して片付けるまでの最小の構成 | 意味の変更(置き場と出所を確定する) | +| 利用者の識別子 | `secrets/backend.yml` の `openbao.user`。サーバの entity 名と同じ値にし、個人単位の置き場のパスに使う | 追加 | +| コンテナから見た接続先 | dev コンテナへ `BAO_ADDR` として渡す接続先。`openbao.url` が `http` の loopback でローカル扱いのときはホストを `host.docker.internal` に置き換えた値、それ以外(`https` を含む)は `openbao.url` のまま | 追加 | +| 管理する人 | 利用者のサーバで、マウント・AppRole・ポリシー・entity を作る人。個人の形では利用者本人 | 追加 | + +## 機能一覧 + +| # | 機能 | 誰が使うか | +| --- | --- | --- | +| F1 | 利用の形の 3 つの違いと、形ごとに用意するものを読み、形を選ぶ | 利用者 | +| F2 | 試用の構成でサーバを手元に立て、接続を試し、片付ける | 個人の利用者 | +| F3 | サーバへ KV v2・AppRole・ポリシー・entity・alias・ロールを用意し、端末の `secret_id` を発行して接続する | 管理する人(個人の形では本人) | +| F4 | 利用者と端末を足し、チーム共通への書き込みを許し、端末 1 台の `secret_id` を失効させる | チームの管理する人 | +| F5 | ポリシーの雛形(`version: 1` / `2`)をサーバへ書き込む | 管理する人 | +| F6 | 既存のサーバへ、管理者から受け取った値で接続する | 既存のサーバの利用者 | +| F7 | 手元のサーバ(loopback の `openbao.url`)でも、dev コンテナの中の `bao` がサーバへ届く | 個人の利用者 | +| F8 | devbase がサーバに求める条件を確定仕様で読む | 管理する人・開発者 | + +## 構成要素 + +| 要素 | 責務 | 機能 | +| --- | --- | --- | +| サーバの用意の文書(`docs/user/openbao-server.md`、新設) | 利用の形の 3 つを見出しで分け、形ごとの用意・試用の構成・管理の手順・片付けを示す | F1〜F6 | +| 機密の保存先の文書(`docs/user/env-backend.md`) | 接続する側の手順だけを持つ。「OpenBao 側で用意するもの」をサーバの用意の文書への案内と受け取る値の表に変え、管理者から受け取るのは既存のサーバの形だけにする。コンテナから見た接続先を書く | F1・F6・F7 | +| ポリシーの雛形(`docs/user/openbao/policies/`、新設) | 個人・チームの読み取り・チームの書き込みの権限を HCL で持つ | F5 | +| 試用の構成(`docs/user/openbao/compose.yml`、新設) | 開発モードの OpenBao を、版を 1 か所に書いて loopback にだけ公開して立てる | F2 | +| dev 環境の組み立て(`commands/container.py` の `_bao_environment`) | コンテナから見た接続先を決め、置き換えたかを返す | F7 | +| スケール構成の生成(`volume/compose.py`) | 受け取った `extra_hosts` の行を、元の行と重ねずに dev のインスタンスへ足す | F7 | +| 確定仕様(`docs/specifications/secret-backend.md`) | サーバに求める条件・雛形の置き場・利用者の識別子・コンテナから見た接続先を持つ。非公開のリポジトリへの参照を外す | F8 | +| 文書の目次(`docs/README.md`)と `CHANGELOG.md` | 新しい文書への入口と、利用者に見える変更の記入 | F1 | + +文脈(外部との関係): + +```mermaid +graph LR + 利用者 --> 文書[devbase の文書と雛形] + 管理する人 --> 文書 + 管理する人 -->|bao の管理の操作| サーバ[利用者の OpenBao サーバ] + 利用者 -->|devbase env / up| CLI[devbase] + CLI -->|AppRole のログインと KV v2| サーバ + CLI -->|compose の構成| Docker[Docker Engine / Desktop] +``` + +OpenBao サーバと Docker は devbase が変えられない外部である。文書・雛形・試用の構成・`devbase` は同じ変更で変える内部である。 + +構成要素: + +```mermaid +graph TD + subgraph 文書と雛形 + G[サーバの用意の文書] + B[機密の保存先の文書] + P[ポリシーの雛形] + T[試用の構成] + S[確定仕様] + end + subgraph devbase の up と scale + E[dev 環境の組み立て] + C[スケール構成の生成] + end + G --> P + G --> T + G --> B + B --> G + S --> P + E -->|コンテナから見た接続先と extra_hosts| C +``` + +配置(手元のサーバ、ローカル扱い): + +```mermaid +graph TD + subgraph 利用者の端末 + CLI[devbase] + subgraph Docker + BAO[試用のサーバ 0.0.0.0:8200] + DEV[dev コンテナ] + end + end + CLI -->|http://127.0.0.1:8200 公開したポート| BAO + DEV -->|http://host.docker.internal:8200| BAO + CLI -->|docker exec で token 1 行| DEV +``` + +境界をまたぐのは、ホストからサーバへの AppRole のログインと KV v2 の要求(`role_id` / `secret_id` / token)、ホストからコンテナへの 1 時間の token(今と同じ `~/.vault-token`)、コンテナからサーバへの token 付きの要求である。`secret_id` はホストから出ない(今と同じ)。 + +置き場所(新設は `+`、変更は `~`): + +```text +docs/ +├── README.md ~ 新しい文書を目次に足す +├── specifications/secret-backend.md ~ サーバに求める条件・コンテナから見た接続先 +└── user/ + ├── env-backend.md ~ 接続する側だけにする + ├── openbao-server.md + サーバの用意(利用の形の 3 つ) + └── openbao/ + ├── compose.yml + 試用の構成 + └── policies/ + ├── devbase-user.hcl + 個人の雛形(version 1 / 2 共通) + ├── v1/team-read.hcl + + ├── v1/team-write.hcl + + ├── v2/team-read.hcl + __GROUP__ を置き換えて使う + └── v2/team-write.hcl + +lib/devbase/ +├── commands/container.py ~ _bao_environment +└── volume/compose.py ~ extra_hosts を dev のインスタンスへ足す +tests/ +├── commands/test_container_bao.py ~ +├── volume/ ~ 生成のテスト(既存のファイルへ足す) +└── docs/test_openbao_templates.py + 雛形と試用の構成の検査 +CHANGELOG.md ~ [Unreleased] +``` + +## 構造 + +型は足さず、変えない。`_bao_environment` は `BAO_ADDR` を返す今の役目に、置き換えたときの `extra_hosts` の 1 行を返す役目を足し、スケール構成の生成がその行を dev のインスタンスへ足す(入出力の契約の表)。返し方の形は実装で決める。読む型は次の 2 つだけである。 + +```mermaid +classDiagram + class OpenBaoSettings { + url: str + } + class DockerTarget { + remote: bool + } + class _bao_environment { + <> + } + _bao_environment ..> OpenBaoSettings : url を読む + _bao_environment ..> DockerTarget : remote を読む +``` + +## 入出力の契約 + +コンテナへ渡す設定(`devbase up` / `devbase scale` の dev のインスタンス): + +| `openbao.url` | docker の接続先 | `BAO_ADDR` | dev のインスタンスへ足す `extra_hosts` | +| --- | --- | --- | --- | +| `http://127.0.0.1:8200` | ローカル扱い | `http://host.docker.internal:8200` | `host.docker.internal:host-gateway` | +| `http://localhost:8200` / `http://[::1]:8200` | ローカル扱い | `http://host.docker.internal:8200` | 同上 | +| `http://localhost`(ポートなし) | ローカル扱い | `http://host.docker.internal` | 同上 | +| loopback のどれか | リモート扱い | `openbao.url` のまま | 足さない | +| `https://localhost:8200` など `https` の loopback | どちらでも | `openbao.url` のまま | 足さない | +| `https://openbao.example.com` | どちらでも | `openbao.url` のまま | 足さない | +| backend が `openbao` でない | — | 渡さない(今と同じ) | 足さない | + +- 元の dev サービスの `extra_hosts` は、リストの形(`"name:ip"`)と辞書の形(`name: ip`)の両方を受け、元の形を保って足す。`host.docker.internal` の行が既にあれば、どちらの形でも足さない(I8) +- コンテナから見た接続先は `up` のたびに組み直す。今動いているコンテナは、次の `up` で新しい `BAO_ADDR` になる +- `devbase env token` と `project post-start` が書く `~/.vault-token` は変えない + +ポリシーの雛形と試用の構成の約束(文書が指すもの): + +| もの | 約束 | +| --- | --- | +| 雛形のファイル名 | 上の置き場所のツリーの 5 つ。書き込むポリシーの名前は文書が決める(個人 `devbase-user`、`version: 1` は `devbase-team-read` / `devbase-team-write`、`version: 2` は `devbase-team-read-` / `devbase-team-write-`) | +| 置き換え語 | `version: 2` の雛形の `__GROUP__` だけ。`sed 's/__GROUP__/personal/g'` の 1 回で使える形にする | +| マウント名 | 雛形は `devbase` を書く。ほかのマウント名を使う管理する人は、同じく `sed` で置き換える(文書に書く) | +| 試用の構成の名前 | compose のプロジェクト名 `devbase-openbao-trial`、サービス名 `openbao`、ホストのポート `127.0.0.1:8200` | + +## 処理の流れ + +試用の構成で個人の形を通す流れ(サーバの用意の文書の手順の順序): + +```mermaid +sequenceDiagram + actor U as 利用者 + participant T as 試用のサーバ + participant D as devbase + participant C as dev コンテナ + U->>T: docker compose up -d と bao status + U->>T: secrets enable と auth enable approle + U->>T: policy write(個人と personal のチームの書き込み) + U->>T: ロールと entity と alias(名前は role_id) + U->>D: 既存の backend.yml と bootstrap.env.age を退避 + U->>T: secret_id を発行 + T-->>D: 標準入力で backend use openbao --secret-id-stdin + U->>D: プロジェクトの env に DEVBASE_ACCOUNT_GROUP=personal + U->>D: env set と env get + D->>T: AppRole のログインと KV v2 + U->>D: devbase up + D->>C: 機密と BAO_ADDR(host.docker.internal)と token + U->>C: bao kv get users/<本人>/personal/global + C->>T: token 付きの要求 + U->>T: docker compose down + U->>D: 退避した 2 つを戻すか backend use auto +``` + +`devbase up` / `scale` でコンテナから見た接続先を決める流れ: + +```mermaid +graph TD + subgraph dev 環境の組み立て + A[backend が openbao か] -->|いいえ| N[何も足さない] + A -->|はい| B[openbao.url が http でホストが loopback か] + B -->|いいえ| K[BAO_ADDR = openbao.url] + B -->|はい| R[docker の接続先がリモート扱いか] + R -->|はい| K + R -->|いいえ| H[BAO_ADDR のホストを host.docker.internal に置き換え] + end + subgraph スケール構成の生成 + X[元の dev サービスに host.docker.internal の行があるか] + X -->|ある| G[extra_hosts は足さない] + X -->|無い| Y[dev のインスタンスへ host-gateway の 1 行を足す] + end + H --> X +``` + +1 枚目は、サーバの用意の文書が並べる手順を、試用の構成・ポリシーの雛形・機密の保存先の文書(接続の手順)の順にたどる。確定仕様・文書の目次・`CHANGELOG.md` は処理に現れないため、どちらの図にも含めない。 + +## 非機能の実現方式 + +| 大項目 | 要求の条件 | 実現方式 | 確かめ方 | +| --- | --- | --- | --- | +| セキュリティ | ポリシーの雛形は、受け入れ条件 12 に並べた権限のほかを許さない(本人以外の個人の置き場・`sys/` の管理の経路を利用者のポリシーに含めない) | 雛形は `devbase/data/` と `devbase/metadata/` の下だけを書く。個人の雛形は `users/{{identity.entity.name}}/` の下に限り、チームの読み取りの雛形は `read` だけにする。利用者のロールの `token_policies` は `default` だけにし、雛形は entity へ付ける | 雛形の検査(パスの接頭辞と capabilities)と、実機の 2 人の読み書き(受け入れ条件 14) | +| セキュリティ | 文書の例に実の token・`secret_id`・root token の値を書かない。devbase の接続に root token を使う手順を示さない。`secret_id` をコマンド引数で渡す手順を示さない | 試用の構成は root token を決めず、管理の操作はサーバのコンテナの中の `bao`(開発モードが書いた `~/.vault-token`)で打つ。`secret_id` は発行のコマンドの出力を `devbase env backend use … --secret-id-stdin` へパイプで渡し、画面にも引数にも出さない | 文書の検査(`--secret-id ` の後に値を置く形・`root` の token の値・`BAO_TOKEN=` の値の無いこと)と、レビュー | +| 運用・保守性 | 雛形と試用の構成は OpenBao の版を 1 か所に書き、base イメージの `BAO_VERSION` と同じ 2.6 系に揃える | 版は `compose.yml` の `image: openbao/openbao:<版>` の 1 行だけに書き、文書は版を写さずその行を指す。雛形は版を持たない | 雛形の検査が、`image` の版と `containers/base/Dockerfile` の `ARG BAO_VERSION` の 2 つ目までの数字を比べる | +| システム環境 | 試用の構成は macOS の Docker Desktop と Linux の Docker の両方で動く手順を示す。片方でしか確かめていなければ、文書に確かめた環境を書く | 試用の構成は Docker と compose だけを使い、ホストに `bao` を求めない。コンテナの中の `bao` の到達は、Linux の Docker では、ホストの loopback にだけ公開したポートへ `host-gateway` から届かない見込みのため、文書に「確かめた環境」と「Linux の Docker での扱い」を分けて書く(未確認のまま残ること) | 実装で Linux の Docker の端末を使えれば試用の構成とコンテナの中の `bao` を打つ。使えなければ文書に確かめた環境(macOS の Docker Desktop)を書く | + +## 決定の記録 + +### 決定 1: 雛形を文書から相対リンクで開けるようにするため、ポリシーの雛形と試用の構成を `docs/user/openbao/` に置く + +雛形と試用の構成はサーバの側で使うもので、devbase の実行時には読まない。`install.sh` は `git clone` でリポジトリ全体を配るため、`docs/` に置いても利用者の `$DEVBASE_ROOT` に届き、手順は `$DEVBASE_ROOT/docs/user/openbao/…` を指せる。GitHub の上でも文書から相対リンクで開ける。 + +`etc/` は devbase が実行時に使う補完のファイルの置き場で、サーバへ書き込むファイルを混ぜると「devbase が読むもの」の境界が崩れるため採らない。 + +根拠: Mission / Value 1(MVV 版 1) + +### 決定 2: 接続する側とサーバを用意する側の読み手を分けるため、サーバの用意を新しい文書 1 本にまとめ、利用の形の 3 つを見出しで分ける + +`docs/user/openbao-server.md` を新しく置き、冒頭に利用の形の 3 つ(個人・チーム・既存のサーバ)の違いと用意するものの表、続けて形ごとの見出しを置く。個人とチームの手順は同じ管理のコマンドを共有し、チームの節は利用者と端末を足す・書き込みを許す・失効させる手順だけを足す。`env-backend.md`(619 行)は接続する側の手順に絞り、「OpenBao 側で用意するもの」はこの文書への案内と受け取る値の表にする。 + +形ごとに 3 本へ分けると、個人とチームで同じ管理のコマンドを 2 か所に写すことになる。`env-backend.md` へ足すと、接続する利用者がサーバの構築の手順を読み飛ばす分量になる。 + +根拠: Mission(MVV 版 1) + +### 決定 3: 公開インタフェースを増やさないため、雛形を書き出す副命令を足さず、文書と雛形のファイルだけにする + +雛形は `$DEVBASE_ROOT/docs/user/openbao/` に既にあり、管理する人は `bao policy write <名前> - < <ファイル>` で書き込める。副命令が代わりにできるのはファイルの写しだけで、サーバの構成は管理する人の `bao` が書く(前提 5)。 + +副命令を足すと、既存のサーバの形の利用者にも見えるコマンドが増え、雛形の版と devbase の版の組み合わせを持つことになる。 + +根拠: Mission / Value 1(MVV 版 1) + +### 決定 4: 手元のサーバでもコンテナの中の `bao` を使えるようにするため、`http` の loopback の `openbao.url` はローカル扱いのときだけ、コンテナへ渡す `BAO_ADDR` のホストを `host.docker.internal` に置き換え、`host-gateway` の行を足す + +コンテナの中の `127.0.0.1` はコンテナ自身を指し、loopback の `openbao.url` をそのまま渡すと届かない。Docker Desktop で `host.docker.internal` がホストの loopback のサーバへ届くことを実測した(あるべき姿の根拠)。`host-gateway` の行は Linux の Docker で `host.docker.internal` を引けるようにするためで、Docker Desktop でも行の有無によらず届いた。リモート扱いでは loopback がホストの端末を指し、コンテナのある端末から届かないため置き換えない。`https` の loopback は置き換えない。証明書の名前が `host.docker.internal` と合わず、TLS の検証で落ちるためである(受け入れ条件 16)。ホストの devbase の URL と検査は変えない(I9)。 + +文書で「コンテナの中では `export BAO_ADDR=…` を打つ」と案内する形は、シェルを開くたびに打つことになり、`devbase up` だけで使える今の形と揃わない。「使えない」と書く形は、試用の構成で dev コンテナの `bao` を確かめられない。 + +根拠: Mission / Vision(MVV 版 1) + +### 決定 5: ホストに `bao` を入れずに試せるようにするため、試用の構成を compose の 1 ファイルにし、管理の操作をサーバのコンテナの中の `bao` で打つ + +`openbao/openbao:<版>` を開発モードで起動し、ポートを `127.0.0.1:8200` にだけ公開する。管理の操作は `docker compose … exec -T openbao bao …` を包むシェル関数(文書では `bao_admin`)で打つ。開発モードのサーバがコンテナの中に root token を置くため、root token を画面にも文書にも出さずに済む。自分のサーバの節では同じ関数を `bao` の呼び出しに替えるだけで、以後の手順を共有する。 + +`bao server -dev` をホストで起動する形は、ホストへ `bao` を入れる手順と、root token を画面から写す手順が要る。`compose.yml` の `image` の 1 行に版を書けば、版を 1 か所に保てる(I5)。 + +根拠: Value 1 / Mission(MVV 版 1) + +### 決定 6: 利用者ごとの権限を他の利用者の設定に触れずに変えられるようにするため、雛形のポリシーを利用者の entity へ直に付け、identity group を使わない + +利用者 1 人につき、AppRole のロール(`token_policies=default`)・entity(名前は利用者の識別子、`policies` に雛形のポリシー)・entity alias(名前は `role_id`、AppRole のマウントの accessor)を作る。チーム共通への書き込みを許すのは、その利用者の entity の `policies` に書き込みのポリシーを足すことである。個人の形も同じ手順で、`personal` の書き込みのポリシーを付ける。 + +identity group で束ねると、メンバーの一覧を書き直す操作が他の利用者の権限を巻き込み、1 人を足すときに既存のメンバーを落とし得る。ロールの `token_policies` に付ける形でも権限は効くが、1 人の権限がロールと entity の 2 か所に分かれ、チーム共通への書き込みを誰に許したかを entity の一覧だけで読めなくなる。 + +根拠: Value 3(MVV 版 1) + +### 決定 7: `version: 2` の雛形をグループ名の書き換えだけで使えるようにするため、グループごとのポリシーを置き換え語 `__GROUP__` から作る + +OpenBao のポリシーのテンプレートは、entity の属するグループを列挙してパスへ入れる書き方を持たない。`team/__GROUP__/` を書いた雛形を `sed` で置き換え、`devbase-team-read-` / `devbase-team-write-` として書き込む。個人の雛形は `users/{{identity.entity.name}}/` の下を許すため、`version: 1` と `2` で 1 つを共有する。 + +entity の metadata にグループ名を持たせて `{{identity.entity.metadata.…}}` で組む形は、1 人が複数のグループに属せず、チームの形で 2 つのグループを持つ利用者を表せない。 + +根拠: Value 3(MVV 版 1) + +### 決定 8: 試用で既存の接続設定を失わないため、試用の前に `secrets/backend.yml` と `secrets/bootstrap.env.age` を退避し、片付けで戻す + +`devbase env backend use openbao` は 2 つのファイルを上書きする。既存のサーバの形の利用者が試用の構成を試すと、今の接続設定と資格情報を失う。片付けは、退避があれば戻し、無ければ `devbase env backend use auto` で設定なしへ戻して試用の `bootstrap.env.age` を消す。どちらも最後に `devbase env backend status` で OpenBao を指していないことを確かめる(受け入れ条件 7)。手元のキャッシュ(`secrets/cache/`)は、退避を戻すときは消さない(元の接続の控えが入っており、試用の控えは接続先と `role_id` の指紋が違うため元の接続では使われない)。`use auto` で戻すときは、控えが試用のものだけなので丸ごと消す。 + +試用を別の `DEVBASE_ROOT` で行う形は、作業用のプロジェクトとプラグインを別に用意することになり、数分で試せる構成にならない。 + +根拠: Value 2 / Value 3(MVV 版 1) + +### 決定 9: 失効させる端末を `secret_id` の値を持たずに選べるようにするため、端末の名前を `secret_id` の metadata に入れて発行し、失効は accessor で行う + +発行のときに `metadata='{"terminal":"<端末の名前>"}'` を付け、失効は `secret-id` の一覧(accessor)と `secret-id-accessor/lookup` で端末の名前を確かめてから `secret-id-accessor/destroy` で行う。管理する人は発行した `secret_id` の値を控えずに済む。 + +`secret_id` の値で失効させる形は、管理する人が全端末の `secret_id` を保管することになり、漏れたときの範囲が広がる。 + +根拠: Value 3(MVV 版 1) + +## テスト設計 + +| 受け入れ条件・不変条件 | どの振る舞いで縛るか | どう壊したら落ちるべきか | +| --- | --- | --- | +| 受け入れ条件 1 | サーバの用意の文書に、個人・チーム・既存のサーバの 3 つの見出しと、形ごとに用意するものの表がある(レビュー) | 見出しを 2 つにまとめると落ちる | +| 受け入れ条件 2 | 個人とチームの手順が、マウント・AppRole・ポリシーの書き込み・entity と alias・ロール・`secret_id` を `bao` のコマンドで順に並べ、実機で上から打って通る(手動確認) | entity の名前を利用者の識別子(`--user`)と違えて作ると、本人の置き場が 403 になって打鍵で止まる | +| 受け入れ条件 3 | チームの節の、利用者と端末を足す・失効させる手順が実機で通る(手動確認。受け入れ条件 15 と同じ打鍵) | accessor の取り違えで別の端末が失効すると落ちる | +| 受け入れ条件 4・I4・I5・I6 | 雛形と試用の構成の検査(pytest): 5 つの雛形がある、どの `path` も `devbase/data/` か `devbase/metadata/` で始まる、`sys/` `auth/` `identity/` を含まない、`compose.yml` の版が `BAO_VERSION` と 2 つ目まで同じ、ポートが `127.0.0.1:` で始まる。`bao policy write` で拒まれないことは実機(手動確認) | `path "sys/*"` を足す、`image` の版を 2.5 にする、ポートを `8200:8200` にすると落ちる | +| 受け入れ条件 5・I3 | `version: 2` の雛形の `__GROUP__` を `personal` に置き換えると、`team/personal/` の下だけを指す(pytest)。組織の名前が無いことは固有の語の検査 | `team/*` を書くと落ちる | +| 受け入れ条件 6・7 | 試用の構成の起動・確かめ(`bao status`)・片付けの後の `devbase env backend status` が OpenBao を指さないことを、作業用の端末で通す(手動確認) | 片付けで退避を戻し忘れると、`status` が試用の URL を指して落ちる | +| 受け入れ条件 8・I7・I8・I9 | `_bao_environment` と生成した構成(pytest): 入出力の契約の表の各行、`extra_hosts` のリストと辞書の形、既にある `host.docker.internal` の行。実機ではコンテナの中の `bao kv get` が本人の個人共通を読める(手動確認) | 置き換えを常に行う・リモート扱いでも行う・`https` でも行う・行を重ねて足す・`openbao.url` 自体を書き換えると落ちる | +| 受け入れ条件 9・10・11 | `grep -rn '社内メールアドレス\|運用側のリポジトリ' docs/user docs/specifications/secret-backend.md` が 0 件。`env-backend.md` の「管理者から」が既存のサーバの形の記述だけにある(レビュー) | 語を 1 つ残すと grep が当たる | +| 受け入れ条件 12 | 確定仕様の「OpenBao との契約」に、サーバに求める条件の表と雛形の置き場がある(レビュー) | 条件の 1 つ(`users/` 直下の一覧の拒否など)を落とすとレビューで落ちる | +| 受け入れ条件 13 | 作業用のプロジェクトで、試用の構成から `env set` → `env get` → `devbase up` を通し、コンテナの環境変数に値がある(手動確認。非対話で base を建てられなければ「判断が要る」で返す) | 文書の手順のどこかが欠けると打鍵が止まる | +| 受け入れ条件 14・I1・I2 | `version: 2` と `version: 1` の雛形を書き込んだサーバで、`member01`・`member02` の読み書きが 403 / 通る の組になる(手動確認) | 個人の雛形の `{{identity.entity.name}}` を `*` にする、読み取りの雛形に `update` を足すと落ちる | +| 受け入れ条件 15 | `member02` の端末 A の `secret_id` を accessor で失効させると、A の `devbase env backend test` がログインの失敗で止まり、端末 B は通る(手動確認) | ロールごと消すと B も止まって落ちる | +| 受け入れ条件 16 | `https` の `openbao.url` で、生成した構成の `BAO_ADDR` と `extra_hosts` が今と同じ(pytest)。`env-backend.md` の接続の手順のコマンドと引数が変わらない(レビュー) | `https` の URL を書き換えると落ちる | +| 受け入れ条件 17・18 | `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` と、語の一覧のある端末での `proper_term_check.py` | — | +| I10 | 文書の検査(pytest): サーバの用意の文書と `env-backend.md` に、`--secret-id` の後に値を置く形・`BAO_TOKEN=` に値を置く形・`root` の token の値が無い | 手順に `--secret-id xxxx` を書くと落ちる | + +## 設計の結果 + +| 課題 | 扱い | 取り込み先 | 触るファイル | +| --- | --- | --- | --- | +| #295 | 実装する | — | `docs/user/openbao-server.md`、`docs/user/openbao/`、`docs/user/env-backend.md`、`docs/specifications/secret-backend.md`、`docs/README.md`、`docs/glossary/glossary.json`、`docs/glossary.md`、`lib/devbase/commands/container.py`、`lib/devbase/volume/compose.py`、`tests/commands/test_container_bao.py`、`tests/volume/`、`tests/docs/`、`CHANGELOG.md` | + +確定仕様(`secret-backend.md`)で書き直す箇所は、393 行(グループのポリシーの持ち主)・922 行(利用者の識別子)・926-929 行(サーバ側の構成)・974 行(`bao` を入れる理由の「管理スクリプト」)・「コンテナの中の `bao`」の `BAO_ADDR` の行・1418-1420 行(運用の「範囲外」)・1652 行(関連リンク)と、「テスト観点」の手動確認の行である。`env-backend.md` は 36-47 行・133-134 行・「コンテナの中から `bao` を使う」の表・578 行の注釈・607 行を書き直す。 + +## 未確認のまま残ること + +| 項目 | 内容 | +| --- | --- | +| Linux の Docker でのコンテナの中の `bao` | 試用の構成はポートをホストの `127.0.0.1` にだけ公開する。Linux の Docker では、コンテナから `host-gateway`(ブリッジのゲートウェイ)宛ての要求が、`127.0.0.1` にだけ公開したポートへ届かない見込みである。実装で Linux の Docker の端末を使えれば確かめ、届かなければ文書に「Linux の Docker では試用の構成でコンテナの中の `bao` は使えない。使うにはサーバをブリッジから届くアドレスでも待たせる」と書く。使えなければ確かめた環境(macOS の Docker Desktop)を文書に書く | +| `network_mode: host` の dev サービス | 手元のプラグインに `network_mode` を使う dev サービスは無い(`repos/*/` を探した。プラグインの中のプロジェクトのコードの compose は devbase が生成しない)。Linux でこの形の dev サービスでは loopback がそのまま届くが、置き換えで `host.docker.internal` 宛てになる。実装で `network_mode: host` の dev サービスには置き換えないかを、生成の側で判定できるか確かめる | +| 本番のサーバの構築 | 冗長化・バックアップ・監査ログ・TLS の証明書・seal の扱いは範囲外で、OpenBao の公式の文書へのリンクにとどめる。自分のサーバの節は、起動と初期化が済んだサーバを入口の前提にする | +| `secret_id` の受け渡しの経路 | チームの形で、管理する人が発行した `secret_id` を利用者へ渡す経路(応答の包み込み・対面など)は文書の範囲外とし、「安全な経路で渡す」とだけ書く | diff --git a/issues/issue-295-requirements.md b/issues/issue-295-requirements.md new file mode 100644 index 00000000..f75db232 --- /dev/null +++ b/issues/issue-295-requirements.md @@ -0,0 +1,244 @@ +# #295: OpenBao backend を、利用者が個人・チームで自分のサーバを立てて使う前提の文書と雛形に揃える + +正は課題の本文(#295)で、この文書はその写しである。`spec-copy.py write` で作り直す。手で直さない。 + +## 何をしたいか + +devbase の OpenBao backend を、**利用者が個人またはチームで自分の OpenBao サーバを立てて使う**前提で使えるようにする。 + +今の文書と設計は、ある 1 つの組織が運用しているサーバへ接続することを前提に書かれている。OSS として多くの人が使うなら、サーバの用意から devbase の接続までを、利用者が公開の情報だけで再現できる必要がある。 + +## 今どうなっているか + +`main` = `2ee7e27`(2026-10-06)で確認した。 + +| 場所 | 現状 | OSS の利用者にとっての問題 | +| --- | --- | --- | +| `docs/user/env-backend.md:34-48`「OpenBao 側で用意するもの」 | 「サーバの構築と運用は devbase の範囲外です。管理者から次の値を受け取ってください」 | 管理者のいない個人や小さなチームは、何を用意すればよいか分からない | +| `docs/user/env-backend.md:42`、`docs/specifications/secret-backend.md:922` | 利用者の識別子を「社内メールアドレスの `@` より前」と説明している | 特定の組織の運用の決まりで、一般の前提ではない | +| `docs/specifications/secret-backend.md:393,926-929,1418,1652` | サーバ側の構成(KV v2 のマウント、ポリシー、AppRole、token の期限)は「運用側のリポジトリ」が持つとし、サーバの構築・運用は devbase の範囲外としている | devbase が前提にしているポリシー(本人だけが `users//` を読み書きできる、チーム共通は書き込み権限を持つ人だけ、`version: 2` ではグループ単位)が公開されておらず、再現できない | +| `lib/devbase/env/backend_config.py:126-141` | URL は `https`、または loopback 宛ての `http` だけを受け付ける | 個人で `bao server -dev` を手元で動かす形は通る。ただし下の「確かめること」のとおり、コンテナの中からは同じ URL で届かない疑いがある | +| `lib/devbase/cli.py:522-546`、`lib/devbase/env/openbao.py` | 認証は AppRole(`role_id` / 端末ごとの `secret_id`)だけ | 個人の 1 台の利用でも、AppRole の有効化・ロール・ポリシーを自分で組む必要がある。手順が無い | + +## 提案 + +| 項目 | 内容 | +| --- | --- | +| 利用の形 | 次の 3 つを文書で分けて示す。(1) **個人**: 手元または自分のサーバに 1 台立てて 1 人で使う。(2) **チーム**: チームで 1 台立て、チーム共通と個人の置き場を分ける。(3) **既存のサーバ**: 組織が運用しているサーバへ接続する(今の文書の形) | +| サーバの用意の手順 | KV v2 のマウント(既定 `devbase`)、AppRole の有効化、利用者ごとのロールと端末ごとの `secret_id` の発行、entity と alias の作成を、`bao` のコマンドで順に示す | +| ポリシーの雛形 | devbase が前提にする権限(本人の `users//`、チーム共通の読み取りと書き込み、`version: 2` のグループ単位の `team//…`。例: `team/personal/…`)を HCL の雛形としてリポジトリに置く(例 `docs/user/openbao/` や `etc/openbao/`)。`version: 1` と `version: 2` の両方 | +| 最小の試し方 | 個人が数分で試せる構成(`bao server -dev` か、docker compose の 1 ファイル)と、終わったら片付ける手順 | +| 利用者の識別子 | 「社内メールアドレス」ではなく「サーバの entity 名と同じ識別子」と説明する | +| 前提の書き直し | 確定仕様から非公開のリポジトリへの参照を外し、devbase がサーバに求める条件(マウント・パス・ポリシー・認証)を仕様の側に書く。`secret-backend.md:393,926-929,1418,1652` と、利用者の識別子を「社内メールアドレスの `@` より前」とする `secret-backend.md:922` の書き直しはこの課題が持つ(置き換えではなく、前提の書き直しのため #294 に含めない) | + +## 確かめること + +- `openbao.url` を `http://127.0.0.1:8200` にしたとき、`devbase up` がコンテナへ渡す `BAO_ADDR`(`lib/devbase/commands/container.py:921-932` の `_bao_environment`)がコンテナの中から届くか。`_bao_environment` は `openbao.url` をそのまま `BAO_ADDR` に入れ、loopback の宛先を書き換えないため、コンテナの中の `127.0.0.1` はコンテナ自身を指す。届かないなら、個人の手元のサーバの形では、コンテナの中の `bao` が使えない +- 個人の 1 人の利用で、`version: 2`(グループ別の置き場)と `group_aliases` が要るか。要らないなら、個人の形の既定をどうするか。アカウントグループの宣言は必須で、`default` は予約語として弾かれる(#315)。backend を問わず各プロジェクトの `env` に `DEVBASE_ACCOUNT_GROUP` が要るため、個人の形で宣言するグループ名(例: `personal`)を合わせて決める +- AppRole のほかに、個人の形で token による認証を足す価値があるか(足すなら、token の保存先と期限切れの扱い) +- 雛形のポリシーで、今の受け入れ条件(他人のパスは 403、チーム共通への書き込みは権限のある人だけ)が満たされるか。実機の OpenBao(2.6 系)で確かめる + +## 決めること + +- **devbasex が共通の OpenBao サーバを 1 台運用する案は採らない(2026-09-26 に決定)。** 利用者が個人またはチームで自分のサーバを立てる形(セルフホスト)を前提にする。試用のための共通サーバも、この課題では作らない。理由は次の 4 つ + - 全利用者の機密が 1 か所に集まり、侵害されたときの被害が全利用者に及ぶ。その責任を OSS の運営が負う + - 利用者と端末の資格情報の発行・失効、濫用への対応、可用性、費用が続く。ツールの開発ではなくサービスの運営になる + - 機密を自前で持ちたいから OpenBao を選ぶ利用者が多い。手順と雛形があれば、同じ構成を誰でも再現できる + - 多数の他人で 1 台を共有すると、ポリシーの誤りがそのまま他人の機密の漏れになる +- ポリシーの雛形とサーバを試す構成をどこに置くか(`docs/` か、配布物に含めるか) +- 個人・チーム・既存のサーバの 3 つの形を、1 つの文書にまとめるか分けるか +- `devbase env backend use openbao` に、個人の形の初期設定を助ける機能(例: ポリシーの雛形を書き出す副命令)を足すか、文書だけにするか + +## 完了条件 + +- 管理者のいない個人が、公開の文書と雛形だけで OpenBao サーバを立て、`devbase env backend use openbao` で接続し、`env set` / `env get` / `devbase up` の機密の読み込みまで通せる +- チームで 1 台立てたとき、本人以外の個人の置き場が 403 になり、チーム共通への書き込みが権限のある人だけに限られることを、雛形のポリシーで確かめてある +- 利用者向けの文書と確定仕様が、特定の組織のサーバや運用の決まりを前提にしていない + +## 関連 + +- #273(閉じた)— `devbase list` の TUI の「OpenBao の接続設定」(`lib/devbase/tui/actions_env_openbao.py`)が入った。この画面は token を入力させず保存もしない。個人の形で token の認証を足すなら、この画面と揃える +- #294(閉じた)— 固有の情報の除去。組織のグループ名の置き換えは #294、サーバの前提の書き直しはこの課題 +- #315(閉じた)— 前提 10 の `team/personal/…` のポリシーを、公開の雛形としてこの課題が示す。#315 の完了はこの課題を待たない + +## 由来 + +利用者からの依頼(2026-09-26)。「OSS として多くの人が利用する場合は、利用者が個人・チームでそれぞれのサーバを立てる前提での運用が必要」。 + +## 依頼(原文) + +> OSS として多くの人が利用する場合は、利用者が個人・チームでそれぞれのサーバを立てる前提での運用が必要 + +(書き手: 利用者、日付: 2026-09-26。本文の「由来」の節が引いた依頼の文) + +> devbasex は共通の OpenBao サーバを運用しない。利用者が個人またはチームで自分のサーバを立てる形(セルフホスト)を前提にする +> 試用のための共通サーバも、この課題では作らない。要るなら別の課題で扱う + +(書き手: 起票者が利用者の決定を記録したコメント、日付: 2026-09-26。決定の記録として、利用者が決めた条件に扱う) + +> 個人の形も `version: 2`(グループ別の置き場)にそろえ、宣言するアカウントグループの名前は `personal` にする。形が 1 つで済み、後からチームの形へ移りやすい +> 認証は AppRole だけにする。token による認証は足さない(保存先と期限切れの扱いを持ち込まない) + +(書き手: 起票者が利用者の決定を記録したコメント「決定(2026-10-11 利用者)」、日付: 2026-10-11。同上) + +上の 3 つのほかの本文(何をしたいか・今どうなっているか・提案・確かめること・決めること・完了条件)は起票者が書いた。 +利用者の指示の原文としては扱わず、原文に無い条件は次の「起票者の推定」に写す。 + +## 起票者の推定 + +題・本文のうち、利用者の指示の原文に無く起票者が推して足した条件。承認ゲート 1 で利用者が確かめるまで、利用者の決定として扱わない。 + +| # | 条件 | 推した出所 | この要求での扱い | +| --- | --- | --- | --- | +| S1 | 利用の形を「個人」「チーム」「既存のサーバ」の 3 つに分けて文書で示す | 本文「提案」の「利用の形」 | 受け入れ条件 1 | +| S2 | サーバの用意(KV v2 のマウント・AppRole の有効化・ロールと端末ごとの `secret_id`・entity と alias)を `bao` のコマンドで順に示す | 本文「提案」の「サーバの用意の手順」 | 受け入れ条件 2・3 | +| S3 | ポリシーの雛形を HCL のファイルとしてリポジトリに置き、`version: 1` と `version: 2` の両方を用意する | 本文「提案」の「ポリシーの雛形」 | 受け入れ条件 4・5。置き場は未決 1 | +| S4 | 個人が数分で試せる構成(`bao server -dev` か docker compose の 1 ファイル)と、片付けの手順を示す | 本文「提案」の「最小の試し方」 | 受け入れ条件 6・7 | +| S5 | 利用者の識別子を「社内メールアドレスの `@` より前」でなく「サーバの entity 名と同じ識別子」と説明する | 本文「提案」の「利用者の識別子」 | 受け入れ条件 10 | +| S6 | 確定仕様から非公開のリポジトリ(運用側のリポジトリ)への参照を外し、devbase がサーバに求める条件を仕様に書く | 本文「提案」の「前提の書き直し」 | 受け入れ条件 11・12 | +| S7 | 管理者のいない個人が、公開の文書と雛形だけで `env set` / `env get` / `devbase up` の機密の読み込みまで通せる | 本文「完了条件」1 | 受け入れ条件 13 | +| S8 | チームで 1 台立てたとき、他人の個人の置き場が 403、チーム共通への書き込みが権限のある人だけになることを雛形で確かめる(実機の OpenBao 2.6 系) | 本文「完了条件」2・「確かめること」4 | 受け入れ条件 14 | +| S9 | 利用者向けの文書と確定仕様が、特定の組織のサーバや運用の決まりを前提にしない | 本文「完了条件」3 | 受け入れ条件 9・11 | +| S10 | `openbao.url` が loopback のとき、コンテナの中の `bao` が同じ URL で届くかを確かめる | 本文「確かめること」1 | 受け入れ条件 8。届かせる手段は未決 4 | +| S11 | `devbase env backend use openbao` に、個人の形の初期設定を助ける機能(雛形を書き出す副命令など)を足すかを決める | 本文「決めること」4 | 未決 3 | + +## 目的 + +- 組織の管理者がいない個人や小さなチームが、公開されている文書と雛形だけで自分の OpenBao サーバを立て、 + devbase の OpenBao backend で機密を扱えるようにする +- 利用者向けの文書と確定仕様から、特定の組織が運用するサーバと運用の決まりを前提にした記述を無くす + +## 前提 + +- 前提 1: devbasex は共通の OpenBao サーバを運用せず、試用のための共通サーバも作らない(2026-09-26 の決定) +- 前提 2: 認証は今の AppRole(利用者ごとの `role_id`・端末ごとの `secret_id`)だけで、token による認証は足さない(2026-10-11 の決定) +- 前提 3: 個人の形も `version: 2`(`openbao.layout: group`)を使い、プロジェクトが宣言するアカウントグループは `personal` とする(2026-10-11 の決定)。個人の形で `group_aliases` は使わない +- 前提 4: 対象の OpenBao は base イメージの `bao` と同じ 2.6 系(`containers/base/Dockerfile` の `ARG BAO_VERSION=2.6.2`)とする。ほかの版での動作は確かめない +- 前提 5: devbase がサーバへ送る要求(4 経路のパス・KV v2 の API・AppRole のログイン)は変えない。サーバの側の雛形と文書を、今の devbase の要求に合わせる +- 前提 6: `bao server -dev` は機密を記憶にだけ置き、止めると消える。試用の構成は本物の機密を置く場所としては案内しない +- 前提 7: 実機の確認は、利用者の実際の環境の `devbase up`(P3)を使わず、作業用のプロジェクトと試用のサーバで行う。利用者の環境での確かめはリリース後テストで利用者が行う + +## 対象範囲 + +含む: +- 利用者向けの文書(`docs/user/env-backend.md` と、要れば新しい文書)の、サーバの用意から接続までの手順 +- OpenBao のポリシーの雛形(HCL)と、試用の構成(起動の手順か compose のファイル)をリポジトリに置くこと +- 確定仕様(`docs/specifications/secret-backend.md`)の、サーバの前提の書き直し(393・922・926-929・1418-1420・1652 行の付近) +- コンテナの中の `bao` が、個人の形の構成で使えるかの確認と、その結果に合わせた文書(届かせるためにコードを変えるかは未決 4) +- 用語集への語の追加 + +含まない: +- devbasex が運用する共通の OpenBao サーバ、試用のための共通サーバ(前提 1) +- token による認証(前提 2) +- サーバの冗長化・バックアップ・監査ログ・TLS の証明書の発行など、サーバの本番の運用の手順(公式の文書へのリンクにとどめる) +- 組織のグループ名などの固有の語の置き換え(#294 で済んでいる) +- base イメージの `bao` の版の変更(P1) +- devbase がサーバへ送る要求の変更(前提 5) + +## ドメインイベント + +| # | イベント | 引き金 | 失敗したとき | 順序の前提 | +| --- | --- | --- | --- | --- | +| E1 | 利用者が利用の形(個人・チーム・既存のサーバ)を選んだ | 利用者が文書を開いた | 形を選べない → 文書の冒頭で形の違いを示す(受け入れ条件 1) | — | +| E2 | 利用者が OpenBao サーバを起動した | 利用者が試用の構成か自分のサーバで起動の手順を打った | ポートが使われている・起動しない → 文書に確かめ方(`bao status`)を書く(受け入れ条件 6) | E1 | +| E3 | 管理する人が KV v2 を既定の `devbase` にマウントした | 手順のコマンド | 既にマウントがある → 手順に既存のマウントの扱いを書く | E2 | +| E4 | 管理する人が AppRole を有効にし、雛形のポリシーを書き込んだ | 手順のコマンド | HCL の誤り → `bao policy write` が拒む。雛形は 2.6 系で書き込めることを確かめておく(受け入れ条件 4) | E3 | +| E5 | 管理する人が利用者の entity・alias・ロールを作った | 利用者を足すとき | entity 名と devbase の `user` が食い違う → 個人の置き場が 403。文書に揃える旨を書く(受け入れ条件 10) | E4 | +| E6 | 管理する人が端末の `secret_id` を発行した | 端末を足すとき | — | E5 | +| E7 | 利用者が `devbase env backend use openbao` で接続した | 利用者のコマンド | URL の検査で止まる・ログインが失敗する → `devbase env backend test` で切り分ける | E6 | +| E8 | 利用者がプロジェクトで `DEVBASE_ACCOUNT_GROUP=personal` を宣言した | 利用者が `projects//env` を書いた | 宣言が無い → 機密のコマンドと `up` が止まる(今の振る舞い) | — | +| E9 | 利用者が `env set` / `env get` で機密を読み書きした | 利用者のコマンド | 他人の個人の置き場・権限の無いチーム共通への書き込み → 403(受け入れ条件 14) | E7・E8 | +| E10 | `devbase up` がコンテナへ機密を入れた | 利用者の `devbase up` | サーバに届かない → 今の振る舞い(キャッシュへ落ちるか止まる) | E9 | +| E11 | コンテナの中の `bao` がサーバへ届いた(または届かなかった) | 利用者がコンテナの中で `bao` を打った | loopback の URL が届かない → 未決 4 と受け入れ条件 8 | E10 | +| E12 | 管理する人が端末の `secret_id` を失効させた | 端末を手放したとき | — | E6 | +| E13 | 利用者が試用の構成を片付けた | 試し終えたとき | devbase の設定がサーバを指したまま残る → 機密のコマンドが不達で止まる。片付けに backend を戻す手順を含める(受け入れ条件 7) | E7 | + +## 用語 + +| 用語 | 意味 | +| --- | --- | +| 利用の形 | OpenBao サーバの持ち方の 3 つの区分。個人の形(1 人で 1 台)・チームの形(チームで 1 台、チーム共通と個人の置き場を分ける)・既存のサーバの形(組織が運用するサーバへ接続する) | +| ポリシーの雛形 | devbase が前提にする権限を OpenBao のポリシー(HCL)として書いた、リポジトリに置くファイル。利用者が自分のサーバへ書き込んで使う | +| 試用の構成 | 個人が手元で OpenBao を立てて devbase の接続を試すための、起動から片付けまでの最小の構成 | + +## 受け入れ条件 + +文書と雛形: + +- [ ] 1. 利用者向けの文書が、利用の形の 3 つ(個人・チーム・既存のサーバ)を見出しで分け、それぞれで利用者が何を用意するかを示している +- [ ] 2. 個人の形とチームの形の文書が、KV v2 のマウント(既定 `devbase`)・AppRole の有効化・ポリシーの書き込み・entity と alias の作成・利用者ごとのロールの作成・端末ごとの `secret_id` の発行を、`bao` のコマンドで順に示している +- [ ] 3. チームの形の文書が、利用者と端末を足す手順と、端末 1 台の `secret_id` を失効させる手順を `bao` のコマンドで示している +- [ ] 4. ポリシーの雛形がリポジトリに HCL のファイルとして置かれ、`version: 1` と `version: 2` の両方がある。どの雛形も OpenBao 2.6 系の `bao policy write` で拒まれない +- [ ] 5. `version: 2` の雛形は、グループの名前を書き換えるだけで `team//…` の置き場に使える(例として `personal` を示す)。雛形の中に特定の組織の名前が無い +- [ ] 6. 試用の構成が、個人が手元の 1 台で OpenBao を起動し、受け入れ条件 2 の手順を打ち、`devbase env backend use openbao` で接続するまでを示している。起動を確かめるコマンドと、止めると機密が消えること(前提 6)を書いている +- [ ] 7. 試用の構成の文書が片付けの手順を持ち、サーバを止めることと、devbase の backend を元へ戻すこと(`secrets/backend.yml` と `secrets/bootstrap.env.age` の扱い)を含んでいる。手順の後に `devbase env backend status` が OpenBao を指していない +- [ ] 8. 個人の形の文書が、コンテナの中の `bao` を使えるかと、使うときの `openbao.url` の書き方を明記している。使えると書いた構成では、dev コンテナの中の `bao kv get` が本人の個人共通の置き場を読める +- [ ] 9. `docs/user/env-backend.md` から「サーバの構築と運用は devbase の範囲外です。管理者から次の値を受け取ってください」の前提が消え、既存のサーバの形でだけ「管理者から受け取る」と書かれている +- [ ] 10. 利用者向けの文書と確定仕様が、利用者の識別子(設定の `user`)を「サーバの entity 名と同じ識別子」と説明し、「社内メールアドレス」の語が `docs/user/` と `docs/specifications/secret-backend.md` に無い +- [ ] 11. `docs/specifications/secret-backend.md` に「運用側のリポジトリ」の語が無く、関連リンクから運用側のリポジトリへの参照が消えている +- [ ] 12. `docs/specifications/secret-backend.md` が、devbase がサーバに求める条件(KV v2 のマウント・4 経路のパス・AppRole のログイン・本人のパスの読み書き・チームのパスの読み取り・チーム共通への書き込みは権限のある人だけ・他人のパスと `users/` 直下の一覧の拒否・`version: 2` のグループ単位の権限)を書き、ポリシーの雛形の置き場を示している + +実機(OpenBao 2.6 系): + +- [ ] 13. 前提: 管理者のいない端末。操作: 公開の文書と雛形だけを見て、試用の構成でサーバを立て、`devbase env backend use openbao` で接続し、`DEVBASE_ACCOUNT_GROUP=personal` を宣言した作業用のプロジェクトで `env set` → `env get` → `devbase up` を打つ。結果: `env get` が書いた値を返し、`up` したコンテナの環境変数にその値がある +- [ ] 14. 前提: `version: 2` の雛形を書き込んだサーバに、利用者 2 人(`member01`・`member02`)がいて、チーム共通への書き込みは `member01` にだけ許している。結果: `member02` の資格で `users/member01/…` の読み取りが 403、`member02` のチーム共通への書き込みが 403、`member01` のチーム共通への書き込みと両者のチーム共通の読み取りが通る。`version: 1` の雛形でも、他人の個人の置き場の 403 とチーム共通への書き込みの制限が同じく効く +- [ ] 15. 前提: 受け入れ条件 14 のサーバ。操作: `member02` の端末の `secret_id` を失効させる。結果: その端末の `devbase env backend test` がログインの失敗で止まり、`member02` の別の端末の `secret_id` ではログインが通る + +退行しないこと: + +- [ ] 16. 既存のサーバの形の利用者は、`secrets/backend.yml` と `secrets/bootstrap.env.age` を変えずに、これまでと同じコマンドで機密を読み書きできる(`env-backend.md` の接続の手順のコマンドと引数が変わらない) +- [ ] 17. `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` が通る +- [ ] 18. `python3 .github/scripts/proper_term_check.py` が「語の一覧が無いため飛ばした」と出さずに通る(固有の語の一覧のある端末で打つ) + +## 非機能の条件 + +| 大項目 | 条件 | +| --- | --- | +| セキュリティ | ポリシーの雛形は、受け入れ条件 12 に並べた権限のほかを許さない(本人以外の個人の置き場・`sys/` の管理の経路を利用者のポリシーに含めない)。文書の例に実の token・`secret_id`・root token の値を書かない。devbase の接続に root token を使う手順を示さない。`secret_id` をコマンド引数で渡す手順を示さない(今の `--secret-id-stdin` を使う) | +| 運用・保守性 | 雛形と試用の構成は OpenBao の版を 1 か所に書き、base イメージの `BAO_VERSION` と同じ 2.6 系に揃える | +| システム環境 | 試用の構成は macOS の Docker Desktop と Linux の Docker の両方で動く手順を示す。片方でしか確かめていなければ、文書に確かめた環境を書く | + +## 影響 + +| 対象 | 影響 | +| --- | --- | +| 公開インタフェース | 変わらない。初期設定を助ける副命令を足すかは未決 3 で、足すなら追加だけで既存のコマンドは変えない | +| データ | 変わらない。`secrets/backend.yml` の形とサーバ上のパスは変えない | +| 既存の振る舞い | 文書と確定仕様の記述が変わる。コンテナの `BAO_ADDR` の扱いを変える場合(未決 4)は、loopback の `openbao.url` を使う利用者のコンテナの中の `bao` の宛先だけが変わる | + +## 検証手段 + +| 項目 | 手段 | +| --- | --- | +| テスト | `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` | +| 静的解析 | `uvx ruff check --select=E9,F63,F7,F82 lib`、`python3 .github/scripts/proper_term_check.py` | +| 文書の語 | `grep -rn '社内メールアドレス\|運用側のリポジトリ' docs/user docs/specifications/secret-backend.md` が 0 件(受け入れ条件 10・11) | +| 手動確認(マージ前) | 受け入れ条件 4・14・15: 作業用の端末で OpenBao 2.6 系を試用の構成で立て、雛形を書き込み、2 人の利用者の資格で読み書きと失効を打つ。コマンドと結果を PR の本文かコメントに残す | +| 手動確認(マージ前) | 受け入れ条件 8・13: 作業用のプロジェクト(実の利用者の環境ではない)で接続から `devbase up` とコンテナの中の `bao kv get` までを打ち、結果を残す。非対話のセッションで base を建てられないときは「判断が要る」で返す | +| 手動確認 | リリース後テストで、利用者が自分の端末で個人の形の文書を最初から辿り、受け入れ条件 13 と同じ結果になるかを見る | + +## 前提とする取り決め + +| 項目 | 参照先 / 決めたこと | +| --- | --- | +| プロジェクト構造 | `AGENTS.md` の「このリポジトリ」。利用者向けの文書は `docs/user/`、確定仕様は `docs/specifications/`。雛形の置き場は未決 1 | +| コーディング規約 | `AGENTS.md` の「書き方」。文書は日本語、用語は用語集の語。実機の記録は `member01` などの中立の名前で書く | +| テスト戦略 | 文書と雛形は手動確認(マージ前)で実機の OpenBao に当てる。コードを変える場合(未決 3・4)は pytest で入出力を固める。今の偽の OpenBao(`http.server`)のテストは変えない | + +## 境界 + +| 区分 | 内容 | +| --- | --- | +| 常に行う | 全体のテストと lint と固有の語の検査、`CHANGELOG.md` の `[Unreleased]` の記入(利用者向けの文書が変わるため)、用語集の更新と `render` | +| 確認してから行う | 新しい副命令などの公開インタフェースの追加(未決 3)、`BAO_ADDR` の書き換えなどコードの振る舞いの変更(未決 4)。どちらも承認ゲートで人が見る | +| 行わない | base イメージの `bao` の版の変更(P1)、実の利用者の環境での `devbase up`(P3)、共通サーバの用意、token の認証の追加、devbase がサーバへ送る要求の変更 | + +## 未決 + +| # | 項目 | 誰が決めるか | 期限 | +| --- | --- | --- | --- | +| 1 | ポリシーの雛形と試用の構成の置き場(`docs/` の下か、`etc/` など配布物の中か) | 設計 | 設計 PR | +| 2 | 利用の形の 3 つを 1 つの文書にまとめるか、形ごとに分けるか | 設計 | 設計 PR | +| 3 | `devbase env backend use openbao` に、雛形を書き出すなど個人の形の初期設定を助ける機能を足すか、文書だけにするか | 設計 | 設計 PR | +| 4 | `openbao.url` が loopback のとき、コンテナの中の `bao` を届かせる手段(`BAO_ADDR` の宛先を `host.docker.internal` などへ書き換えるか、文書で別の URL を案内するか、コンテナの中の `bao` は使えないと書くか)。サーバが loopback だけで待つ場合の Linux の Docker の扱いを含む | 設計(実機の確認の結果で決める) | 設計 PR | +| 5 | 試用の構成を `bao server -dev` にするか、docker compose の 1 ファイルにするか | 設計 | 設計 PR |