Skip to content

Fix: OpenBao への接続を OS の信頼ストア(truststore)で検証する(#497) - #498

Merged
takemi-ohama merged 12 commits into
mainfrom
fix/openbao-truststore
Oct 17, 2026
Merged

takemi-ohama merged 12 commits into
mainfrom
fix/openbao-truststore

Conversation

@takemi-ohama

@takemi-ohama takemi-ohama commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Closes #497

何を変えたか

TLS を傍受するプロキシ(Netskope など)の下で、ホストの devbase が OpenBao へ繋がらずキャッシュへ落ちていた。OpenBao への HTTP だけ、OS の信頼ストア(truststore)の SSL コンテキストで検証する。

置き場 変更
lib/devbase/env/openbao.py _tls_context()(1 度だけ作って使い回す)を足し、OpenBaoBackend._http の urlopen に context= で渡す。truststore を import できなければ None(既定のコンテキスト)。verify_flags は ssl.create_default_context() のものを写す(Python 3.13 の VERIFY_X509_STRICT・VERIFY_X509_PARTIAL_CHAIN を落とさない。レビューの指摘で足した)。構造改善で OpenBaoBackend の重複(HTTP の状態の扱い・到達できない旨のエラーの組み立て・保存の送り方)をまとめ、振る舞いは変えていない
pyproject.toml・uv.lock dependencies に truststore>=0.10(0.10.5 を固定)
tests/env/test_openbao.py 下の受け入れ条件のテスト
docs/specifications/secret-backend.md 「OpenBao との契約」に TLS の検証の項、「セキュリティ」に信頼の前提を足した
CHANGELOG.md [Unreleased] の Fixed

urlopen を呼ぶのは lib/devbase の中で OpenBaoBackend._http だけである(認証・取得・保存・削除の 4 経路はすべてここを通る)。bootstrap.py は HTTP を持たない。

受け入れ条件との対応

# 条件 確かめ方
1 4 経路が truststore のコンテキストを使う test_every_openbao_route_uses_the_truststore_context(偽サーバに 4 経路を通し、context が 1 つの truststore.SSLContext・CERT_REQUIRED・ホスト名照合あり・既定と同じ verify_flags を確かめる)、test_truststore_context_copies_the_default_verify_flags、test_urlopen_is_called_only_from_the_openbao_http_helper(AST で urlopen の呼び出し元を挙げる)
2 プロセス全体へ注入しない test_truststore_is_not_injected_into_the_process(ssl.SSLContext が差し替わらない)
3 import できなくても落ちない test_missing_truststore_falls_back_to_the_default_context
4 検証の失敗は SecretUnreachableError、メッセージの形は同じ test_certificate_verification_failure_is_unreachable
5 依存と uv.lock、uv sync --locked uv sync --locked が通る
6 プロキシの下の macOS で取得できる リリース後テスト。worktree の中で読むだけの確認をした(下)
7 CHANGELOG と仕様 上の表

設計の決定との差

無し(設計 PR なし)

テスト

  • env -u DEVBASE_ROOT uv run --locked pytest tests/ -q — 4510 passed, 32 skipped(2026-10-17、HEAD bca2cc6)
  • uvx ruff check --select=E9,F63,F7,F82 lib — 通る
  • python3 .github/scripts/proper_term_check.py — 手元は語の一覧が無いため飛ばした(検査はしていない)
  • 読むだけの実機の確認(macOS・Python 3.13・TLS を傍受するプロキシの下、SSL_CERT_FILE・REQUESTS_CA_BUNDLE を外して AppRole のログインと 1 参照の GET だけ)
lib 結果
この PR(bca2cc6) ログインと取得が通る
main CERTIFICATE_VERIFY_FAILED ... self-signed certificate in certificate chain で到達できない

構造改善とレビュー

リリース後テスト

  • 受け入れ条件 6: main へのマージ後、回避の環境変数を付けずに bin/devbase の読むだけの操作で OpenBao から取得できる(devbase up は使わない)

🤖 Generated with Claude Code

https://claude.ai/code/session_01NjbAm85zNM1QmoDYNiX5Dy

takemi-ohama and others added 11 commits October 17, 2026 16:07
TLS を傍受するプロキシの下で、既定の CA では検証に落ちてキャッシュへ落ちていた。
OpenBaoBackend._http の urlopen に truststore.SSLContext を渡す。プロセス全体へは
注入せず、truststore を読み込めないときは既定のコンテキストで続ける。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NjbAm85zNM1QmoDYNiX5Dy
Item-Id: I-001
Impl-Runtime: claude
Impl-Model: default
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fetch

404 の応答の本文から現在の版を取り出す段階を _version_from_404 へ抽出した。
fetch は 404 のときにこのヘルパの版で基準を控えるだけになる。

Item-Id: I-001
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HTTP の失敗応答を表す内部の例外 _HttpStatus を、同じファイルのほかの例外に
そろえて _HttpStatusError へ改名した(定義・raise 1 か所・except 4 か所)。

Item-Id: I-002
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oBackend.save

応答の版が整数か (真偽値でない) を確かめる規則を _as_version へまとめ、
_version_of と save の新しい版の読み取りの両方から使う。

Item-Id: I-004
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
失敗の文言の末尾へ「接続先: <URL>」の行を足す処理を _at_server へ抽出し、
接続先の行で終わる文言 (login・_http・_unreachable・_unreadable・_forbidden) を
このヘルパで組む。接続先の後ろに案内やパスが続く文言はそのまま残した。

Item-Id: I-005
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…called_only_from_the_openbao_http_helper

関数が urlopen を属性か名前で参照するかの判定を _refers_to_urlopen へ抽出し、
演算子の優先順位に頼っていた条件を 2 つの if に分けた。テスト本体は
ファイルと関数の列挙だけになる。補助関数の判定 (属性・名前・入れ子・無関係な名前) を
小さな AST で確かめるテストを足した。

Item-Id: I-007
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aoBackend

login・fetch・save・remove に生のまま散っていた HTTP の状態コード (400・403・404) を
_HTTP_BAD_REQUEST・_HTTP_FORBIDDEN・_HTTP_NOT_FOUND とログインの拒否の組
_LOGIN_REJECT_STATUSES に置き換えた。4xx の範囲の判定はそのまま残した。

Item-Id: I-008
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ends_the_whole_dictionary_in_one_post_with_the_read_version

KV への POST だけを取り出す内包表記を _kv_posts(openbao) へまとめ、
書き方の揺れ (kv_path is not None と真偽) も 1 つにそろえた。

Item-Id: I-009
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oBackend._unreachable

接続先の行を文面の途中に手で組んでいた 3 か所 (login の認証拒否・_basis_for_write・
save の 4xx 拒否) を _at_server 経由にした。_at_server は接続先の行の後へ続く行を
受け取れるようにし、文面は変えていない。

Item-Id: I-010
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tatus.errors

共通化の前に、書き込みの 400 の本文 (空・不正な JSON・不正な UTF-8・配列・
errors がリストでない・要素が文字列でない) の扱いを現状固定テストで押さえる。

Item-Id: I-011
Impl-Runtime: claude
Impl-Model: claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

改修計画 — devbasex/devbase #498

/ndf:cross-refactoring が提案し、改修計画し、適用した改善項目の記録である。
理由と手順は提案の時点でしか残らないため、公開の直前に書き出している。

  • 件数: 採用 9・未確認 0・取り消し 2・見送り 0(最終ゲート: passed)
  • 公開: 641c3f8 を push した
  • 書き直した時刻: 2026-10-17T16:34:36
  • 対象範囲: lib/devbase/env/openbao.py, tests/env/test_openbao.py
  • テストの戦略: local-full(根拠 test.strategy)
  • 範囲テストの雛形: env -u DEVBASE_ROOT uv run --locked pytest -q -o junit_family=xunit1 --junitxml=build/ndf/junit.xml {paths}
  • 着手前のテスト: env -u DEVBASE_ROOT uv run --locked pytest tests/ -q -o junit_family=xunit1 --junitxml=build/ndf/junit.xml(whole)
  • 着手前のテストの結果: 通過(172.4 秒 / HEAD 5ea80ba)
  • 既存失敗の件数: 0
  • 想定最大時間: 35 分 / 改修計画の時点で使えた時間: 13.81 分
  • 実装担当: claude

改善項目

I-001 — lib/devbase/env/openbao.py#OpenBaoBackend.fetch

兆候 手法 重要度 等級 提案元 状態 コミット
long_method extract_method major medium kiro / claude 採用 2

なぜ: 本体で最も認知的複雑度が高い (15)。GET の送信・404 の本文再解釈による版の復元・403 の封筒・取得成功時の secrets と版の解釈が 1 つの関数に同居し、部分だけをテストできない。404 の分岐は e.body を再度 json.loads して _version_of を引く独立した段階である

手順: 1. 404 応答から版を取り出す段階を _version_from_404(e.body) として抽出する
2. fetch 本体では except _HttpStatus 内でそのヘルパを呼ぶだけにする
3. 既存テスト (test_save_after_a_soft_delete_uses_the_current_version_not_zero, test_empty_reference_is_absent) で版の復元が変わらないことを確認する

I-002 — lib/devbase/env/openbao.py#_HttpStatus

兆候 手法 重要度 等級 提案元 状態 コミット
inconsistent_naming rename minor medium claude 採用 1

なぜ: _HttpStatus は Exception の派生で raise / except で使われるが、名前が状態の値に読める。同じファイルの例外はすべて ...Error で終わる(SecretUnreachableError など)

手順: 1. _HttpStatus を _HttpStatusError へ改名する(定義・raise 1 か所・except 4 か所)
2. ほかの差分と混ぜず単独のコミットにする
3. テストを通す(テストはこの名前を参照していない)

I-003 — tests/env/test_openbao.py#test_every_openbao_route_uses_the_truststore_context

兆候 手法 重要度 等級 提案元 状態 コミット
magic_value introduce_named_constant minor medium claude 取り消し 0

なぜ: ログインの経路を見分けるのに '/v1/auth/approle/login' を文字列で書いている。本体には同じ値の LOGIN_PATH があり、経路が変わるとテストだけ古い値で 'login' を見分けられなくなる

手順: 1. from devbase.env.openbao の import に LOGIN_PATH を足す
2. 内包表記の比較を path != LOGIN_PATH に置き換える
3. テストを通す

取り消した理由: テストの期待する振る舞いが変わっています(tests/env/test_openbao.py)。構造改善では期待出力を変えません。振る舞いの変更は別の変更に分けてください

I-004 — lib/devbase/env/openbao.py#OpenBaoBackend.save

兆候 手法 重要度 等級 提案元 状態 コミット
duplication consolidate_duplication minor medium claude 採用 1

なぜ: 応答の版が整数か(isinstance(int) かつ bool でない)を確かめる規則が _version_of と save の新しい版の読み取りに 2 か所ある。版の扱いは CAS の基準そのもので、片方だけ直ると読みと書きで版の解釈が食い違う

手順: 1. モジュールの関数 _as_version(value) -> Optional[int] を足す(int かつ bool でなければ値、ほかは None)
2. _version_of の最後の return を _as_version(version) に置き換える
3. save の new_version を _as_version(_dict_get(inner, 'version')) にし、判定を if new_version is None: に置き換える
4. 保存の版のテスト(2 度目の保存が 1 度目の応答の版を使う)が通ることを確かめる

I-005 — lib/devbase/env/openbao.py#OpenBaoBackend

兆候 手法 重要度 等級 提案元 状態 コミット
duplication extract_method minor medium claude 採用 1

なぜ: 失敗の文言の末尾の「\n 接続先: {self.url}」が login・_http(3 か所)・_unreachable・_unreadable・_forbidden・_basis_for_write・save の 4xx に 10 か所近く手で書かれている。テストは '\n 接続先: ' の形を確かめており、1 か所の書き損じが利用者の見る文言の揺れになる

手順: 1. _at_server(self, message: str) -> str を足し、f"{message}\n 接続先: {self.url}" を返す
2. 接続先の行で終わる文言だけを _at_server(...) で包む形へ置き換える(接続先の後ろにパスや案内が続く文言は置き換えない)
3. 不達・認証・JSON の不正のテストで文言が変わらないことを確かめる

I-006 — tests/env/test_openbao.py#test_issue_token_logs_in_again_near_expiry

兆候 手法 重要度 等級 提案元 状態 コミット
test_coupled_to_internals fix_dependency_direction minor medium codex 取り消し 0

なぜ: 期限切れのテストが backend._token_expires_at を直接0へ書き換え、内部属性名と期限の保存方法に結合している。同じファイルの test_an_expiring_token_is_renewed_before_the_request は time.monotonic を進めて公開APIで同じ現象を試しており、既存の方法へそろえられる。

手順: 1. 最初の issue_token の前に time.monotonic を制御し、既知の基準時刻でトークンを取得する。
2. _token_expires_at の直接変更を外し、偽サーバのトークン寿命を越える時刻へ時計を進めて再度 issue_token を呼ぶ。
3. openbao.logins == 2 の期待値を維持し、内部状態に触れず再認証を確認する。既存の取得経路の期限テストと合わせて実行する。

取り消した理由: テストの期待する振る舞いが変わっています(tests/env/test_openbao.py)。構造改善では期待出力を変えません。振る舞いの変更は別の変更に分けてください

I-007 — tests/env/test_openbao.py#test_urlopen_is_called_only_from_the_openbao_http_helper

兆候 手法 重要度 等級 提案元 状態 コミット
deep_nesting extract_method major low claude / codex / kiro 採用 1

なぜ: テストで最も認知的複雑度が高い (18)。rglob → ast.parse → ast.walk の三重の入れ子の中で urlopen の呼び出し元を集める。さらに判定条件 (isinstance(..., Attribute) and inner.attr == 'urlopen' or isinstance(..., Name) and inner.id == 'urlopen') は演算子の優先順位が読みにくく、意図を取り違えやすい。呼び出し元の収集を関数へ切り出すと、入れ子が浅くなりテスト本体はアサートだけになる

手順: 1. 同じテストファイル内にASTの関数ノードを受けてurlopen参照の有無を返す補助関数を抽出する。Attribute と Name の両方、FunctionDef のみを扱う条件、入れ子の関数も走査する現在の意味を保つ。
2. 外側のテストはファイルと関数を列挙して補助関数で絞る形にし、対象ディレクトリと最終的な sorted(set(callers)) の期待値を変えない。
3. 小さなAST入力で属性参照・名前参照・無関係な名前・入れ子を確かめ、既存の配置制約テストと全経路のTLSテストを実行する。

レビューで確かめるテストの差分: tests/env/test_openbao.py(機械で期待値が変わったか決まらなかった)

I-008 — lib/devbase/env/openbao.py#OpenBaoBackend

兆候 手法 重要度 等級 提案元 状態 コミット
magic_value introduce_named_constant minor low kiro 採用 1

なぜ: HTTP の状態コード 400・403・404・500 と範囲 (400 <= e.status < 500) が login・fetch・save・remove に生のまま散在する。LOGIN_PATH や _CAS_MISMATCH は定数化されているのに状態コードは数値のままで、意味が読めず変更漏れが起きる。特に 403 は「権限不足」、404 は「未作成/論理削除」、400/403 のログイン拒否という固有の意味を持つ

手順: 1. モジュール先頭に _HTTP_FORBIDDEN = 403, _HTTP_NOT_FOUND = 404, _LOGIN_REJECT_STATUSES = (400, 403) を定義する
2. login・fetch・save・remove の該当リテラルを定数へ置き換える (範囲判定 400 <= e.status < 500 はそのまま残すか _is_client_error ヘルパにまとめる)
3. 既存の状態コード別テストが通ることを確認する

I-009 — tests/env/test_openbao.py#test_save_sends_the_whole_dictionary_in_one_post_with_the_read_version

兆候 手法 重要度 等級 提案元 状態 コミット
duplication extract_test_helper minor low claude 採用 1

なぜ: KV への POST だけを取り出す [r for r in openbao.requests_of('POST') if r.kv_path] が 7 本のテストに書かれている(kv_path is not None と真偽の 2 通りの書き方が混じる)

手順: 1. モジュールの関数 _kv_posts(openbao) を足す(ログインの POST を除いた KV への POST を返す)
2. 7 か所を _kv_posts(openbao) / _kv_posts(openbao)[0] に置き換える。test_write_does_not_touch_the_server_when_the_read_fails の any(...) は not _kv_posts(openbao) にする
3. テストを通す

レビューで確かめるテストの差分: tests/env/test_openbao.py(機械で期待値が変わったか決まらなかった)

I-010 — lib/devbase/env/openbao.py#OpenBaoBackend._unreachable

兆候 手法 重要度 等級 提案元 状態 コミット
duplication consolidate_duplication minor low kiro 採用 1

なぜ: 不達エラーの文面「OpenBao へ到達できません (...)\n 接続先: {self.url}」が login・_http(到達不能/JSON 不可)・_unreachable・_basis_for_write に別々に組み立てられている。接続先行の形式を変えるとき複数箇所を直す必要があり、片方だけ直す事故が起きる。接続先行の付与は _unreachable に既にある共通化を広げて 1 箇所へ寄せる

手順: 1. 「\n 接続先: {self.url}」を付ける小さなヘルパ(例 _endpoint_line / _with_endpoint(message))を 1 つ用意する
2. login の 2 つの SecretUnreachableError と _http の到達不能・JSON 不可の文面をヘルパ経由に置き換える
3. _basis_for_write の文面もヘルパ経由にする
4. 既存テスト (test_unreachable_server_is_reported_with_the_url ほか) で接続先行が残ることを確認する

I-011 — lib/devbase/env/openbao.py#_HttpStatus.errors

兆候 手法 重要度 等級 提案元 状態 コミット
duplication consolidate_duplication minor low codex 採用 1

なぜ: _HttpStatus.errors と OpenBaoBackend.fetch の404処理が、同じ失敗応答の bytes をUTF-8でJSONへ変換し、解釈できなければ空として扱う手順を重複して持つ。失敗応答の寛容な読み取りを一か所に寄せると、fetch の例外処理内のネストも減る。成功応答の厳格なJSON処理は目的が異なるため共通化しない。

手順: 1. tests/env/test_openbao.py に、404本文が空・不正JSON・不正UTF-8・有効な版を持つ場合、および400本文が不正な場合の公開API経由の現状固定テストを置く。既存の論理削除とCAS競合のテストも使う。
2. _HttpStatus に、本文を解釈して返し、現在捕捉している例外だけを捕捉して空辞書を返す内部メソッドを抽出する。
3. errors と fetch の404分岐をその呼び出しへ置き換える。errors の文字列化、404で版が無いときの0、正常な非辞書JSONの扱いを保つ。
4. 対象テストと全体テストを実行する。

レビューで確かめるテストの差分: tests/env/test_openbao.py(機械で期待値が変わったか決まらなかった)

指標の測定

  • 状態: written / 言語とツールの対応: 既定(.ndf/code-metrics.json は無い) / 所要: 2.2 秒(使えた時間 105 秒 / 上限 105 秒)
  • 指標のファイル: /var/folders/qz/qxt0p_y15xv5jg4x77zmtxj80000gp/T/ndf-worktrees/devbasex--devbase/rf498/work/.cross_refactoring/code-metrics-rf498.md
言語 ツール 版 ランナー ファイル 秒 結果
python ruff-complexipy ruff 0.16.9 / complexipy 8.0.1 uvx 2 0.7 測った
重複: python symilar pylint 4.0.9 uvx 2 1.4 測った(0 箇所)

時間の上限

値 中身
提案の枠の終わり(監視の上限 = 残り + 余裕) 2026-10-17T16:19:22+09:00
改修計画の枠の終わり(同上) 2026-10-17T16:22:52+09:00
テストの追加の終わり = 実装の終わり − Σ(採っていて未検証の項目の実装の見積り) 2026-10-17T16:27:44+09:00
実装の終わり = 最終ゲートの修正の打ち切り − バッファ − Σ(採っていて未検証の項目の検証の見積り) 2026-10-17T16:28:56+09:00
直しの試行の打ち切り 2026-10-17T16:35:38+09:00
想定最大時間の終わり H(子プロセスはすべてここまでに止まる) 2026-10-17T16:44:01+09:00
確かめの開始の締め切り = H − 確かめの見込み(最終ゲート修正と打ち切りの後の取り消しもここまで) 2026-10-17T16:41:08+09:00
確かめの見込み(秒。最終ゲートの確かめ 1 回。バッファの最終ゲートの全体テスト) 173
最終ゲート修正 1 回の見込み(秒。バッファの final_fix。起動は今 + この値が確かめの開始の締め切り以前のときだけ) 330
指標の測定の上限(秒。提案の枠の中から割く) 105
着手前のテスト 1 回の上限(秒) 420
範囲テスト 1 回の上限(秒) 518
手元の全体テスト 1 回の上限(秒) 420
CI の待ちの上限(秒) 324
余裕(秒。手順の上限と CLI の上限に足す) 105

入力: 予算 35 分 / 全体テストの所要 w 139.8(ndf-record) / 着手前の実測 x 172.4 / CI の壁時計 c 108.0 / 範囲テストの所要 s None(—) / 戦略 local-full

無音の打ち切りは手順の監視の上限と同じ値である。項目ごとの期限は持たない(実装を止めるのは実装の終わりだけ)。

見送った提案

対象 兆候 理由 根拠(MVV) 補足
lib/devbase/env/openbao.py#OpenBaoBackend.fetch duplication duplicate Vision C-004 と同じ変更
lib/devbase/env/openbao.py#OpenBaoBackend.save long_method no_target C3 / Value 2 / Vision / Value 3 範囲テストを組み立てられない(test_targets が --scope のテストの置き場所に無いか、雛形を持つ suite が無い)
tests/env/test_openbao.py#test_group_layout_reads_and_writes_the_group_paths duplication budget Vision 見積り 1.2 分が残りに入らない
lib/devbase/env/openbao.py#OpenBaoBackend._http long_method budget Value 3 見積り 1.4 分が残りに入らない
lib/devbase/env/openbao.py#OpenBaoBackend.remove duplication budget C3 / Vision 見積り 2.1 分が残りに入らない

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | codex | REQUEST_CHANGES

6ファイルをレビューし、修正が必要な指摘は major 1件です。

Comment thread lib/devbase/env/openbao.py Outdated

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 1 | kiro | APPROVE

6 ファイルすべてを確認した。openbao.py の構造改善(_at_server への文言の集約・_as_version・_version_from_404 の抽出・状態コードの定数化・例外名の改名)は、メッセージの行順・例外の型・HTTP の状態の扱いを保っており、振る舞いを変えていない。#497 の TLS 変更は意図した新機能で、4 経路すべてへ同じ SSL コンテキストを渡し、検証を緩めず(CERT_REQUIRED・check_hostname・inject_into_ssl 不使用)、truststore 不在時は既定コンテキストへ退避する。CHANGELOG と secret-backend.md はトラストストアでの検証の前提(OS に CA を足せる者は接続先を偽れる)を明記し、実装と整合している。依存 truststore>=0.10 は lock で 0.10.5 に固定。critical/major の基準に該当する指摘は見つからなかった。

truststore.SSLContext(PROTOCOL_TLS_CLIENT) は、Python 3.13 の
ssl.create_default_context() が立てる VERIFY_X509_STRICT と
VERIFY_X509_PARTIAL_CHAIN を立てない。今までの urlopen が拒んだ不適合な
証明書チェーンを受け入れないよう、その Python の既定の verify_flags を写す。
3.10・3.13 で truststore の SSLContext が verify_flags の設定を受けることを
確かめ、版によらず写されることを見る回帰テストを足した。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NjbAm85zNM1QmoDYNiX5Dy
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

🔧 /ndf:fix サマリ | round 1 | commit bca2cc6

対応件数: critical=0 / major=1 / minor=0(合計 1 件)
決着: 1 件 / 見送り: 0 件 / 却下: 0 件
CI: SUCCESS

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | claude | APPROVE

6 ファイルを確認し、critical / major の基準に当たる指摘は無い。Refactor のコミットはエラーの型・文言の行順・HTTP の状態の扱い・トークンの再取得を変えておらず、round 1 の VERIFY_X509_STRICT の指摘は bca2cc6 で既定の verify_flags を写す形に直り回帰テストもある。urlopen の呼び出しは _http の 1 か所で、4 経路すべてに同じコンテキストが渡る。

@takemi-ohama takemi-ohama left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 cross-review | round 2 | kiro | APPROVE

6 ファイルすべてを確認した。round 1 の唯一の major(takemi-ohama: truststore.SSLContext が create_default_context の VERIFY_X509_STRICT を立てず、Linux/OpenSSL で不適合な証明書チェーンを受け入れ得る)は bca2cc6 で context.verify_flags = ssl.create_default_context().verify_flags により解消されている。truststore の実装(_api.py)で verify_flags のセッターは _ctx に書き込み、その _ctx が wrap_socket 時の _configure_context で使われるため、写した検証フラグは実際のハンドシェイクに効く。test_truststore_context_copies_the_default_verify_flags が Python の版に依らず写しを確認する回帰テストになっている。構造改善のコミット(_at_server への文言集約・_as_version・_version_from_404 の抽出・HTTP 状態コードの定数化・_HttpStatus→_HttpStatusError の改名・テストヘルパ _kv_posts)は、例外の型・文言の行順・HTTP 状態の分岐・トークン再取得の振る舞いを保っている。truststore の SSL コンテキストは _http の 1 経路から 4 つの urlopen 呼び出しすべてへ _tls_context() 経由で渡り(test_every_openbao_route_uses_the_truststore_context が id 一致を確認)、検証は緩めず(CERT_REQUIRED・check_hostname・inject_into_ssl 不使用)、truststore 不在時は None へ退避して既定コンテキストで続ける。依存 truststore>=0.10 は lock で 0.10.5 に固定。critical/major の基準に該当する指摘は見つからなかった。

@takemi-ohama

Copy link
Copy Markdown
Contributor Author

🔧 /ndf:fix サマリ | commit (なし)

対応件数: critical=0 / major=0 / minor=0(合計 0 件)
決着: 0 件 / 見送り: 0 件 / 却下: 0 件
CI: NONE

@takemi-ohama
takemi-ohama marked this pull request as ready for review October 17, 2026 07:53
@takemi-ohama
takemi-ohama merged commit 5b8a5dc into main Oct 17, 2026
10 checks passed
@takemi-ohama

Copy link
Copy Markdown
Contributor Author

配布の記録

段階: 本番(2026-10-17、main へのマージで反映。承認ゲート 2 を同日利用者が承認)
版: 4.1.0 → 版数を上げない(main = 5b8a5dc を配布。タグ・GitHub Release は切らない)
スプリント: PR #498

  • 形: 反映で届くもの(install.sh が main を取得する)。次の bin/devbase の起動で uv が truststore 0.10.5 を入れる
  • 取り消しの手段: main へ打ち消しの Pull Request を出す
  • 確定仕様は同じ PR で docs/specifications/secret-backend.md を更新済み

リリース後テスト

対象の版: main 5b8a5dc(2026-10-17)

課題 受け入れ条件 実行したこと 実行時刻 結果
#497 6. TLS を傍受するプロキシの下の macOS(Python 3.13)で、回避の環境変数なしに bin/devbase が OpenBao から取得できる メインディレクトリで env -u SSL_CERT_FILE -u REQUESTS_CA_BUNDLE -u DEVBASE_ROOT bin/devbase env list --global --keys --group <g> を 2 グループで打った(読むだけ、値は出さない)。初回の起動で uv が 1 パッケージ(truststore 0.10.5)を入れた 2026-10-17 合格(2 グループとも exit 0、キャッシュへ落ちる警告なし)
#497 1〜5・7 PR の CI(10 件成功)と全体テスト 4510 passed 2026-10-17 合格

合否: 合格

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenBao への接続を OS の信頼ストア(truststore)で検証し、TLS を傍受するプロキシ越しでも到達できるようにする

1 participant