みーきゅんクラウド ポータル — 運営/顧客/VPNエンジン

// PORTAL / mikyun-portal — 最終更新: 2026-05-14 / maguro Docker稼働 (mikyun-portal:1.1)

運用中 maguro Docker 全面移植完了 (Phase 2 / 2026-05-14) 3層構成 /ui (運営) + /client (顧客) + VPN Engine

稼働形態 (2026-05-14 現在)
サービス所在ポート備考
mikyun-portal (FastAPI)maguro Docker (mikyun-portal:1.1)9000 (UI) / 9081 (Engine)運営ポータル + 顧客ポータル + VPN Engine を 1イメージに統合
MariaDB (mikyun_cloud)maguro Docker compose-db-1内部のみNextcloud と同一スタックへ統合 (旧ホストMariaDBは disable)
WireGuardmaguro ホスト (wg-clients / wg-customers / wg-storage)UDP 54024 / 61001 / 62001VPN Engine が wg syncconf でピア管理
歴史: 旧 R640 ホストで mikyun-cloud.service + mikyun-vpn-engine.service として動いていたものを、 2026-05-14 の Phase 2 で maguro Docker (mikyun-portal:1.1, ポート 9000 + 9081) に統合移植。 ホスト直の systemd ユニットは退役。
アクセス方法
用途URL備考
運営ポータル (内部 LAN)https://cloud.k2-o.net/ui/contractsmaguro nginx の location allow で内部VLAN20/30/WG (10.200.0.0/16) を許可
運営ポータル (外出先)https://admin.wan-secure.net/ui/contractsWireGuard wg-clients 接続必須
顧客ポータルhttps://cloud.k2-o.net/client顧客はWG接続 → 親端末IPで自動判定 → パスフレーズ認証

Basic認証: admin / .env に保管 (ADMIN_PASS)。 旧ハードコードは 2026-04-06 のセキュリティ修正で撤去済。

運営ポータル (/ui) 全機能

認証: HTTP Basic (ADMIN_USER/ADMIN_PASS 環境変数)

nginx 側 IP制限: 192.168.20.0/24, 192.168.30.0/24, 10.200.0.0/16 (wg-clients), 127.0.0.1, fabric subnets

PathMethod機能
/GETHOME (ユーザー数 / オンライン / 累計トラフィック上位)
/ui/contractsGET契約一覧 (DB直読、 状態 / 容量 / 稼働端末数 / 使用量GB)
/ui/contracts/newGET / POST新規契約作成 → VPN Engine /contracts/create 委譲
/ui/contracts/{id}GET契約詳細 (親端末カード + 子端末一覧)
/ui/contracts/{id}/add-childPOST子端末追加 → /devices/add-child
/ui/contracts/{id}/promote-parentPOST親端末昇格
/ui/contracts/{id}/terminate-confirmGET契約解除確認
/ui/contracts/{id}/terminatePOST解除実行 (端末 paused + WGピア削除 + NC完全削除)
/ui/billingGET入金管理 (ポイント残高 / 警告 / suspended 一覧、 2026-04-10 追加)
/ui/contracts/{id}/billingGET個別契約の課金履歴
/ui/trafficGET全端末のリアルタイム bps + 24h/7d/月次/累計
/ui/notesGET / POST顧客メモ (メモ / トラブル / 通話 / 警告)
/healthGET監視用 (認証不要)
顧客ポータル (/client) 全機能

認証: WireGuard 接続元IP (10.200.0.0/16) で端末特定 + identity_passphrase 追加認証。 親端末のみ管理画面、 子端末は NC へリダイレクト。

PathMethod機能
/clientGETログイン (親端末→パスフレーズ入力、 子端末→NCリダイレクト)
/client/loginPOSTidentity_passphrase 照合
/client/devices/{contract_id}GET端末一覧 + 課金カード (残高/月額/次回支払日)
/client/add-device/{contract_id}GET / POST子端末追加 (NCアカウント任意作成、 残容量<20GB は不可)
/client/pause|resume|promote|delete/{device_id}POST各種端末操作 (削除時は親端末へ10GB返却)
/client/qr/{contract_id}/{device_id}GETWG設定QR (24h有効を警告)
/client/conf/{device_id}GETconf ダウンロード
VPN Engine API (内部 :9081)

バインド: 127.0.0.1:9081 (Docker内ネット経由) / 外部からは nginx + WGで二重制限
ファイル: mikyun-portal イメージ内 /opt/mikyun-portal/mikyun_vpn_engine.py

PathMethod用途
/healthGET生存確認
/contracts/createPOST契約作成 + 親端末WGピア + conf/QR + NCアカウント作成
/contracts/listGET契約一覧
/contracts/{contract_id}GET契約詳細 (NC情報含む)
/contracts/{contract_id}/devicesGET端末一覧
/devices/add-childPOST子端末追加 (IP採番 + WGピア)
/devices/promote-parentPOST親端末昇格 (旧親はchild降格)
/devices/pause|resume|deletePOST停止/復帰/削除 + 親端末容量返却
ディレクトリ構造 (maguro)
/opt/mikyun-portal/                  ← Docker bind ソース (ホスト側)
├── docker-compose.yml               ← compose 定義 (mikyun-portal サービス)
├── .env                              ← DB / Admin / Stripe 秘匿値 (mode 600)
├── app/
│   ├── main.py                       ← FastAPI エントリ + Basic認証ミドルウェア
│   ├── ui_admin.py                   ← 運営ポータルUI
│   ├── api_users.py                  ← /api/users (Basic認証必須)
│   └── client/
│       └── client_ui.py              ← 顧客ポータルUI
├── mikyun_vpn_engine.py              ← VPN Engine 本体
└── billing/
    └── billing_daily.py              ← 月次ポイント処理 (2026-04-10〜)

