2026-05-15 mikyun-portal 大規模バグ修繕作業ログ

📋 概要

2026-05-15 にコード君が実施した mikyun-portal (cloud.k2-o.net) の大規模バグ修繕作業ログ。35 件以上のバグを修正し、新規契約のカード決済フロー end-to-end が完全に動作するように整備した。

🚨 緊急対応

  • DEV-001 / DEV-002 peer 消失事故の復旧 — add-child テスト操作で reload_wireguard() が DB の wg_iface='wg0' 旧名残デバイスを除外し、顧客 wan の親端末 (10.200.0.2) と iPhone (10.200.0.30) の WG peer が /etc/wireguard/wg-clients.conf から消失。顧客 VPN 接続が断。/opt/mikyun/output/client-configs/ の stored conf から PrivateKey/PresharedKey を抽出し、PublicKey を再計算して手動再追加 → wg syncconf で復旧。
  • DB の wg_iface マイグレーション漏れ補完 — DEV-001/002 を wg_iface='wg-clients' + preshared_key も DB に流し込み、今後の reload_wireguard で再発しないように。

🔒 セキュリティ修繕

  1. /client/qr/{cid}/{did} および /client/conf/{did} の path traversal — device_id を正規表現で厳格バリデーション
  2. /client/* ハンドラ 5 か所で contract_id を device_id 文字列分割で導出していた auth bypass を DB ルックアップに置換
  3. /client/qr に device 所有権検証を追加 — 別契約の device_id を渡すと 403
  4. /customers エンドポイントが無認証で呼べた件 → X-API-Key (WordPress 用) 必須に
  5. engine の WireGuard サーバー秘密鍵がハードコードだった件 → WG_CLIENTS_PRIVKEY 環境変数化 (fallback あり)
  6. 顧客側 portal is_local_admin に Basic 認証 fallback を追加 — admin が非 WG ネットワーク (darkhero など) からでも /client/* を利用可能に
  7. LOCAL_ADMIN_SUBNETS を拡張 (192.168.30., 10.250.0., 10.251.0. 追加) — R640 SNI relay 経由の admin アクセスを許可

🧱 データ整合性

  1. engine の全 except Exception ブロック 11 か所に except HTTPException: raise を前置 — 404/422 が 500 に化けるバグを根絶
  2. allocate_next_ip に SELECT … FOR UPDATE を追加 — 並行 add-child での IP 重複割当を防止
  3. engine import time 欠落 — Telegram 通知再試行で NameError を起こしていた
  4. main.py の重複 issue_token / consume_token 削除 — 後発の定義が未定義 _tokens 辞書を参照していたバグ
  5. delete_device の順序修正 — DB UPDATE を先に commit、WG 後処理は best-effort
  6. add_child_device の WG / file 書込順序を修正 + 各ステップを try/except でガード — 一部失敗時の orphan を最小化
  7. main.py /api/billing/init の rowcount チェックを commit より前へ
  8. /api/contracts/check-username を form / JSON 両対応 + duplicate-key の list 取扱を追加
  9. main.py の重複 ui_traffic v2 (return 抜けの死コード) を削除

🎨 UI / UX 修繕

  1. client_ui.py の WG_INTERFACE = wg0 ハードコード → wg-clients へ修正。これだけで顧客側 portal 全機能が復活
  2. /ui/contracts/{id} に端末追加・削除ボタンを追加 (POST /ui/devices/{did}/delete, /ui/contracts/{cid}/devices/add)
  3. dashboard の bond0/bond1 ハードコード → bond 名を自動検出 (2026-05-13 のスイッチ再設定で SM-MGMT, SM-STG 等に改名されていた)
  4. dashboard の get_server_stats.sh の Supermicro/R640 case 論理が逆転 → Supermicro=local, R640/hirame=remote SSH に修正
  5. SSH に UserKnownHostsFile=/dev/null を付加 (host key 変更耐性)
  6. maguro 公開鍵を R640 katuo の authorized_keys に追加 → dashboard で R640 stats 取得可能に
  7. nextcloud-stack-app-1 ハードコード × 4 ファイルを compose-app-1 に修正 — 新規契約時の NC アカウント作成が止まっていた根本原因
  8. compose.yml に /srv/storage:/srv/storage:ro mount を追加
  9. Dockerfile に qrencode を永続追加 (container 内に未 install で QR が 22byte テキストに化けていた)
  10. identity_passphrase のリテラル残骸 (古い API 経由の JSON エスケープ漏れ) を 260403-865C で除去 → 顧客 wan がログインできるように
  11. client_ui.py の render_parent_passphrase_page(dev=None) による NoneType crash を修正
  12. get_contract_json で WHERE deleted_at IS NULL を撤廃 + HTTPException 隔離を追加 — terminated 契約も admin が履歴閲覧可能に
  13. validate_nc_username の policy を緩和 — アンダースコア / ハイフン許可
  14. ui_admin の confirm() ダイアログから user-data 補間を除去 (XSS ベクタ封じ)

⏱ 永続化 (systemd timer)

  • mikyun-traffic-collect.timer — 60 秒毎に traffic_collect_v2.py 実行 (顧客視点 rx=download / tx=upload swap 済、delta + counter reset 検知付き)
  • mikyun-traffic-rollover.timer — hourly check で 24h / 7d カウンタを境界跨いだら自動リセット
  • 全 115 QR を qrencode で再生成 + 孤児ファイルを掃除 (conf = qr = 115 件で整合)

💳 顧客カード決済フロー検証

WordPress (R640 上 astra-child theme の functions.php) から portal への接続が 10.250.0.2:9000 (Phase 2+3 で消失した WG-storage ルート) を使っていたため、verify-contract, purchase-points, create-subscription, check-username, my-billing の全 WP REST 経路が「接続エラー」になっていた件を判明。10 か所すべて 192.168.10.31:9000 (maguro LAN 直接) に書き換えて即解決。

  • 新規契約 (Stripe → /ui/contracts/new → engine /contracts/create) → contract + device + NC アカウント + conf + QR まで自動生成 OK
  • サンキューページ (?contract_id=XXX) → /ui/contracts/{id}/info 経由で NC user / password を取得して顧客に表示 OK
  • 継続ポイント購入 (/point-my-page/) → verify → Stripe payment_intent → /api/points/charge → 残高加算 OK
  • 顧客 /client/login (passphrase 認証) → 契約管理ページ表示 OK
  • 顧客 260403-865C wan さんに +59,800pt (10 ヶ月分) を管理者手動チャージ済 (tx#7)

📱 モバイル対応

顧客向け portal の 3 ページ (/point-my-page/, /my-page/, /thanks/) が固定幅 700-800px でスマホで窮屈だった件。wp_head フックで @media (max-width: 768px) ルールを注入。

  • コンテナ幅 100% / 左右 margin 8px に
  • 3 列 grid (残高/月額/次回支払) を縦 1 列にスタック
  • label / value の横並びを縦並びに
  • ボタン min-height 48px (タップ領域確保)
  • input font-size 16px 維持 (iOS 自動ズーム防止)
  • テーブル横スクロール対応
  • WP-super-cache パージ済

🔧 その他の点検結果

  • DNS は正常 — katuocloud.com は 114.164.250.167 (現在の自宅 WAN IP) と一致、外部 (Tokyo VPS) から HTTP 200 で WP 表示確認済。当初「DNS ステール」と判定したのは Windows 端末からの NAT hairpin による誤検知。
  • WP_DEBUG=false 化を R640 本番 + xserver VPS 控え 両方で適用
  • ポイント計算 calc_monthly_amount(storage_gb, max_devices) = 1980 + (max_devices-3)×500 + ((storage_gb-200)/100)×500 が DB 値と完全一致 (260403-865C で 1000GB×3端末 = 5,980 円、テスト契約 500GB×5端末 = 4,480 円)
  • 契約容量整合 — 親 + 子の storage_quota_gb 合計が contracts.storage_quota_gb と一致 (1000GB = 980 + 10 + 10)
  • point_transactions 元帳 も balance_after が累積一致

⚠️ 残課題 (別セッションで対応可)

  • create_contract_and_parent の NC 作成失敗時の完全 rollback (現状は部分コミット可能性)
  • hirame@192.168.10.40 の authorized_keys に maguro 公開鍵を追加 (dashboard で hirame stats 取得用、別途パスワード要)
  • Stripe public key を functions.php から env 化
  • ui_admin ui_billing の N+1 query 最適化
  • /check-username JS 事前バリデーション (WP signup form 側、policy 緩和で必須ではない)
  • R640 別 Code-kun セッションでの Cockpit / Portainer / NPM インストール作業との同時進行ガード (互いの作業領域を住み分け済み)

🗂 バックアップ場所

  • maguro: /etc/wireguard/wg-clients.conf.bak.audit-20260515-013213 (peer 復旧前)
  • maguro: /root/mikyun-cloud/mikyun-portal/app/main.py.bak.thorough-20260515-033634 等
  • maguro: /root/mikyun-cloud/mikyun-portal/mikyun_vpn_engine.py.bak.thorough-20260515-033634 等
  • R640: /var/www/katuocloud/wp-content/themes/astra-child/functions.php.bak.audit-20260515
  • R640: /var/www/katuocloud/wp-config.php.bak.audit-20260515
  • xserver VPS: /var/www/katuocloud/wp-config.php.bak.audit-20260515 (xserver 解約までの控え)

作成: コード君 (Claude) / 2026-05-15