# WG関連 (ホスト)
/etc/wireguard/wg-clients.conf       ← 10.200.0.0/16 / UDP 54024
/etc/wireguard/wg-customers.conf     ← UDP 61001
/etc/wireguard/wg-storage.conf       ← UDP 62001

# 出力
/opt/mikyun/output/client-configs/   ← .conf
/opt/mikyun/output/qr/               ← .png

# systemd (ホスト側)
/etc/systemd/system/billing-daily.timer
/etc/systemd/system/billing-daily.service
旧パスは無効: 旧 R640 ホストの /root/mikyun-cloud/、 /root/mikyun-cloud/mikyun-portal/、 /root/mikyun-cloud/app/ はすべて R640再構築 (2026-05-14) で消滅。 現行ソースは maguro /opt/mikyun-portal/ のみが正。
セキュリティ修正履歴 (継続)
日付項目修正前修正後
2026-04-06MySQL 外部公開0.0.0.0:3306127.0.0.1:3306 (現在は Docker 内部ネットのみ)
2026-04-06VPN Engine 外部公開0.0.0.0:9081127.0.0.1:9081
2026-04-06DB / Adminパスワードハードコード.env 読み込み (mode 600)
2026-04-06WG鍵パーミッション644600 (root only)
2026-04-06/api/users認証なしBasic認証必須
2026-05-14本番経路R640 ホスト直 systemdmaguro Docker (mikyun-portal:1.1)
NC強制パスワード変更機能 (2026-04-03 修正記録)

運営ポータルから NCユーザーのパスワードを強制変更する機能のバグ修正記録。 教訓として残す。

// 問題1: www-data ユーザー指定の欠如

Nextcloud の occ は www-data ユーザー実行が必須。 docker exec に -u www-data を付けていなかった。

# NG
docker exec -e OC_PASS=xxx nextcloud-stack-app-1 php occ user:resetpassword

# OK
docker exec -u www-data -e OC_PASS=xxx nextcloud-stack-app-1 php occ user:resetpassword
// 問題2: HaveIBeenPwned ポリシーで一般的なパスワードが拒否

運営強制変更の前後で enforceHaveIBeenPwned を一時的に 0 にし、 変更後 1 に戻す。 例外時も必ず戻す try/finally で保護。

# パスワードポリシー一時無効化
docker exec -u www-data nextcloud-stack-app-1 \
  php occ config:app:set password_policy enforceHaveIBeenPwned --value=0

# 強制変更
docker exec -u www-data -e OC_PASS="$PW" nextcloud-stack-app-1 \
  php occ user:resetpassword --password-from-env "$NC_USER"

# 再有効化 (必ず)
docker exec -u www-data nextcloud-stack-app-1 \
  php occ config:app:set password_policy enforceHaveIBeenPwned --value=1
使用量表示 (2026-04時点で実装中だった機能 → 現状)

NC使用量を customer_devices.nc_used_bytes に1時間ごと収集して契約一覧に表示する機能。 maguro Docker 移植時に /opt/mikyun-portal/scripts/collect_file_count.py として cron 化済。

# cron (maguro)
0 * * * * docker exec mikyun-portal python3 /opt/scripts/collect_file_count.py >> /var/log/storage_collect.log 2>&1
注意点 (教訓として残す): 旧 R640時代に /root/mikyun-cloud/app/ui_admin.py (古いコピー) と /root/mikyun-cloud/mikyun-portal/app/ui_admin.py (本番) が両方存在し、 編集ミスで反映されない事故があった。 Docker 化後は /opt/mikyun-portal/app/ui_admin.py 一本に統一済。
サービス管理コマンド (maguro)
# コンテナ再起動 (UI + Engine 同一コンテナ)
docker compose -f /opt/mikyun-portal/docker-compose.yml restart mikyun-portal

# ログ
docker logs -f --tail=200 mikyun-portal

# DB
docker compose -f /opt/nextcloud/compose/compose.yml exec db mysql -u root -p mikyun_cloud

# 月次バッチ
systemctl status billing-daily.timer
journalctl -u billing-daily.service -n 50
パスワード保管
種類場所
DB / Admin / Stripe/opt/mikyun-portal/.env (mode 600, root)
WG鍵 (peer)/etc/wireguard/peers/ (mode 600)
顧客 identity_passphraseDB contracts.identity_passphrase (将来 Argon2 ハッシュ化検討)
NC親アカウントPWDB contracts.nc_password (現状平文、 改善TODO)
残る課題: NCパスワード/identity_passphrase の DB平文保管は SQLite時代の名残。 Fernet暗号化 + Argon2 検証に統一する計画あり (優先度: 中)。
関連