コンテンツにスキップ

JWNET 標準EDI — GCP 環境構築と Biware 導入・接続テスト

標準EDI(拡張Z手順)の常駐環境を GCP に建て、EDI クライアント Biware 全銀TCPクライアント (インターコム)を評価版から導入し、JWNET 接続テストまで到達するための手順書。

  • 方式選定の経緯と候補比較は docs/superpowers/specs/2026-09-01-jwnet-edi-client-selection.md (JEITA 共通クライアント不適合の裁定・価格・JWNET 窓口への照会文面ドラフトを含む)
  • 機能側の仕様は マニフェスト・JWNET (標準EDI)
  • 通信要件の正典は接続仕様書ダイジェスト(docs/superpowers/specs/2026-07-13-jwnet-edi-spec-digest.md

操作環境(この手順書の前提)

GCP の操作はブラウザ(コンソール + Cloud Shell)で完結する。Windows VM のデスクトップ操作だけは Mac の Windows App と gcloud CLI を使う (方針 2026-09-08: Chrome リモート デスクトップは使わない。RDP はインターネットに開けず、IAP TCP forwarding のトンネル越しに接続する)。

操作 使うもの
GCP の設定・コマンド実行 GCP コンソール(Web UI)と Cloud Shell(コンソール右上の >_ アイコン。gcloud 認証済みのブラウザ内ターミナル)
Windows VM のデスクトップ操作(Biware のインストール・設定) Windows App(Mac の App Store・Microsoft 提供・無料)+ IAP トンネル(Mac のターミナルで gcloud compute start-iap-tunnel jwnet-gw-vm 3389 --local-host-port=localhost:13389 --zone=asia-northeast1-b を実行したまま、Windows App で localhost:13389 へ接続)
初回だけ: Mac の準備 Windows App の導入(App Store)と gcloud CLI の導入brew install --cask google-cloud-sdkgcloud auth logingcloud config set project <プロジェクトID>)。IAP トンネルは Cloud Shell からは張れない(Mac の localhost へ転送するため)

Cloud Shell の性質

Cloud Shell は無料で、gcloud がログイン済みで入っている。無操作 20 分ほどで セッションが切れるが、再度 >_ を押せば続きから使える(ホームディレクトリは保持)。 以下のコードブロックは、「Mac のターミナル」と明記したもの(IAP トンネル)以外はすべて Cloud Shell に貼り付けて実行する。

全体像

[GCP asia-northeast1 (東京) / VPC jwnet-vpc / subnet jwnet-ipsec-subnet (10.100.121.0/24)]

  [Windows VM: Biware]              [strongSwan ルータ VM]
   例 10.100.121.10                  NIC 例 10.100.121.2・IP forward + SNAT
   受信は IAP(SSH/RDP)のみ            ├ IPsec 終端(IKEv2/ESP)  ══════ [JWNET センター]
   操作は Windows App(IAP 3389)       │   to 210.164.154.13            210.164.154.26:5020
   Biware(拡張Z手順→5020)            │   発信先 210.164.154.24/30
   C:\jwnet\{outbox,inbox,...}       │   発信元 10.100.121.0/24
        └ 210.164.154.24/30 宛を ──▶ └ src を 10.100.121.1 へ SNAT
           strongSwan VM へルート        egress は安定 IP で UDP500/4500(NAT-T)
   (統合フェーズ) Node 常駐ゲートウェイは Windows VM に同居

通信要件(接続仕様書ダイジェスト + 設定票): 全銀協標準通信プロトコル(TCP/IP版)拡張Z手順 / port 5020 固定 / 最大レコード長 32,000 byte(通信仕様の上限。Biware に入れるレコード長は設定票どおり 4,000・不定長 — 窓口 Q9 回答 2026-09-11) / 転送形式 テキスト / NW 暗号化 IPsec(IKEv2)。 IPsec とファイル交換の確定パラメータは 接続テスト構成メモ(docs/superpowers/specs/2026-09-02-jwnet-edi-connection-test-config.md・内部文書・サイト非公開)が権威。

設定票の秘密値は手入力・リポジトリに書かない

全銀パスワード / ファイルアクセスキー / IPsec 事前共有鍵(PSK) / センター確認コードは 秘密。設定票原本(返却義務・盗難紛失注意)と GCP Secret Manager だけを保管先とし、 VM へは設定票を見て手入力する。本手順書には値を書かず「設定票の〈欄名〉」で参照する。

接続テスト日程は確定済み(バッファ薄)

設定票(2026-08-31 発行)で 接続テスト期間 = 2026-10-05〜2026-10-18 が確定した。 この設定票は接続テスト専用・期間限定で、本番用の値は接続テスト通過後に別票で発行される。 10-05 までに Phase A〜E を「値を入れれば動く」状態まで仕上げる(strongSwan の追加分だけ 工数が増える)。トンネル確立の実確認はセンター側の都合で 10-05 以降になりうる。

Phase A — プロジェクト作成(GCP コンソール)

既存の OCR プロジェクトとは別に作る

WasteLink の OCR(Vision API)用プロジェクトが既にあるが、EDI は専用プロジェクト kyoei-jwnet-gw を新設する。理由: (1) EDI 側の VM は JWNET の接続資格情報と IPsec トンネルの終端を持ち、OCR 側の API キー/サービスアカウントとは権限の境界を 分けたい(プロジェクト = GCP の信頼境界)。(2) ゲートウェイは JWNET と Supabase としか 通信せず、Vision と共有するものが何もない。(3) 常駐インフラ(月額)と従量課金の コストを分けて見たい。課金アカウントは既存プロジェクトと同じものに紐付けるので 支払いは 1 本のまま。

  1. GCP コンソールを開き、画面上部の プロジェクト セレクタ → 「新しいプロジェクト」:
    • プロジェクト名: JWNET Gateway / プロジェクト ID: kyoei-jwnet-gw
    • 請求先アカウント: 既存 OCR プロジェクトと同じものを選択
  2. 作成後、プロジェクト セレクタで kyoei-jwnet-gw切り替える (以降の操作はすべてこのプロジェクトで行う。左上の表示で常に確認)。
  3. コンソール右上の >_(Cloud Shell をアクティブにする) を押し、 開いたターミナルで対象プロジェクトを固定して API を有効化する。

    gcloud config set project kyoei-jwnet-gw
    gcloud services enable compute.googleapis.com iap.googleapis.com
    

Phase B — ネットワーク(Cloud Shell)

  1. カスタム VPC とサブネット(東京)を作る。サブネットは設定票の発信元レンジに合わせて 10.100.121.0/24(旧手順の 10.10.0.0/24 から変更)。

    gcloud compute networks create jwnet-vpc --subnet-mode=custom
    gcloud compute networks subnets create jwnet-ipsec-subnet \
      --network=jwnet-vpc --region=asia-northeast1 --range=10.100.121.0/24
    

    既存プロジェクトは差分だけを適用する

    kyoei-jwnet-gw では旧 jwnet-subnet10.10.0.0/24)が残ったまま、 10.100.121.0/24jwnet-ipsec-subnet として追加済み(2026-09-07 実測)。 作成済み資源を再作成しない。Windows VM が旧サブネットに居る場合は strongSwan runbook §2 の差分適用(移設)を行う。

  2. 管理用 SSH と Windows VM の RDP を IAP レンジからだけ許可する(Cloud Shell からの gcloud compute ssh と Windows App の IAP トンネルがこの経路を使う。公開網からは到達できない)。RDP の FW は target tag jwnet-biware を 付けた VM にだけ効くので、Phase C 手順 1 の VM 作成で同じタグを付ける。既存環境で allow-iap-rdp が既にあれば作らない。

    gcloud compute firewall-rules create allow-iap-ssh \
      --network=jwnet-vpc --direction=INGRESS --action=ALLOW \
      --rules=tcp:22 --source-ranges=35.235.240.0/20
    gcloud compute firewall-rules describe allow-iap-rdp --format='value(name)' 2>/dev/null \
      || gcloud compute firewall-rules create allow-iap-rdp \
           --network=jwnet-vpc --direction=INGRESS --action=ALLOW \
           --rules=tcp:3389 --source-ranges=35.235.240.0/20 --target-tags=jwnet-biware
    
  3. JWNET センターの IPsec ルータからの IKE / NAT-T だけを ingress 許可する (当方が initiator なので戻りは stateful に通るが、IKE の双方向性のため明示許可する)。

    gcloud compute firewall-rules create allow-jwnet-ipsec \
      --network=jwnet-vpc --direction=INGRESS --action=ALLOW \
      --rules=udp:500,udp:4500,esp --source-ranges=210.164.154.13/32
    
  4. 外部 IP なし VM の Windows Update / パッケージ取得用に Cloud NAT を作る。

    既存環境では存在を先に確認する(2026-09-07 の実測では未確認)。 PROJECT_ID は対象プロジェクトの非秘密 ID を設定する。

    export PROJECT_ID='kyoei-jwnet-gw'
    gcloud compute routers nats list --router=jwnet-router --region=asia-northeast1 --project="$PROJECT_ID"
    gcloud compute routers nats describe jwnet-nat --router=jwnet-router --region=asia-northeast1 --project="$PROJECT_ID" --format='value(sourceSubnetworkIpRangesToNat)'
    

    ALL_SUBNETWORKS_ALL_IP_RANGES なら全サブネットが対象である。未作成の場合だけ以下を実行する。

    gcloud compute routers create jwnet-router --network=jwnet-vpc --region=asia-northeast1
    gcloud compute routers nats create jwnet-nat --router=jwnet-router \
      --region=asia-northeast1 --auto-allocate-nat-external-ips --nat-all-subnet-ip-ranges
    

固定グローバル IP の JWNET 事前届出は不要

当方 peer は IPsec ID(FQDN 形式・名前解決不要) で識別され、当方の実グローバル IP は 「NAT 前 発信元=要求ファイルの送信元 IP」として JWNET 側で受ける(設定票)。そのため グローバル IP を JWNET へ届け出る必要はない。ただし JWNET 側 ACL が当方 IP を絞る可能性は 残るので、strongSwan VM の egress は安定 IP(VM の static 外部 IP か Cloud NAT の 予約 IP)にし、「当方グローバル IP の登録要否」は接続テスト前に窓口へ一度確認する。

受信ポートは管理 SSH と JWNET IPsec だけ

Windows VM への RDP(3389) はインターネットに開けない。IAP レンジ(35.235.240.0/20)からの 3389 だけを allow-iap-rdp(target tag jwnet-biware)で許可し、Mac から gcloud compute start-iap-tunnel でトンネルして Windows App で接続する(付録 A)。 ingress は IAP(SSH / RDP)と、上の JWNET センター IP 限定 IPsec と、 Phase E-2.7 の IPsec 状態エンドポイント(allow-gw-ipsec-status・ 送信元はゲートウェイ VM の内部 IP /32・宛先タグ jwnet-ipsec の TCP 8788)と、 E-2.11 の外形監視(jwnet-allow-router-health・送信元はルータ VM の 内部 IP /32・宛先タグ jwnet-biware の TCP 8787)のみ。 特定 /32 からの常設 RDP 許可(旧 allow-my-rdp・旧付録 A が作っていた一時 FW tmp-bootstrap-rdp)は接続テストに不要で、残っていれば NAT の有無に関係なく外す。 Windows の外部 IP は Cloud NAT が確認できた場合だけ外す(外向き通信のため)。手順は strongSwan runbook §2 手順 5を参照する。

Phase C — Windows VM(Cloud Shell + Mac の Windows App)

  1. Windows Server 2022 の VM(e2-medium・外部 IP なし・RDP は IAP 経由のみ・Windows の SSH は有効化しない)を作る。 target tag jwnet-biware で Phase B 手順 2 の allow-iap-rdp がこの VM に効く。

    gcloud compute instances create jwnet-gw-vm \
      --zone=asia-northeast1-b --machine-type=e2-medium \
      --image-family=windows-2022 --image-project=windows-cloud \
      --boot-disk-size=50GB --boot-disk-type=pd-balanced \
      --network-interface=subnet=jwnet-ipsec-subnet,no-address \
      --tags=jwnet-biware \
      --shielded-secure-boot
    

    既存 VM は再作成せず、strongSwan runbook §2 の差分適用で移設する。

  2. Windows のユーザーとパスワードを発行して控える(パスワードは一度しか表示されない。 このパスワードは後で Windows App のサインインに使う。出力は Cloud Shell 内に留め、文書やチャットへ転記しない)。

    gcloud compute reset-windows-password jwnet-gw-vm \
      --zone=asia-northeast1-b --user=jwnetadmin
    
  3. Windows App で接続する(Mac・IAP トンネル経由。詳細と切り分けは付録 A)。 Windows VM 側に追加のインストールは要らない(Chrome リモート デスクトップは使わない)。

    1. 前提の FW とタグを用意する(Cloud Shell)。allow-iap-rdp35.235.240.0/20 → tcp:3389・ target tag jwnet-biware で存在し、jwnet-gw-vm にそのタグが付いていることを確認する。

      gcloud compute firewall-rules describe allow-iap-rdp --format='value(sourceRanges,allowed,targetTags)'
      gcloud compute instances describe jwnet-gw-vm --zone=asia-northeast1-b --format='value(tags.items)'
      

    無ければここで作る(既存の FW / VM は再作成しない。タグの追加は冪等で、既存のタグは残る)。

    ```bash
    gcloud compute firewall-rules describe allow-iap-rdp --format='value(name)' 2>/dev/null \
      || gcloud compute firewall-rules create allow-iap-rdp \
           --network=jwnet-vpc --direction=INGRESS --action=ALLOW \
           --rules=tcp:3389 --source-ranges=35.235.240.0/20 --target-tags=jwnet-biware
    gcloud compute instances add-tags jwnet-gw-vm --zone=asia-northeast1-b --tags=jwnet-biware
    ```
    
    1. Mac のターミナルでトンネルを張る(実行したまま次へ。止めるときは Ctrl+C)。

      gcloud compute start-iap-tunnel jwnet-gw-vm 3389 \
        --local-host-port=localhost:13389 --zone=asia-northeast1-b
      

      Listening on port [13389] が出れば成功。ポートが使用中なら 13390 など別の番号に変える。 3. Windows App を起動 → 上部の 「+」→「PC の追加」「PC 名」に localhost:13389 → 「追加」。 タイルをダブルクリック → ユーザー名 jwnetadmin と手順 2 のパスワードを入力する。 「証明書を検証できません」は「続行」(GCP の VM は自己署名証明書のため。この文脈では問題ない)。 4. Windows のデスクトップが表示されれば完了。以後の VM 操作はすべてこの手順で行う。 接続できるのは IAP のトンネル権限(roles/iap.tunnelResourceAccessor)を持つ GCP アカウントだけ。

    Windows への SSH は使わない

    Windows VM の SSH(enable-windows-ssh)は新規 VM で有効化せず、既存 VM でも使わない。ファイルの受け渡しは、VM 内のブラウザ(Edge)で Greco / 申込メールからダウンロードするか、Windows App のクリップボード共有で行う(scp は使えない)。

  4. Windows 初期設定(Windows App で接続して実施):

    1. システムロケールを日本語にする(Biware を入れる前に必ず)。 GCP の Windows イメージは 英語版で、Biware は Shift_JIS 前提の日本語アプリ(非 Unicode)のため、このままだと 画面もインストール先のフォルダ名も文字化けする。管理者 PowerShell (スタート右クリック → Windows PowerShell (Admin))で 1 行ずつ実行し、最後に再起動する。

      Set-WinSystemLocale ja-JP
      Set-Culture ja-JP
      Set-WinHomeLocation -GeoId 122
      Set-TimeZone -Id "Tokyo Standard Time"
      DISM /Online /Add-Capability /CapabilityName:Language.Basic~~~ja-JP~0.0.1.0
      DISM /Online /Add-Capability /CapabilityName:Language.Fonts.Jpan~~~und-JPAN~0.0.1.0
      Restart-Computer
      
      • 1 行目が本命(非 Unicode プログラムの文字コードを CP932 にする・再起動後に有効)。 DISM の 2 行は日本語の言語基盤とフォントの追加(数分・Cloud NAT 経由で取得)。
      • コントロール パネル → 地域 → 管理 → 「システム ロケールの変更」が 日本語 (日本) になっていれば OK。「ベータ: ワールドワイド言語サポートで Unicode UTF-8 を使用」には チェックを入れない(Shift_JIS アプリが壊れる)。
      • ロケール変更より前に Biware を入れてしまった場合は、再起動後に一度アンインストール して入れ直す(アプリ内の文字は直るが、インストール時に化けたフォルダ名・ ショートカット名・設定ファイルは自動では直らない)。
        1. gpedit.msc → Windows Update → 自動再起動を 0:00–4:00 に制限 (JWNET 本番運用時間 4:00–24:00 の外へ逃がす)
        2. フォルダ作成: C:\jwnet\outbox / C:\jwnet\inbox / C:\jwnet\archive / C:\jwnet\error

Phase D — Biware 評価版の導入(VM 内・ブラウザ経由)

評価版の条件(2026-09-01 実測): 30 日・機能制限なし・商品版と同等サポート・ 自動有料切替なし。申込は法人メール必須。正式版は 86,000 円(税抜・初年度保守込み)+ 保守更新 12,000 円/年。

  1. トライアルページから 会社メールで申込 → ダウンロード URL がメールで届く。
  2. Windows App で VM に入り、VM 内のブラウザ(Edge)で申込メールの ダウンロード URL を直接開いてインストーラを取得する(Mac からファイルを運ぶ必要はない。 VM は Cloud NAT 経由で外向き通信できる)。
  3. 管理者権限でインストールする。試用ライセンスの投入は申込メールの案内に従う (画面構成はバージョン差があるため同梱マニュアルが正)。
  4. 通信設定を入れる。

    設定項目
    通信手順 全銀TCP/IP手順(広域IP網) = 拡張Z手順・ベーシック手順
    接続先 IP 210.164.154.26(情報処理センター側)
    接続先ポート 5020
    転送形式 テキスト
    レコード長 4,000・不定長(Q9 回答: 設定票が正。仕様書 §3-3(4) No.14 の 32,000 は Biware へ入れる値ではない)
    要求(送信)ファイル名 JW9810FCHKD1(レコード長 4,000・不定長・圧縮あり)
    結果(受信)ファイル名 JW9810CCHKR1(レコード長 4,000・不定長・圧縮あり)
    連続受信回数 15
    パスワード / ファイルアクセスキー / センター確認コード 設定票を見て手入力(秘密・本書に値を書かない)
    送信フォルダ C:\jwnet\outbox
    受信フォルダ C:\jwnet\inbox

    非秘密パラメータの一覧は 接続テスト構成メモ §1(docs/superpowers/specs/2026-09-02-jwnet-edi-connection-test-config.md・内部文書・サイト非公開)。 秘密(パスワード・アクセスキー・確認コード)は EDIサーバ設定票の該当欄の値をそのまま入れる。

  5. センター名・業務名を登録し、その名前を E-2.3 / nssm の -H -W に入れる。 推奨の名前はプレースホルダのまま(センター <センター名>・送信業務 <送信業務名>・ 受信業務 <受信業務名>。実値は設定票由来なので書かない)。

    • センター名と業務名は -H -W厳密一致させる。大小文字を区別し、 半角 50 桁以内・同名の複数登録不可。センター名は設定や通信履歴の管理用で、 情報として送受信されることはない。不一致は終了コード 1009 (指定されたセンター名または業務名が存在しない)
    • 送信用・受信用の別業務 2 つとして登録する。「複数転送 1 業務」は検討のうえ 採らない(標準EDI 仕様書 3-18 No.16 マルチファイル転送 = なし 固定・ ゲートウェイは 1 要求 = 1 送信 = 1 受信の直列。複数結果の取り扱いは wl-b4r のコード修正待ち)
    • 通信ファイル登録の「送信・受信」は送信 / 受信 / 複数転送のいずれかを指定する。 受信業務には電文区分標識(開始要求/再送要求)と受信ファイル名がある。 送信業務で受信を叩くと 1013(受信ファイル名が指定されていません)系になる
    • [センター情報]タブの電話番号は空欄にする(全銀協 TCP/IP 手順で LAN 接続する 場合の登録条件)。同期通信環境設定は触らない(全銀協手順(モデム)用。 TCP/IP のみなら不要)
    • 操作パスワードは設定しない。設定すると全銀エクスプローラのダイアログが パスワード保護され、コマンドライン実行は 1008(パスワード確認失敗)になる。 サイレント /S は入力を求められない
    • 受信業務の電文区分標識は「開始要求」固定/Q を使わない (標準EDI 仕様書 3-18 No.19「再送要求に対応しない」・3-19 (7)-4。 再送要求はセンター非対応で結果ファイルを受信不能になる)
    • 詳細設定①「TTC 接続形態区分」は 「ベーシック手順」 を選ぶ(2026-09-21 確定) (JWNET 仕様書 §3-17 (4)① の No.1「プロトコル種別」=ベーシック手順 〈備考「『PC手順』への変更は可能です」〉。Biware 側は同じものを 「TTC 接続形態区分」という名前で持っており、クライアント説明書 6-24 が 「ホスト-端末間は『パソコン手順』、ホスト-ホスト間は『ベーシック手順』」と定義している。 JWNET 窓口は 2026-09-21 に「設定票に記載通りの設定をしてください/外部パッケージの 設定方法はわかりかねます」と回答しており、JWNET 側はこの項目を別途指定していない。 指令ファイルで直接指定する場合のコードは '1'〈全銀協ベーシック手順との接続〉。 '0' とスペースは既定のパソコン手順側なので使わない — プログラミング編 6-55)
    • 動作環境設定「同時通信を行う」は OFF に固定 (STATUS.BIZ の所在が 1 箇所に定まる。ゲートウェイは直列に動く)
  6. センター未接続でも確認できること: 送信フォルダにテキストファイルを置く → ジョブ起動 → ログとエラー時のファイルの行き先。ここでゲートウェイとのファイル受け渡し仕様 (ファイル名規則・完了検知)を確定させる。

  7. Biware を自動起動(サービス登録またはタスクスケジューラ)にし、VM 再起動後も 立ち上がることを確認する。
  8. 再インストールの手順(H8・9/29。評価版の 30 日延長)。 バックアップには登録済みの秘密(パスワード・ファイルアクセスキー・確認コード)が 含まれる。シリアルナンバー・ライセンスキー・秘密の値は証跡・チャットに写さない

    1. [編集]→[設定ファイルのバックアップ]で VM 内に保存する (既定 <インストールフォルダ>¥Backup¥YYYYMMDD-HHMMSS。Mac へ持ち出さない)
    2. アンインストール → 再インストールする(ユーザー名・所属・シリアル番号は 導入ガイド 3-3 の案内で入力。使用許諾契約 第 3 条の機密として扱う)
    3. [編集]→[設定ファイルのインポート]する(フォルダを選び、上書き警告で OK)
    4. インストールフォルダの実パス(既定 ¥Intercom¥全銀クライアント。 半角カナ表記にバリエーションがあるため実機で確認)と Zexplorer.exe のパスSTATUS.BIZ の所在(制御ファイルと同じフォルダの 固定名。同時通信 OFF)を再確認し、違っていたら E-2.3 / nssm へ反映する
    5. バックアップフォルダを削除する(秘密の塊のため残さない)
    6. R-2 を再実行する(14 章の R-2 行に既記)

Phase E — IPsec(strongSwan ルータ VM・Cloud Shell + VM 内)

IPsec の実行手順は swanctl 版へ進む

先に strongSwan 5.9 / swanctl 実装手順 を開く。 下記は構成の旧概要であり、コマンドは実行しない。VM の内部 IP は使用可能な .2 へ訂正済み。 legacy ipsec コマンドと Windows の手動ルートは、リンク先の swanctl・VPC ルートを使う。

Cloud VPN 単体では組めない(strongSwan ルータ VM を使う)

設定票は (1) トンネル内 source NAT(当方発信元を 10.100.121.1/24 へ変換)、(2) FQDN 形式の IPsec ID、(3) PFS を使用しない、を要求する。GCP Cloud VPN はいずれも素直に扱えないため、 Linux VM に strongSwan を載せて IPsec を終端する。判断の根拠の詳細は 接続テスト構成メモ §2(docs/superpowers/specs/2026-09-02-jwnet-edi-connection-test-config.md・内部文書・サイト非公開)に記録している。

  1. strongSwan ルータ VM(Debian・NIC 内部 IP は例 10.100.121.2・IP 転送可・egress 安定用に static 外部 IP) を作る。NIC は空きを確認した使用可能なアドレスを使う。.1 は GCP のゲートウェイ予約のため NIC・alias・loopback には割り当てず、トンネル内 SNAT 後の発信元 10.100.121.1 として維持する。

    gcloud compute addresses create jwnet-ipsec-egress --region=asia-northeast1
    gcloud compute instances create jwnet-ipsec-vm \
      --zone=asia-northeast1-b --machine-type=e2-small \
      --image-family=debian-12 --image-project=debian-cloud --boot-disk-size=20GB \
      --network-interface=subnet=jwnet-ipsec-subnet,private-network-ip=10.100.121.2,address=jwnet-ipsec-egress \
      --can-ip-forward --shielded-secure-boot
    

    address= に対応する固定 IP の予約名は環境に合わせる(kyoei-jwnet-gwjwnet-vpn-ip)。 この旧概要の作成コマンドは実行せず、実際の作成は strongSwan runbook §2 のスクリプトで 予約を describe して IPv4 値を渡す。既存 kyoei-jwnet-gw のルータ VM は作成済みで再作成しない。

  2. VM に SSH(Cloud Shell から gcloud compute ssh jwnet-ipsec-vm --tunnel-through-iap)し、 IP 転送・strongSwan・source NAT を設定する。PSK 実値は設定票を見て投入(下記の <設定票のPSK> を置き換える。コミットしない・可能なら Secret Manager から読む)。

    sudo sysctl -w net.ipv4.ip_forward=1
    echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-ipfwd.conf
    sudo apt-get update && sudo apt-get install -y strongswan
    # source NAT: JWNET 宛の発信元を 10.100.121.1 に固定(設定票の NAT 後 発信元)
    sudo iptables -t nat -A POSTROUTING -d 210.164.154.24/30 -j SNAT --to-source 10.100.121.1
    
  3. /etc/ipsec.conf(または swanctl)に設定票 §1-3 の非秘密パラメータを入れる。要点のみ:

    strongSwan 項目 設定票の値
    keyexchange ikev2
    right(peer) 210.164.154.13
    leftid @AG3b5kF62ZcalzsnVIYBrWxvdw.local(FQDN 形式 IPsec ID)
    ike aes256-sha256-modp2048(DH group 14)
    esp aes256-sha256(PFS なし=modp を付けない
    leftsubnet / rightsubnet 10.100.121.0/24 / 210.164.154.24/30
    ikelifetime / lifetime 8h / 1h
    dpdaction / dpddelay / dpdtimeout restart / 30s / 90s
    authby secret(PSK は /etc/ipsec.secrets に設定票の値・平文コミット禁止

    strongSwan 5.9 の設定テンプレート・VM アドレスの予約に伴う補正・Secret Manager からの 起動時読込・VPC ルート・検証は strongSwan / swanctl 実装手順 に従う。IPsec 部分の実行順序とコマンドはリンク先を使用する。本表は設定票の対応を示す。

  4. Biware VM から JWNET 宛(210.164.154.24/30)を strongSwan VM へ流す経路は、VPC のカスタムルートscripts/jwnet-ipsec/gcloud/configure-network.shjwnet-to-center・Windows VM のタグにだけ適用・ 次ホップ = ルータ VM)で入れる。GCP では JWNET 宛のような他ネットワーク宛の既定経路はサブネットのゲートウェイ 10.100.121.1 に向き、 VPC のルート表が転送先(ルータ VM)を選ぶため、Windows 側の route -p add は不要で、.1 を次ホップにしても OS ルートだけでは strongSwan VM に向かない。OS の手動ルートは追加せず VPC ルートに一本化する。 kyoei-jwnet-gw には同等の to-jwnet-via-ipsec(タグ無し・priority 100)が既にあり、それを使う (CREATE_CENTER_ROUTE=0)。照合と差分適用は strongSwan runbook §2に従う。

  5. トンネルを上げて確認する(VM 内): sudo swanctl --load-all --nopromptsudo swanctl --initiate --child jwnetsudo swanctl --list-sas で ESTABLISHED / INSTALLED を確認(strongSwan runbook §7)。センター側がトンネルを受けるのは接続テスト期間(10-05〜10-18)内の 可能性があるため、期間前は設定投入までを済ませ、確立確認は期間開始後に行う。

Phase E-2 — 常駐ゲートウェイの配備(Windows VM・Greco 側)

Node 常駐ゲートウェイ(services/jwnet-gateway)を Biware と同じ Windows VM (jwnet-gw-vm)に配備し、Windows サービスとして常駐させる。 配備先は C:\greco\gateway(repo 展開先のルート) で以後通す。 動作の概要: Greco の接続テスト画面 → 送受信キュー(Supabase)→ ゲートウェイが claim → 復号 → 原本保存 → Defender スキャン → Biware で送信 → 5 分以上待つ → 新規要求で受信 → DB に記録 → 画面が判定を表示(Phase F の主経路)。

E-2.1 Node・pnpm・git・gcloud の導入(VM 内)

repo の .nvmrc(= 22)に合わせる。管理者 PowerShell で実行する。

winget install OpenJS.NodeJS.LTS --version 22.22.3 --silent
corepack enable
corepack prepare pnpm@9.15.0 --activate
winget install Git.Git --silent
winget install Google.CloudSDK --silent
node --version   # v22.x であること
pnpm --version   # 9.15.0 であること
  • winget が無い環境(古い App Installer)は公式 MSI(node.js の 22 系 LTS)を Edge で取得して導入し、同じ版確認を行う。
  • gcloud は配備物の取得と Secret Manager からの秘密取得に使う。 gcloud auth login(または VM のサービスアカウント)で kyoei-jwnet-gw を選ぶ。

E-2.2 成果物の持ち込みとビルド

VM 内で pnpm install + build する。node_modulesdist/ を OS 越しに コピーしない。 実測の根拠(2026-09-10・macOS 上の検証):

持ち込み候補 結果
dist/(125 ファイル・892KB)+ src/ts-loader.mjs + package.json だけ 起動しない(ERR_MODULE_NOT_FOUND: @workspace/shared
上記 + packages/shared の TS ソース + workspace symlink 起動しない(ERR_MODULE_NOT_FOUND: @supabase/supabase-js
pnpm --filter @waste-link/jwnet-gateway deploy の隔離成果物(337MB) 起動しない(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING@workspace/shared は TS ソース配布で、node_modules 下では型剥離が禁止される)
repo レイアウト + pnpm install --frozen-lockfile + build 起動する(env 不足時は変数名だけ出して fail-closed)

持ち込み経路は Cloud Storage 経由(zip 1 ファイル・チェックサム付き・ RDP のドライブ共有設定に依存しない。gcloud は E-2.1 でどのみち入れる)。 node_modules(フルで 1.2GB)は運ばず、VM 内で生成する。

  1. 手元(Mac)で release commit の zip を作り、非公開バケットへ置く。 commit SHA(短 SHA・版名に使う)と zip の SHA-256 (shasum -a 256 gateway-deploy.zip)を控える。

    git archive --format=zip -o gateway-deploy.zip <release-commit>
    gcloud storage buckets create gs://kyoei-jwnet-gw-deploy --project=kyoei-jwnet-gw --location=asia-northeast1 --uniform-bucket-level-access
    gcloud storage cp gateway-deploy.zip gs://kyoei-jwnet-gw-deploy/
    

    バケットは初回だけ作る。公開(allUsers)にしない。

  2. VM 内(管理者 PowerShell)で取得・展開・導入する。 取得した zip は版名つきで releases\ に保存し、稼働版の記録 (CURRENT.txt)と展開した版の印(VERSION.txt)を作る。

    $commit = '<release-commit の短 SHA>'
    gcloud storage cp gs://kyoei-jwnet-gw-deploy/gateway-deploy.zip C:\greco\gateway-deploy.zip
    $hash = (Get-FileHash C:\greco\gateway-deploy.zip -Algorithm SHA256).Hash
    # $hash が手順 1 で控えた SHA-256 と一致することを確認してから進む。
    New-Item C:\greco\releases -ItemType Directory -Force
    Copy-Item C:\greco\gateway-deploy.zip C:\greco\releases\gateway-deploy-$commit.zip
    Expand-Archive C:\greco\gateway-deploy.zip -DestinationPath C:\greco\gateway
    Remove-Item C:\greco\gateway-deploy.zip  # 用済みの一時名は残さない(古い zip の誤展開を防ぐ)。
    $commit | Set-Content C:\greco\gateway\VERSION.txt
    "$commit`n$hash`n$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Set-Content C:\greco\releases\CURRENT.txt
    cd C:\greco\gateway
    pnpm install --frozen-lockfile
    pnpm --filter @waste-link/jwnet-gateway build
    Test-Path C:\greco\gateway\services\jwnet-gateway\dist\services\jwnet-gateway\src\index.js  # True
    

    CURRENT.txt は 1 行目 commit SHA・2 行目 zip の SHA-256・3 行目 配備日時の 3 行。VERSION.txt は展開した版の commit SHA 1 行。 初回配備なので PREVIOUS.txt は作らない(切り戻し先なし)。 E-2.10 の切り戻しは、E-2.8 の更新で PREVIOUS.txt ができてから使える。

  3. 持ち込み一式(VM 内の配置):

    配置 内容 備考
    C:\greco\gateway\services\jwnet-gateway\dist\ tsc -p tsconfig.build.json の生成物 起動入口は dist/services/jwnet-gateway/src/index.js
    C:\greco\gateway\services\jwnet-gateway\src\ts-loader.mjs 相対 import の解決 loader 起動コマンドが参照する
    C:\greco\gateway\services\jwnet-gateway\package.json type: module を含む 無いと dist/*.js が ESM として読めない
    C:\greco\gateway\packages\shared\src\ @workspace/shared の TS ソース symlink 越しに型剥離で読む
    C:\greco\gateway\node_modules\ pnpm install の生成物 VM 内で生成(コピーしない)
    C:\greco\gateway\VERSION.txt 展開した版の commit SHA(1 行) E-2.10 が PREVIOUS.txt と照合する
    C:\greco\releases\gateway-deploy-<commit>.zip 版名つきの配備 zip E-2.10 第二経路の原本。一時名とは別に残す
    C:\greco\releases\CURRENT.txt 稼働版の commit・zip SHA-256・配備日時(3 行) E-2.8 が PREVIOUS.txt へ保全する

    起動コマンド(package.jsonstart。作業ディレクトリは package 直下):

    cd C:\greco\gateway\services\jwnet-gateway
    $env:JWNET_GATEWAY_RUNTIME = 'production'
    node --experimental-loader ./src/ts-loader.mjs dist/services/jwnet-gateway/src/index.js
    

    手動起動は疎通確認用。常駐は E-2.4 のサービス登録で行う。

E-2.3 環境変数(接続テスト期間の推奨値)

services/jwnet-gateway/README.md の env 表を配備用に落としたもの。 値は nssm の AppEnvironmentExtra に登録する(E-2.4)。<...> は置換箇所。

JWNET_GATEWAY_RUNTIME=production
JWNET_GATEWAY_ENVIRONMENT=connection-test
JWNET_GATEWAY_TENANT_ID=<Greco のテナント UUID>
SUPABASE_URL=<Supabase プロジェクトの API URL。dev リハーサルは dev、期間中は本番。混ぜない>
SUPABASE_SERVICE_ROLE_KEY=<Secret Manager から取得>
JWNET_CREDENTIAL_KEK=<Secret Manager から取得。Worker(E2)が暗号化に使うものと同一>
JWNET_GATEWAY_ARTIFACT_ROOT=C:\greco\gateway\artifacts
JWNET_GATEWAY_MAINTENANCE_CALENDAR_JSON={"connection-test":{"validThrough":"2026-10-18","stopDates":["2026-10-12"]}}
JWNET_GATEWAY_BIWARE_OUTBOX_DIR=C:\jwnet\outbox
JWNET_GATEWAY_BIWARE_INBOX_DIR=C:\jwnet\inbox
JWNET_GATEWAY_BIWARE_RECEIVED_DIR=C:\jwnet\received
JWNET_GATEWAY_BIWARE_SEND_MODE=command
JWNET_GATEWAY_BIWARE_RECEIVE_MODE=command
JWNET_GATEWAY_BIWARE_SEND_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<送信業務名>","/S"]
JWNET_GATEWAY_BIWARE_RECEIVE_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<受信業務名>","/S"]
JWNET_GATEWAY_BIWARE_COMMAND_TIMEOUT_MS=60000
JWNET_GATEWAY_BIWARE_SEND_TIMEOUT_MS=120000
JWNET_GATEWAY_BIWARE_SENT_DETECTION=exit-code
JWNET_GATEWAY_BIWARE_SEND_REJECT_EXIT_CODES=
JWNET_GATEWAY_BIWARE_RECEIVE_NOFILE_EXIT_CODES=31
JWNET_GATEWAY_BIWARE_RECEIVE_WINDOW_MS=120000
JWNET_GATEWAY_BIWARE_INBOX_POLL_MS=2000
JWNET_GATEWAY_BIWARE_FILE_STABLE_MS=2000
JWNET_GATEWAY_BIWARE_OUTBOX_FILE_NAME=transport-file-id
JWNET_GATEWAY_SEND_TIMEOUT_MS=120000
JWNET_GATEWAY_RECEIVE_TIMEOUT_MS=120000
JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS=110000
JWNET_GATEWAY_SCANNER_KIND=defender
JWNET_GATEWAY_SCANNER_EXE=C:\Program Files\Windows Defender\MpCmdRun.exe
JWNET_GATEWAY_IPSEC_STATUS_URL=http://10.100.121.2:8788/ipsec-status
JWNET_GATEWAY_PORT=8787
  • command 方式ではゲートウェイが受信コマンドを 60 秒間隔・最大 10 回(既定)実行して 新規要求を繰り返すので Biware 側の再試行設定は不要(E-2.6)。 scheduler 代替ではゲートウェイは inbox を 60 秒間隔で回収するだけで センターへの再要求は起こさないため、Biware 側の受信ジョブ(スケジューラ)の 定期実行が必須で、その間隔をゲートウェイの受信窓 (JWNET_GATEWAY_BIWARE_RECEIVE_WINDOW_MS 既定 120,000ms × 最大 10 回)に収める。
  • command 受信では _RECEIVE_WINDOW_MS の設定は必須のままだが無視される(E-2.6)。 未設定では起動拒否になるので値は残す。
  • 秘密 2 つ(JWNET_CREDENTIAL_KEK / SUPABASE_SERVICE_ROLE_KEY)は Secret Manager から取得し、文書・チャット・ログに値を書かない。

    gcloud secrets versions access latest --secret=<KEK  Secret >
    gcloud secrets versions access latest --secret=<service-role  Secret >
    

KEK は Worker(E2)が暗号化に使うものと同一でなければ復号できない。 食い違うと claim した行が request-decrypt-failederror になる。

  • SUPABASE_URL はゲートウェイ 1 台につき 1 プロジェクト。dev と本番を混ぜない (dev リハーサルは dev、期間中は本番。切替時は値を入れ替えて再起動する)。
  • メンテナンスカレンダーは接続テスト期間(2026-10-05〜10-18・平日 9:00–17:00)に 合わせる。土日はコードが自動で閉じる(10/11 日曜は元々対象外)。 stopDates2026-10-12 はスポーツの日(月曜・祝日)。 validThrough を過ぎた設定は fail-closed で送信を止めるので、期間延長時は更新する。
  • 有効期限の警告閾値は JWNET_GATEWAY_MAINTENANCE_WARN_DAYS(任意・既定 60 日)。 期限までの残りが閾値以内になると health が maintenance-expiring を返し、 起動時と 1 日 1 回(JST の暦日単位)ログ (gateway-maintenance-calendar-warning)に残る。期限切れ・未配線・不正な 設定では health が unhealthy(maintenance-expired / maintenance-missing / maintenance-invalid)になり送信が止まる。 既定 60 日の根拠: JWNET の運用通知を入手し、PR をレビューし、リリースして VM のゲートウェイを入れ替えるまでの往復に足りる日数。現実のリリース周期より 長く取る。受け付けるのは十進整数のみ(指数表記・小数点・符号を含む表記は不可)。 空文字・空白・非数値・負・小数・安全整数の範囲外は起動拒否(RangeError)になる。 本番の停止日の実値は JWNET の運用通知で確認する(wl-hao4.1 の人間タスク)。
  • 本番切替時は production キーを配線する。下記は形のみで、値は入れない (規則要約からの推定日付を本番設定に入れてはならない。値は wl-hao4.1 が確定する)。

    JWNET_GATEWAY_MAINTENANCE_CALENDAR_JSON={"production":{"validThrough":"<wl-hao4.1 で確定した一覧の末日>","stopDates":["<wl-hao4.1 で確定>"]}}
    

停止日一覧の出所と更新手順: - 出所: JWNET の運用通知(通知の名称・入手先は wl-hao4.1 が埋める)。 - validThrough には確定した一覧の末日を入れる。運用通知が年度単位なら年度末。 - 更新の手順は「運用通知の入手 → JSON の更新 → PR レビュー → リリース → VM のゲートウェイ入れ替え」(警告閾値の既定 60 日と同じ往復)。 毎年の更新時期・担当は wl-hao4.1 の確定後にここへ書く。 - validThrough の接近は health maintenance-expiring で検知される(#800)。 期限切れ・未配線・不正な設定では health が unhealthy になり送信が止まる。 - 起動中 environment のキーが無い設定では起動時に拒否される(RangeError)。 connection-test キーだけの JSON で production 起動はできない。 - C:\jwnet\received はゲートウェイの受信退避先(Biware の archive とは別)。 artifacts(要求・結果原本)とログ用に以下を作る。

```powershell
mkdir C:\jwnet\received, C:\greco\gateway\artifacts, C:\greco\gateway\logs
```
  • タイムアウト類は初期値の目安(実機で調整)。_MIN_RECEIVE_DELAY_MS / _POLL_INTERVAL_MS / _MAX_RECEIVE_ATTEMPTS / _LOOP_INTERVAL_MS / _RPC_TIMEOUT_MS は未設定で既定(5 分待ち + 60 秒間隔 × 10 回・ RPC 30 秒・loop 60 秒)を使う。_SHUTDOWN_TIMEOUT_MS は推奨 command では 110000 を必ず明示する(未設定の既定 40 秒では command の停止予算 102,000 を下回り起動拒否になる。E-2.6 の停止予算式)。 _SEND_TIMEOUT_MS / _RECEIVE_TIMEOUT_MS は 本テンプレートの値を必ず明示する(未設定の既定 30 秒では内側を覆えず起動拒否になる。下記)。 scheduler 代替では停止予算は drain 5 秒+RPC 上限を覆えばよく (shutdown > drain+rpc を起動時に検証。 既定 40000 > 5000+30000。RPC を延ばしたら停止予算も連動させる)。
  • 外側の送受信期限は内側の所要を覆う値にする(下記は command の式。 scheduler 代替では E-2.6 の scheduler 代替の式を使う)。送信 120000(2 分)= コマンド上限 60,000ms +安定待ち 2,000ms +余裕 58,000ms。 受信 120000(2 分)= コマンド上限 60,000ms + 安定待ち 2,000ms +余裕 58,000ms(退避・ハッシュ・時計誤差)。 起動時に外側≦内側なら fail-closed で拒否する(RangeError)。 1 周回で N 件回収しうるため、受信の実際の所要はコマンド所要時間 + N × 安定待ちに なるscheduler 代替では受信窓 + N × 安定待ち。wl-9txz。N は実行時にしか 分からないので起動時検証は最低 1 件分で判定する)。外側が途中で切れても退避済みの分は DB に載る(逐次引き渡し)が、 未回収の残りは次の receive 呼び出し(新しい要求の cycle・sent 行の再開受信) で回収される。キューが空の runOnce() の次の tick だけでは idle で終わり 回収されない。ただし abort 後の bounded drain(5 秒)自体が 切れた場合は例外で、退避済みの分も DB に載せない。行は reconciliation-required / receive-drain-incomplete(通常の枯渇 receive-timeout とは別の code)になり、ログ edi-receive-drain-incompletereceivedDirstreamedCountrequestCode 付き)を見て Phase F の回収手順へ進む。 inbox に複数ファイルが溜まる運用では余裕を厚くする(目安: 余裕 ms ÷ 安定待ち ms 件までは 1 周回で回収できる)。stop 経路の drain は shutdown−RPC に丸めるため、 R6 の確定を待ち切れず強制終了することはない(起動時検証と一体)。
  • SENT_DETECTION=exit-code は「Biware が送信ファイルを outbox から運ぶ」前提ではない。 終了コード 0 で送信確定(command 送信モード専用。scheduler との組合せは起動拒否)。 scheduler 代替では file-moved に戻す(差分は E-2.6)。
  • OUTBOX_FILE_NAME=transport-file-id(Biware は全銀ファイル名 JW9810FCHKD1 を期待)。 dev の V8 リハーサルでは sim へ画面の要求コードを --request-code で渡すか、 dev のみ request-code に切り替える(準備計画 §5「V8 の手順」)。渡さないと fixture のままの結果になり要求コード照合で落ちる。
  • スキャナは Defender(MpCmdRun.exe)が既定。Microsoft の公式手順は MpCmdRun の 実行に昇格したコマンドプロンプトを要求する(exe・フォルダの権限付与だけでは 足りず、非管理者サービスからの直接起動は拒否される。出典: MpCmdRun)。 Start-MpScan を使う場合も管理者モードが必要になることがある(出典: Defender PowerShell cmdlets)。 選択肢とトレードオフ: (A) サービスを管理者権限で動かし Defender を子プロセスで 実行する(期間中の推奨・追加実装なし・侵害時の影響は大きい。VM はゲートウェイ 専用のため受容する。E-2.4 手順 2)。(B) スキャンを別の昇格プロセス(scheduled task 等)へ委譲しサービスは非管理者のままにする(現行コードは子プロセス直接起動のみで 委譲経路が無いため新規実装が必要・期間後の恒久策)。(C) SCANNER_KIND=clamav にし 非管理者サービスのまま ClamAV を使う(別途インストール・定義更新の運用が必要。 Defender が無効な環境ではこちら)。
  • SCANNER_EXE は存在確認(Test-Path)に加え、サービスと同じ実行主体・トークンで 既知の無害ファイルをスキャンできることを実機確認する(V7・サービスアカウントの スケジュールタスクで行う)。管理者の PowerShell や Start-Process -Credential の対話ログオンでの確認では不足である(UAC の filtered token になり、 サービスと同じ権限で動かないため)。Defender が無効な環境では (C) を選ぶ。

E-2.4 サービス登録(nssm)

nssm を使う(node は Windows サービスの ServiceMain を実装しないため、 sc.exe 直登録では SCM が起動失敗と誤判定し、ログ取得・再起動制御もできない)。

  1. nssm を入れる(winget に無ければ公式 zip を Edge で取得して展開)。

    winget install NSSM.NSSM --silent
    nssm version
    
  2. サービス用ローカルユーザーを作る。Defender(MpCmdRun.exe)を使う期間中は Administrators に入れる(MpCmdRun が昇格を要求するため。E-2.3 の選択肢 (A)。 ClamAV(選択肢 (C))にした場合だけ Administrators に入れない)。

    $svcPassword = Read-Host 'svc-greco-gw のパスワード' -AsSecureString
    New-LocalUser svc-greco-gw -Password $svcPassword -PasswordNeverExpires -Description 'Greco JWNET gateway service account'
    # Defender を使う場合のみ(ClamAV の場合は実行しない)。
    Add-LocalGroupMember Administrators svc-greco-gw
    

    管理者サービスの注意: 侵害時の影響が大きいため、VM はゲートウェイ専用にし、 RDP は管理用 IP に絞り、期間後に (B) 委譲方式への移行を検討する。

  3. 原本が通るフォルダを専用化する(/grant の追加だけでは Users 等の既存読取が 残るため、継承を切って明示 ACE だけにする。出典: icacls)。

    # 作業前に現状を保存する(Biware が動かなくなった場合の戻し用)。
    icacls C:\greco\gateway\artifacts > C:\greco\acl-backup-artifacts.txt
    icacls C:\jwnet\outbox > C:\greco\acl-backup-outbox.txt
    icacls C:\jwnet\inbox > C:\greco\acl-backup-inbox.txt
    icacls C:\jwnet\received > C:\greco\acl-backup-received.txt
    icacls <Biware  archive 相当> > C:\greco\acl-backup-biware-archive.txt
    

    対象は artifactsC:\jwnet\outbox / inbox / received・Biware が送信済みを 移動する archive 相当(平文の要求原本が残る)。順序は「明示 /grant を先に 足してから /inheritance:r で継承を切る」(逆にすると作業者が締め出される)。 残す主体は SYSTEM・管理用(Administrators)・gateway のサービスアカウント・ Biware が使う主体だけにする。<Biware 主体> は Biware の実行ユーザー (サービスアカウント名は B1 で確定。未確定なら Biware のサービス表示名から 特定する)へ置き換える。

    # 明示の許可を先に足す(artifacts の例。他フォルダも同様)。
    icacls C:\greco\gateway\artifacts /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls C:\jwnet\outbox /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M' '<Biware 主体>:(OI)(CI)M'
    icacls C:\jwnet\inbox /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M' '<Biware 主体>:(OI)(CI)M'
    icacls C:\jwnet\received /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls <Biware  archive 相当> /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' '<Biware 主体>:(OI)(CI)M'
    icacls C:\greco\gateway /grant 'svc-greco-gw:(OI)(CI)RX'
    icacls C:\greco\gateway\logs /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    # 継承を切り、継承 ACE を除去する。
    icacls C:\greco\gateway\artifacts /inheritance:r
    icacls C:\jwnet\outbox /inheritance:r
    icacls C:\jwnet\inbox /inheritance:r
    icacls C:\jwnet\received /inheritance:r
    icacls <Biware  archive 相当> /inheritance:r
    icacls C:\greco\gateway\logs /inheritance:r
    

    /inheritance:r は継承 ACE だけを消し、既存の明示 ACEUsers:(OI)(CI)R や 個別ユーザーの許可)は残る。/grant の追加や /grant:r の置換では未指定の 主体の ACE は消えないため、残す 4 主体(SYSTEMAdministratorssvc-greco-gw<Biware 主体>artifacts / received は Biware 主体なし) 以外の明示 ACE を /remove で列挙して除く(/reset は継承を有効に戻すため 継承 ACE が復活する窓が開き、grant+切りのやり直しになる。許可主体に触れない /remove の方が Biware を止めないのでこちらを選ぶ)。

    # 継承切り後の DACL を列挙する。各行は「主体:(継承フラグ)権限」の形。
    # (OI)(CI)=子へ継承・F=フル・M=変更・R=読取。末尾の Successfully ... は無視する。
    icacls C:\greco\gateway\artifacts
    icacls C:\jwnet\outbox
    icacls C:\jwnet\inbox
    icacls C:\jwnet\received
    icacls <Biware  archive 相当>
    # 例: 下記の BUILTIN\Users 行が残っていれば除去対象。
    # C:\jwnet\outbox BUILTIN\Users:(OI)(CI)R
    

    継承切り直後は継承 ACE が無いため、表示される ACE はすべて明示である。 残す 4 主体以外の行(Users / Authenticated Users / 個別ユーザー / 削除済みアカウントの S-1-5-21-... 形式の SID)があれば、主体ごとに除く。 /remove <主体>:g / :d を付けない)は許可・拒否の両方の ACE を除く。 残す主体の行に (DENY)(N)(拒否)があれば Biware・ゲートウェイが 動かない原因になるため、勝手に外さず V5・health の確認後に判断する。

    # 例: Users の明示 ACE が残っていた場合(実際の主体名・SID に置き換える)。
    # 許可主体(SYSTEM / Administrators / svc-greco-gw / <Biware 主体>)には触れない。
    icacls C:\jwnet\outbox /remove 'BUILTIN\Users'
    # 除去のたびに列挙し直し、4 主体だけになったことを確認してから次へ進む。
    icacls C:\jwnet\outbox
    

    リハーサル等で事前に置かれたファイルは独自の明示 ACE を持つ場合がある (フォルダの継承切りは既存ファイルの明示 ACE を消さない)。 icacls <フォルダ>\* で列挙し、残す 4 主体以外の主体があれば同じ /remove で ファイルごとに除く。新規ファイルはフォルダの DACL を継承するため対象外。 送信時の一時ファイル(.sending-*)は outbox 内に作られるため、outbox の 書込権で足りる。Biware が動かなくなったら <Biware 主体> の特定ミスを疑い、 上記のバックアップと Biware のログを見て戻す(V5 を再実行する)。

    別の一般ユーザーで読めないことを全対象フォルダで検証する (runas か第 2 アカウント)。outbox / inbox にテストファイルを置くと Biware・ゲートウェイが拾うため、一覧取得で検証する(ファイルは置かない)。

    runas /user:<別ユーザー> "powershell -Command Get-ChildItem C:\greco\gateway\artifacts"
    runas /user:<別ユーザー> "powershell -Command Get-ChildItem C:\jwnet\outbox"
    runas /user:<別ユーザー> "powershell -Command Get-ChildItem C:\jwnet\inbox"
    runas /user:<別ユーザー> "powershell -Command Get-ChildItem C:\jwnet\received"
    runas /user:<別ユーザー> "powershell -Command Get-ChildItem '<Biware の archive 相当>'"
    

    期待値: 5 箇所すべて Access to the path ... is deniedAccess Denied)。 読めたフォルダは明示 ACE の除去漏れ(継承切りの不完全ではない)なので、 DACL を列挙し直して残存主体を /remove する。 除去後は肯定側も確認する: ゲートウェイの health が edi-ok (E-2.5)で、Biware の疎通(V5)が通ること。

  4. サービスを登録する。作業ディレクトリは package 直下services/jwnet-gateway。loader の相対パス解決のため)。

    nssm install GrecoJwnetGateway "C:\Program Files\nodejs\node.exe" "--experimental-loader ./src/ts-loader.mjs dist/services/jwnet-gateway/src/index.js"
    nssm set GrecoJwnetGateway AppDirectory C:\greco\gateway\services\jwnet-gateway
    nssm set GrecoJwnetGateway ObjectName .\svc-greco-gw <パスワード>
    nssm set GrecoJwnetGateway Start SERVICE_AUTO_START
    nssm set GrecoJwnetGateway AppStdout C:\greco\gateway\logs\stdout.log
    nssm set GrecoJwnetGateway AppStderr C:\greco\gateway\logs\stderr.log
    nssm set GrecoJwnetGateway AppRotateFiles 1
    nssm set GrecoJwnetGateway AppRotateBytes 10485760
    nssm set GrecoJwnetGateway AppRotateOnline 1
    nssm set GrecoJwnetGateway AppExit Default Restart
    nssm set GrecoJwnetGateway AppRestartDelay 5000
    # 停止猶予: console 段階に停止予算+余裕 5000ms を載せる。
    # 推奨 command は 115000(110000+5000)。scheduler 代替は 45000(40000+5000)。
    # Window/Threads は既定と同値の 1500ms を明示する(下記の注)。
    nssm set GrecoJwnetGateway AppNoConsole 0
    nssm set GrecoJwnetGateway AppStopMethodSkip 0
    nssm set GrecoJwnetGateway AppStopMethodConsole 115000
    nssm set GrecoJwnetGateway AppStopMethodWindow 1500
    nssm set GrecoJwnetGateway AppStopMethodThreads 1500
    nssm set GrecoJwnetGateway AppEnvironmentExtra "JWNET_GATEWAY_RUNTIME=production" "JWNET_GATEWAY_ENVIRONMENT=connection-test" "JWNET_GATEWAY_TENANT_ID=<テナント UUID>" "SUPABASE_URL=<URL>" "SUPABASE_SERVICE_ROLE_KEY=<秘密>" "JWNET_CREDENTIAL_KEK=<秘密>" "JWNET_GATEWAY_ARTIFACT_ROOT=C:\greco\gateway\artifacts" "JWNET_GATEWAY_MAINTENANCE_CALENDAR_JSON=<E-2.3 の JSON>" "JWNET_GATEWAY_BIWARE_OUTBOX_DIR=C:\jwnet\outbox" "JWNET_GATEWAY_BIWARE_INBOX_DIR=C:\jwnet\inbox" "JWNET_GATEWAY_BIWARE_RECEIVED_DIR=C:\jwnet\received" "JWNET_GATEWAY_BIWARE_SEND_MODE=command" "JWNET_GATEWAY_BIWARE_RECEIVE_MODE=command" 'JWNET_GATEWAY_BIWARE_SEND_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<送信業務名>","/S"]' 'JWNET_GATEWAY_BIWARE_RECEIVE_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<受信業務名>","/S"]' "JWNET_GATEWAY_BIWARE_COMMAND_TIMEOUT_MS=60000" "JWNET_GATEWAY_BIWARE_SEND_TIMEOUT_MS=120000" "JWNET_GATEWAY_BIWARE_SENT_DETECTION=exit-code" "JWNET_GATEWAY_BIWARE_SEND_REJECT_EXIT_CODES=" "JWNET_GATEWAY_BIWARE_RECEIVE_NOFILE_EXIT_CODES=31" "JWNET_GATEWAY_BIWARE_RECEIVE_WINDOW_MS=120000" "JWNET_GATEWAY_BIWARE_INBOX_POLL_MS=2000" "JWNET_GATEWAY_BIWARE_FILE_STABLE_MS=2000" "JWNET_GATEWAY_BIWARE_OUTBOX_FILE_NAME=transport-file-id" "JWNET_GATEWAY_SEND_TIMEOUT_MS=120000" "JWNET_GATEWAY_RECEIVE_TIMEOUT_MS=120000" "JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS=110000" "JWNET_GATEWAY_SCANNER_KIND=defender" "JWNET_GATEWAY_SCANNER_EXE=C:\Program Files\Windows Defender\MpCmdRun.exe" "JWNET_GATEWAY_IPSEC_STATUS_URL=http://10.100.121.2:8788/ipsec-status" "JWNET_GATEWAY_PORT=8787"
    # SEND/RECEIVE_COMMAND の 2 項目だけシングルクォート(JSON のダブルクォートを保つため。他は従来どおり)。
    nssm start GrecoJwnetGateway
    
    • 自動起動(SERVICE_AUTO_START)・失敗時は 5 秒後に再起動。
    • 標準出力/エラーは logs/ に出し、10MB でローテーションする。
    • AppEnvironmentExtra はレジストリに保持される。秘密の更新時は値を入れ直して nssm restart GrecoJwnetGateway する(ファイルに secrets を残さない)。
    • 本番切替時は JWNET_GATEWAY_ENVIRONMENT=productionproduction キーの 両方を入れ替えること。片方だけ(environment だけ・JSON のキーだけ)だと起動時に拒否される。
    • ObjectName のパスワードはコマンド履歴に残る。実行後は履歴を消すか、 サービスのプロパティ画面から設定し直す。
    • 停止猶予の導出: nssm は停止時に Control-C → WM_CLOSE → WM_QUIT → TerminateProcess の順で止め、各段階の既定は 1,500ms(nssm 公式 Usage)。 既定のままでは停止予算(JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS・推奨 110,000ms) の drain 完了前、またはその後の R6 応答前に Node が終了させられうる。 Node の graceful stop を受けるのは console 段階の Control-C(SIGINT ハンドラ)だけなので、console 段階だけが実効 shutdown を上回る猶予 (推奨 110,000ms +余裕 5,000ms = 115,000ms)を持つ。 Window/Threads に長い猶予を配分して合計だけ 40 秒にしても、 Node がその段階で graceful stop する保証にならない。 console を無効化すると Control-C を送れないため AppNoConsole 0 を明示する。 JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS を変えたら console も連動させる (80,000 にするなら console は例えば 85,000)。 推奨 command 受信の停止予算 110000 の内訳は E-2.6 (コマンド上限 60,000 +安定待ち 2,000 +保存 5,000 +drain 余裕 5,000 + R5 上限 30,000 = 102,000 を上回る)のとおり。満たさなければ 起動時に拒否する。scheduler 代替では停止予算 40,000ms・console 45,000ms に戻す。
    • AppRestartDelay は終了後の再起動待ち、AppExit Default Restart は 既に終了したアプリの再起動方針であり、どちらも停止中の drain 猶予ではない。 停止予算と混ぜない。
    • AppStopMethodSkip で TerminateProcess を無効にする解決は採らない (Skip 0 のまま)。無効にすると停止に応じないプロセスが未管理で残る。
    • 設定の確認(nssm get で個別に。AppEnvironmentExtra の dump は 秘密が出るため取得しない):
    nssm get GrecoJwnetGateway AppNoConsole
    nssm get GrecoJwnetGateway AppStopMethodSkip
    nssm get GrecoJwnetGateway AppStopMethodConsole
    nssm get GrecoJwnetGateway AppStopMethodWindow
    nssm get GrecoJwnetGateway AppStopMethodThreads
    

    期待値: AppNoConsole=0 / AppStopMethodSkip=0 / AppStopMethodConsole=115000 / Window=1500 / Threads=1500scheduler 代替では 45000。E-2.3 の式に戻した場合はこちらも戻す)。 - VM の再起動は別の制限: OS の shutdown 通知後の猶予は約 20 秒で、 OS 再起動では SCM の WaitToKillServiceTimeout が上限になる。 console 設定だけから「VM 再起動でも 40 秒使える」とは結論できない。 OS の値は変更せず、明示的なサービス停止の完了を待ってから再起動するのが 正規手順。自動再起動・OS shutdown 経路は別途受け入れを取る。

    nssm stop GrecoJwnetGateway
    # 停止完了(drain 中は最大で停止予算+余裕ぶんかかる)まで待ってから再起動する。
    Restart-Computer
    
  5. サービスと同じ実行主体・トークン・scanner 設定でスキャンが通ることを 確認する(V7)。サービスと同じ非対話ログオンで動かすため、サービスアカウントの スケジュールタスクで probe 実行する(ゲートウェイのサービスは止めない)。 Start-Process -Credential の対話ログオンでは UAC 有効な管理者は filtered token になり、正しい設定でもこの確認だけが失敗しうる。-Verb RunAs との併記は parameter set が衝突して不可(出典: Start-Process)。 一時サービス方式(SCM への登録・削除を伴い、終了コードの写り先がもう一段増える) より、標準的なスケジュールタスクを選ぶ。

    'harmless-v7-probe' | Out-File C:\greco\gateway\artifacts\v7-scan-probe.txt -Encoding ascii
    # exe・引数・成功の終了コードは本番のスキャナ設定と同一(E-2.3 の
    # JWNET_GATEWAY_SCANNER_KIND・JWNET_GATEWAY_SCANNER_EXE。実装の分岐は
    # production.ts の scanner 解決と windows-defender.ts の固定引数が正典)。
    # Defender の場合(E-2.3 の選択肢 (A)・成功は 0・検出は 2):
    $action = New-ScheduledTaskAction -Execute 'C:\Program Files\Windows Defender\MpCmdRun.exe' -Argument '-Scan -ScanType 3 -File C:\greco\gateway\artifacts\v7-scan-probe.txt -DisableRemediation'
    $principal = New-ScheduledTaskPrincipal -UserId 'svc-greco-gw' -LogonType Password -RunLevel Highest
    # ClamAV の場合(E-2.3 の選択肢 (C)・SCANNER_EXE の実値に置き換える。
    # 成功は 0・検出は 1。サービスは非管理者のため Highest は付けない):
    # $action = New-ScheduledTaskAction -Execute '<ClamAV の clamscan.exe のパス>' -Argument 'C:\greco\gateway\artifacts\v7-scan-probe.txt'
    # $principal = New-ScheduledTaskPrincipal -UserId 'svc-greco-gw' -LogonType Password -RunLevel Limited
    $task = New-ScheduledTask -Action $action -Principal $principal
    # Principal parameter set には -Password が無いため、定義を -InputObject で渡す
    # Object parameter set(-InputObject・-User・-Password の組合せ)で登録する。
    # パスワードは入力させ、文書・履歴に値を書かない。
    $svcPassword = Read-Host 'svc-greco-gw のパスワード' -AsSecureString
    $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($svcPassword)
    $plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    try {
      Register-ScheduledTask -TaskName 'GrecoGwV7ScanProbe' -InputObject $task -User 'svc-greco-gw' -Password $plainPassword
      $startedAt = Get-Date
      Start-ScheduledTask -TaskName 'GrecoGwV7ScanProbe'
      # 開始待ち→完了待ち(最大 2 分=スキャン上限の既定)。各反復で
      # Get-ScheduledTaskInfo を 1 回だけ取得し、その組(LastRunTime と
      # LastTaskResult)だけで完了を判定する。State と Info を別時点に
      # 取得して混ぜると、取得の間に開始・終了したときに誤判定する。
      $deadline = (Get-Date).AddMinutes(2)
      $info = $null
      while ($true) {
        Start-Sleep 5
        $info = Get-ScheduledTaskInfo -TaskName 'GrecoGwV7ScanProbe'
        $isThisRun = $info.LastRunTime -ge $startedAt
        $isSettled = ($info.LastTaskResult -ne 267009) -and ($info.LastTaskResult -ne 267045)
        if ($isThisRun -and $isSettled) {
          break
        }
        # 未完了(開始前・実行中・実行要求済みのいずれか)。期限内なら待つ。
        if ((Get-Date) -ge $deadline) {
          Stop-ScheduledTask -TaskName 'GrecoGwV7ScanProbe'
          throw 'V7 probe が 2 分以内に完了しませんでした'
        }
      }
      # 完了判定に使った組の LastTaskResult を今回の終了コードとして扱う。
      # 267009(0x41301 = SCHED_S_TASK_RUNNING)は「実行中」、
      # 267045(0x41325 = SCHED_S_TASK_QUEUED)は「実行要求済み・未開始」を
      # 表す Task Scheduler の値で、scanner の終了コードではない(出典は下記)。
      # 0 以外が出たら下の切り分け表へ進む。
      $info.LastTaskResult
    } finally {
      # 後片付け(成功・失敗・超過のいずれでもタスクと probe ファイルを消す)。
      Unregister-ScheduledTask -TaskName 'GrecoGwV7ScanProbe' -Confirm:$false
      Remove-Item C:\greco\gateway\artifacts\v7-scan-probe.txt
      $plainPassword = $null
    }
    

    出典(parameter set の裏取り): Register-ScheduledTask (Principal parameter set には -Password / -User が無く、Object parameter set の -InputObject / -User / -Password の組合せが実在する)、 New-ScheduledTask-Action / -Principal で定義を組む)、 New-ScheduledTaskPrincipal-RunLevel Highest / -LogonType Password)、 Security Contexts for Tasks (昇格を要するタスクは Highest)、 Stop-ScheduledTaskRead-Host-AsSecureString でパスワードを入力させる)、 Marshal.SecureStringToBSTRSecureString の復号と ZeroFreeBSTR での解放)、 Task Scheduler error and success constantsSCHED_S_TASK_RUNNING = 0x41301 = 267009「実行中」・ SCHED_S_TASK_QUEUED = 0x41325 = 267045「実行要求済み」。 どちらも scanner の終了コードではないため完了条件から除く)。

    受入値: LastTaskResult0(Defender・ClamAV とも終了コード 0=無害ファイルが clean)。0 が得られないまま本番へ進まない(V7 の受入条件)。 0 が得られないまま本番へ進むと送信前スキャンで cycle が blocked になる。 失敗時の切り分け:

    症状 見方
    検出コード(Defender の 2 / ClamAV の 1 無害なはずの probe が検出されるのは異常(probe 内容の取り違え・定義の状態)のため、原因を潰して 0 を得てから進む。本番では virus-check-failed として送信が止まる。
    Defender の拒否(probe だけ失敗する) アカウントの権限不足。サービスを Administrators に入れたか(E-2.4 手順 2)・-RunLevel Highest を付けたかを確認する。直らなければ ClamAV への切替(E-2.3 の選択肢 (C))を見直す。
    ClamAV の未起動(0 以外・ソケットなし等) SCANNER_EXE のパスと ClamAV の起動・定義更新の運用を確認する。
    タスク登録の失敗(0x80070005=パスワード誤り / 0x80070002=exe パスなし) タスク・資格情報の問題で、本番の成否とは別に直して登録し直す。
    バッチログオン権限なし(0x80070569 アカウントにバッチログオン権を付けてタスクを登録し直す。
    2 分超過で停止した scanner が応答しない(Defender の多重起動・ClamAV の未起動)。サービス側の状態を直して再実行する。

E-2.5 health の確認

VM 内から叩く(health は既定で 127.0.0.1 にだけ bind される。 E-2.11 で内部 IP へ変えた後は 127.0.0.1 では応答しない)。

Invoke-WebRequest http://127.0.0.1:8787/health | Select-Object -ExpandProperty Content

200 = healthy: true、503 = どこか unhealthy。応答は { healthy, ipsec: { healthy, code }, edi: {...}, polling: {...}, maintenance: {...} }code ごとの原因と対処(services/jwnet-gateway/src/health.tsGATEWAY_HEALTH_CODES が実装の正典):

code healthy 原因と対処
ipsec-ok IPsec 確立中(E-2.7 の JSON が両方 true かつ 120 秒以内)
ipsec-health-error × URL 到達不能・非200・JSON 不正・stale・established/childInstalled のいずれか false。ルータ VM で swanctl --list-sas と server 稼働、FW allow-gw-ipsec-status を確認
ipsec-health-timeout × probe が 5 秒で応答なし。ルータ負荷・swanctl ハングを確認
ipsec-health-invalid / ipsec-health-not-configured × 実装・配線の不整合。production では出ないはず。出たら版と env を確認
edi-ok outbox/inbox/received が書込可、command の exe が存在
edi-health-error × 3 フォルダのいずれかが書込不可。フォルダ存在と svc-greco-gw の ACL(E-2.4 手順 3)を確認
edi-command-missing × command モードの送信・受信コマンドの exe が無い。パスと B6 設定(E-2.6)を確認
edi-health-timeout / edi-health-invalid / edi-transport-not-configured × 5 秒応答なし / 実装不整合 / dev 専用。production では最後は出ない
polling-ok loop が回り、直近 cycle が前進した
polling-not-started × 起動直後で初回 cycle 未実行。loop 間隔(既定 60 秒)を待って再確認
polling-cycle-failed × 直近 cycle が例外。サービスログの gateway-cycle-failed と直前の edi-* 行を見る
polling-no-progress × 直近 cycle が blocked/failed/indeterminate/reconciliation-required/skipped。exchange 行の状態・Biware・スキャナを確認(下記)
polling-stale × heartbeat が約 7 分途絶。サービスが止まっていないか nssm status で確認
polling-health-error / polling-health-invalid / polling-loop-not-configured × 実装・配線の不整合。出たら版と env を確認
maintenance-ok メンテカレンダーが有効期限内(残日数が警告閾値・既定 60 日より多い)
maintenance-expiring ○(要更新手配) 有効期限が警告閾値以内(残日数 0 の当日を含む)。まだ送れるがカレンダーを更新する
maintenance-missing × 対象環境のカレンダー欠落。送信は止まる。設定を見直す
maintenance-invalid × カレンダー不正(日付形式等)。送信は止まる。設定を見直す
maintenance-expired × 有効期限切れ。fail-closed で送信は止まる。カレンダーを更新する
maintenance-health-invalid / maintenance-health-error × 実装・配線の不整合 / health 取得自体の例外。出たら版と env を確認
transport-ok dev/test の Mock 搬送(contracts.tsMockEdiTransport.health())が返す。production の Biware アダプタは edi-ok を返すので、本番でこの code は出ない
polling-stopped (予約) 現行の producer なし。出ない
probe-unspecified (予約) sanitize が invalid 系へ変換する予約値(health.ts:13:175)。probe が code を付けずに返す前の初期値で、表に出ることはない

スキャナの成否は health の code に出ない。未設定・失敗は cycle が blockedvirus-scanner-not-configured / virus-check-failed)になり、polling-no-progress と exchange 行の error で現れる。MpCmdRun.exe のパスは Test-Path で事前確認し、 加えて実際のサービス実行アカウントで既知の無害ファイルのスキャンが通ることを V7 で確認する(管理者 PowerShell からの確認だけでは不足。E-2.3)。

全銀レベルの処理結果コードの読み方: 終了情報ファイル(制御ファイルと同じフォルダの固定名 STATUS.BIZ。同時通信 OFF のとき。実パスは R-2 で確定)(固定長 128 桁)の 21〜22 桁目 = 制御電文の処理結果コード。エラーは 1/2-XX-23 / 1/2-XX-33 の形で出力される。出典 = 「プログラミング」6-3 終了情報ファイルのデータ形式 / 6.5.3 通信中のエラー、「クライアントアプリケーション」5 エラー終了の原因と対策 / 5.2 通信中エラー。全銀レベルの失敗は JWNET の EDI 処理状況照会に載らないので、このファイルが唯一の証跡(写しは認証値をマスク)。

E-2.6 Biware の駆動方式(B6 回答反映・推奨 command

インターコム回答(2026-09-15)で次の事実が確定し、2026-09-16 の裁定 D1 (wl-k7io.1.5)で推奨環境は送受信とも command(第 7 章 全銀エクスプローラのコマンドライン実行・同期・終了コード 0 で確定)になった。 E-2.3 のテンプレートはその設定である。scheduler(監視フォルダ + Biware 側スケジューラ)は代替として残す(消さない)。

  • 送信ジョブ / 受信ジョブの分離は「クライアントアプリケーション」第 6 章 5「通信ファイル情報設定」。
  • 受信完了までのタイムアウトは全銀TCPクライアントでは設定不可(→ ゲートウェイ側の期限で管理)。
  • 「該当ファイルなし」時は「転送後処理: 受信データなし時に実行」に設定したプログラムが走る。通信の再試行は標準機能なし。方式で意味が違う: command ではゲートウェイが受信コマンドを 60 秒間隔・最大 10 回(既定)実行して新規要求を繰り返すので Biware 側の再試行設定は不要。scheduler ではゲートウェイは inbox を 60 秒間隔で回収するだけでセンターへの再要求は起こさないため、Biware 側の受信ジョブ(スケジューラ)の定期実行が必須で、その間隔をゲートウェイの受信窓(JWNET_GATEWAY_BIWARE_RECEIVE_WINDOW_MS 既定 120,000ms × 最大 10 回)に収める。
  • 外部駆動はコマンド実行が可能(第 7 章「全銀エクスプローラ(コマンドライン実行)」)。同期処理で終了コードにより成否判定、終了コード 0 なら受信ファイルの書込完了と判定できるcommand モードの exit-code 検知と整合。exe 名・引数・終了コード表は第 7 章の実値(下記の表・wl-k7io.1 で手順書 5 冊から転記)。

推奨 command の設定値(E-2.3 のテンプレートと同じ。<...> は置換箇所。 センター名・業務名の実値は設定票由来なので書かない)。

JWNET_GATEWAY_BIWARE_SEND_MODE=command
JWNET_GATEWAY_BIWARE_RECEIVE_MODE=command
JWNET_GATEWAY_BIWARE_SEND_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<送信業務名>","/S"]
JWNET_GATEWAY_BIWARE_RECEIVE_COMMAND=["<Biware インストール先>\\Zexplorer.exe","-H<センター名>","-W<受信業務名>","/S"]
JWNET_GATEWAY_BIWARE_COMMAND_TIMEOUT_MS=60000
JWNET_GATEWAY_BIWARE_SENT_DETECTION=exit-code
JWNET_GATEWAY_BIWARE_SEND_REJECT_EXIT_CODES=
JWNET_GATEWAY_BIWARE_RECEIVE_NOFILE_EXIT_CODES=31
JWNET_GATEWAY_SEND_TIMEOUT_MS=120000
JWNET_GATEWAY_RECEIVE_TIMEOUT_MS=120000
JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS=110000
  • 送受信コマンドは Zexplorer.exe(全銀クライアントのインストール先)に -H<センター名> と送信は -W<送信業務名>・受信は -W<受信業務名>/S(サイレント実行)を渡す。 /R(即時実行)は手動確認時の代替。/C/Q/N は使う前提で書かない (下記の表に載せるだけ。/R/S 未指定時は使えない)。 受信コマンドも同じ形で、業務名は受信用に登録したものにする。 プレースホルダ({FILE} 等)が無くても動く(ゲートウェイは置換するだけ)。
  • scheduler 代替に戻す場合の差分は以下(command の行と置き換える)。 SENT_DETECTION=file-moved は「Biware が送信ファイルを outbox から運ぶ」前提。 scheduler の外側期限の式はここに 1 箇所化する(E-2.3 と下記の本文はここを指す): 送信 360000(6 分)= Biware 送信期限 300,000 +安定待ち 2,000 +余裕 58,000。 受信 180000(3 分)= 受信窓 120,000 +安定待ち 2,000 +余裕 58,000。
JWNET_GATEWAY_BIWARE_SEND_MODE=scheduler
JWNET_GATEWAY_BIWARE_RECEIVE_MODE=scheduler
JWNET_GATEWAY_BIWARE_SEND_TIMEOUT_MS=300000
JWNET_GATEWAY_BIWARE_SENT_DETECTION=file-moved
JWNET_GATEWAY_BIWARE_RECEIVE_WINDOW_MS=120000
JWNET_GATEWAY_SEND_TIMEOUT_MS=360000
JWNET_GATEWAY_RECEIVE_TIMEOUT_MS=180000
JWNET_GATEWAY_SHUTDOWN_TIMEOUT_MS=40000
  • _SEND_COMMAND / _RECEIVE_COMMAND は JSON の argv 配列。{FILE} / {OUTBOX_DIR} / {INBOX_DIR} を置換する(無くても動く。上記の Zexplorer 形)。 実配備先は H8 で確定する。 health の edi-command-missing は両コマンドの exe 存在を起動後に毎回確認する。
  • _SENT_DETECTION の選び方: exit-code(終了コード 0 で送信確定。command 送信モード専用。scheduler との組合せは起動拒否)/ file-moved (outbox からの移動を待つ。scheduler 既定)/ log-line (Biware ログの行で判定。_LOG_PATH と成功/失敗パターンが必須)。

    JWNET_GATEWAY_BIWARE_SENT_DETECTION=log-line
    JWNET_GATEWAY_BIWARE_LOG_PATH=C:\Biware\<ログ名>
    JWNET_GATEWAY_BIWARE_LOG_SUCCESS_PATTERN=<成功行の正規表現>
    JWNET_GATEWAY_BIWARE_LOG_FAILURE_PATTERN=<失敗行の正規表現>
    

パターンは JS の RegExp として読む。送信ファイル名を含む行だけを見る。 - _SEND_REJECT_EXIT_CODES / _RECEIVE_NOFILE_EXIT_CODES は Biware 第 7 章の 終了コード表から転記する欄(wl-w6nn・任意・既定は空 = 従来挙動)。 カンマ区切りの非負整数(例 1,2)だけを受け付け、不正値は起動時に拒否する。 空のままなら分類は働かず、送信の非 0 は全部「送ったか不明」 (行は sending のまま残り、次回以降の R7 在庫確認(再起動時を含む)で gateway-restarted-during-send の照合待ちになる。遷移を待つための再起動は 不要)、受信の非 0 は warn の 上で回収 → 再試行する(止めない)。 - 送信: exit-code 検知の非 0 のうち REJECT 集合に含まれる code のときだけ 「未送信が確定」として error/biware-send-rejected(R6・error)にし、 別の要求コードで再生成する。集合外は従来どおり不明扱い。 - 受信: NOFILE 集合に含まれる code は「ファイルなし」として info のみ、 集合外の非 0 は warn(edi-biware-receive-command-failed)の上で、 どちらも従来どおり回収 → 再試行する(止めない)。 - コマンドの終了コード・所要時間・試行番号は edi-biware-command に残る (送信は attempt 1・受信は 1 起点。NOFILE 集合外の非 0 / timeout は warn)。

「ファイルなし」の識別(_RECEIVE_NOFILE_EXIT_CODES に入れる値)

第 7 章の戻り値表には「ファイルなし」の行が無い。値はプログラミング説明書と 複数ファイル転送追加機能説明書の 2 冊から取る。

事実 出典 確度
制御電文の処理結果コード 17 = ファイルなし00=正常 / 17=ファイルなし / 99=その他エラー) プログラミング説明書「全銀協標準通信プロトコルの仕様・処理結果」+標準EDI 仕様書 3-2 (6)「送信する結果ファイルが存在しなければファイルなしレスポンス(ファイル制御文:17)」 原文(2 冊で裏取り済み)
終了ステータス 0-00-31 = 『ファイル制御電文』の処理結果コードにセンター側ファイル無し「17」を受信した プログラミング説明書 0-00-31 の原因 原文
0-00-31 は複数ファイル伝送で唯一、通信を中断しないエラー 複数ファイル転送追加機能説明書 3-4 原文
全銀エクスプローラに「転送後処理: 受信データなし時に実行(センター側送信ファイル存在しない)」があり、0-00-31 で終了したときに指定コマンドが走る クライアントアプリケーション説明書 原文
終了コードは 31 第 7 章 3.3 の符号化規則(X-XX-XX の下 2 桁・英字が無いので加算なし)から導出 ⚠ 導出。原文に「31 を返す」という記述は無い

設定値: JWNET_GATEWAY_BIWARE_RECEIVE_NOFILE_EXIT_CODES=31

ただし導出なので、R-2(9/25 のリハーサル・再インストール後 9/29 に再実行)で 実際にセンター側ファイル無しを引いたときの終了コードを実測して確定する。 実測が 31 でなければこの値を直す。空のままにすると「ファイルなし」が warn として毎回記録され、正常な空振りとエラーの区別がつかなくなる。 貼り付ける 3 箇所(E-2.3 のテンプレート・E-2.4 の nssm 行・E-2.6 の推奨ブロック)は 31 で揃える。実測で値が変わったら 3 箇所とここの設定値を同時に直し、 nssm restart で反映する(変更手順の一本化)。

_SEND_REJECT_EXIT_CODES には 31 を入れない。 受信の空振りであって 送信の拒否ではない。送信側の REJECT 集合は R-2 で実測するまで空のままにする。

より確実な判別が要るなら STATUS.BIZ の制御電文の処理結果コード(2 桁)が 17 かを見る(こちらは原文どおりで導出を含まない)。終了コードでの判別が R-2 で 確認できなかった場合の代替として使う。 - 外側期限の式は方式で違う(内側の所要が違うため)。推奨 command の式は以下で、 scheduler 代替では上記の scheduler 代替の式を使う。 _COMMAND_TIMEOUT_MS はコマンド起動の上限で、command モードでは必須 (未設定は起動拒否。上限なしのコマンドは外側でしか止められず、 内側を計算できないため)。実装はコマンド終了を待ってから検知期限を起算する (直列)ため、送信(file-moved / log-line)の内側は 3 項の合計になる。 command 受信は受信窓を使わない(_RECEIVE_WINDOW_MS の設定は必須のままだが 無視される)。exit-code の送信検知は終了コードで確定し検知待ちが無い。 - 送信(file-moved / log-line): 外側 = コマンド上限 + Biware 送信期限 + 安定待ち + 余裕。例: 420000(7 分)= 60,000 + 300,000 + 2,000 + 58,000。 - 送信(exit-code): 外側 = コマンド上限 + 安定待ち + 余裕。 例: 120000(2 分)= 60,000 + 2,000 + 58,000(上記の差分値)。 - 受信: 外側 = コマンド上限 + 安定待ち + 余裕。 例: 120000(2 分)= 60,000 + 2,000 + 58,000(上記の差分値)。 こちらも 1 周回で N 件回収しうるため実際は N × 安定待ちが要る (起動時検証は最低 1 件分・E-2.3 と同じ注意)。 コマンド上限の 60,000ms(1 分)は初期値の目安(実機で調整)。 Biware のコマンドがこの上限を超えると子プロセスを止め、送信は不確定 (indeterminatesending 維持・自動再送しない)、受信は照合待ち (reconciliation-required / receive-failed)になる。 起動時に外側≦内側なら fail-closed で拒否する(RangeError)。 - 送受信モードは別々に選べる(送信だけ command 等)。E-2.3 の推奨値から変える場合は AppEnvironmentExtra に入れ直して nssm restart する。 混ぜた場合の外側は送受信それぞれの式で決める(例: 送信だけ command なら 送信は上記の command 式・受信は上記の scheduler 代替の式)。 - command 受信の停止予算は 110000(コマンド上限 60,000 +安定待ち 2,000 +保存 5,000 +drain 余裕 5,000 +R5 上限 30,000 = 102,000 を上回る)。 受信コマンドの実行中に止めても kill せず完了まで待つため、この余裕が要る。 満たさなければ起動時に拒否する。nssm の AppStopMethodConsole115000(停止予算+余裕 5,000)にする(E-2.4)。

第 7 章の実値(転記済み・手順書 5 冊から転記)

コマンドライン実行の exe・引数・終了コード・STATUS.BIZ の実値。 manual の例示: c:\全銀クライアント\Zexplorer.exe -HセンターA -W受注 -R (「センター A」の「受注」業務を即時実行する手順書の例示であり、実配備値ではない。 実配備では上記の推奨値を使う)。

パラメータ 意味 設定内容
/H センター名 全銀エクスプローラで登録したセンター名を指定
/W 業務名 全銀エクスプローラで登録した業務名を指定
/C 全銀ファイル名のサイクル部 省略時は登録済み番号。指定する場合は 4 桁 16 進数(例: サイクル番号 01 → F0F1(EBCDIC)/ 3031(JIS))
/Q 電文区分標識 照会時のみ有効(連絡時は無視)。省略時は登録済み区分値。0 = 開始要求 1 = 再送要求
/N DLL 番号指定 1 = W32BIZ.DLL(省略時) 2 = W32BIZ2.DLL 3 = W32BIZ3.DLL 4 = W32BIZ4.DLL
/R 即時実行モード ダイアログ表示後、即座に通信を実行
/S サイレント実行モード ダイアログ表示を行わずに通信を実行
/B バッチ実行フラグ /R/S 未指定時に、通信が終了したら全銀エクスプローラを閉じる

注意(manual に明記):

  • センター名と業務名は大小文字を区別する
  • /R・/S の両パラメータを指定しない場合は /C・/Q・/N は使用できない

コマンドライン実行時の戻り値(第 7 章 3.2):

状態 意味
通信開始 0 正常終了(通信が開始され、正常に終了した時)
全銀エクスプローラ側エラー 1001 インスタンスの初期処理失敗(1) ※通常は発生しません
1002 インスタンスの初期処理失敗(2) ※通常は発生しません
1003 ZExplorer.ini ファイルの所在確認失敗
1004 実行モードが指定されていません(但しセンター名と業務名を指定した場合はエラーではなく、通信選択ダイアログ表示状態で起動)
1005 センター名が指定されません
1006 業務名が指定されません
1007 コマンドラインパラメータエラー
1008 パスワード確認失敗
1009 指定されたセンター名または業務名が存在しない
1010 通信前処理失敗
1011 全銀パスワードが設定されていません
1012 ファイルアクセスキーが設定されていません
1013 受信ファイル名が指定されていません
1014 送信ファイル名が指定されていません
1015 未定義
1021 通信制御ファイルの作成時にエラーが発生した
1022 通信前送信ファイルのコード変換がエラー発生しました
1023 通信の実行エラーが発生しました(W32BIZ.DLL が見つからない場合)
1024 通信後受信ファイルのコード変換がエラー発生しました
1025 通信後処理失敗
1026 通信選択画面よりキャンセルされました(バッチ実行時戻す)
1027 通信実行待ち中にキャンセルされました
通信準備 負値 通信準備中のエラー(終了ステータスが 3-XX-XX の時の詳細コード)
通信開始 正値(上記 1001–1027 以外) 通信開始後の異常終了エラー(終了ステータスの詳細コード)

終了コードの符号化規則(第 7 章 3.3。ここを誤ると判定を誤る):

終了コードは X - XX - XX の形で、戻り値は下 2 桁を表す。 下 2 桁の 2 桁目は次の規則で加算する(A = 100 / B = 200 / C = 300)。 上記の規則と下 1 桁を加算した値が終了コードとして出力される。

戻り値 終了コード 内容
2 X-XX-02
77 X-XX-77
100 X-XX-A0 A(100) + 0
105 X-XX-A5 A(100) + 5
110 X-XX-AA A(100) + A(10 進数:10)
115 X-XX-AF A(100) + F(10 進数:15)
205 X-XX-B5 B(200) + 5
211 X-XX-BB B(200) + B(10 進数:11)
300 X-XX-C0 C(300) + 0

終了情報ファイル STATUS.BIZ(プログラミング説明書 6-3 / 6-4 / 6-5):

  • 128 バイト / 1 論理レコード。含まれる情報はすべて JIS8 単位コードの印字可能文字
  • 終了情報ファイル ID = F4(X'4634')(バイト 1–2)。これにより Biware 以外が作った STATUS.BIZ と区別できる
  • レコードの桁構成(manual の図の桁数並び): 2 / 1 / 2 / 2 / 8 / 5 / 2 / 6 / 10 / 83 / 5 / 2
  • 2 = 終了情報ファイル ID(F4
  • 1 + 2 = 終了情報ステータス(状態コード 1 桁 + エラー終了時のエラー詳細コード 2 桁)
  • 2 = 終了情報ステータス(2)(以降は FILLER 扱い)
  • 8 = 通信終了時の総送受信済レコード件数
  • 5 = 通信終了時の総送受信済伝送テキスト件数
  • 2 = 制御電文の処理結果コード
  • 6 = 通信ファイル番号
  • 10 = SSL ステータス
  • 83 = FILLER(X'20')
  • 5 = 別途記録先(下記の ※2)
  • 2 = CR LF
  • 状態コード(終了情報ステータスの 1 桁目): 0 … 正常終了(または通信初期状態)/ 1 … 照会モード通信中エラー発生 / 2 … 連絡モード通信中エラー発生 / 3 … パラメータエラー(準備中)発生
  • 通信ファイル番号(6 桁): 複数ファイル転送時に 000001999999 の 送受信ファイル・シーケンス番号が設定される
  • 制御電文の処理結果コード: ホストから最後に受信した「制御電文」上の項目 「処理結果(1 バイト)」を JIS8 コード表現で 2 桁にセットし直したもの
  • ※2: 構成ファイル CONFIG.BIZ の [114] バイト(ファイル正常終了の条件)で 1 が 設定された通信で、閉局要求送信 / 閉局回答受信でエラーが発生した場合、 終了情報ステータスが別途記録される(STATUS.BIZ の [122]〜[126] バイト)

終了コードの判定契約(当日用):

  • 0 だけを成功とし、それ以外は下の段で分類する
  • 10011027全銀エクスプローラ側エラー(表の個別メッセージを併記。 通信の前後はこの群名で判断しない。 1024 は「通信後受信ファイルのコード変換」、 1025 は「通信後処理失敗」と個別説明があるため、群で前後を断定すると 通信後のローカル処理失敗から「送っていない」と誤判断しうる。 前後は個別説明・STATUS.BIZ・センター側記録で確認する。 実装は REJECT 集合が空なら 1024 等も未送信確定にせず不明扱いにする)
  • 負値は通信準備中(終了ステータス 3-XX-XX
  • 正値(1001 未満)は通信開始後の異常終了で、上の符号化規則で X-XX-XX に戻して読む
  • 0 以外を一律に「送信失敗」と書かない。 どの段で落ちたかで当日の手当てが変わる
  • REJECT / NOFILE の欄(wl-w6nn・任意・既定は空)に載せた code だけ分類が働く。 空のままなら送信の非 0 は全部「送ったか不明」、受信の非 0 は warn の上で回収 → 再試行

複数ファイル転送と CONFIG.BIZ の設定(接続テストの成否に直結)

複数ファイル転送追加機能説明書の実値。runbook に無かった内容で、 設定を誤ると接続テストの結果ファイルが失われる。

  • 回線接続から回線切断までの間に、複数ファイルの送受信を連続実行できる。最大 999,999 件
  • 全銀ファイル名やファイルアクセスキーは 1 ファイルごとに指定
  • 「開始要求」から「終了要求」までを 1 ファイルとして連続送受信
  • ファイル転送中にエラーが発生したときは回線が切断され、以降のファイル転送は実行されない
  • 単一 / 複数の区別は ORDER.BIZ の第 3 レコードが設定されているか否かで判断される
  • 追加のパラメータエラーコード: 3-00-55 = 送受信ファイル数が最大値 999,999 件を超えた
  • モード変更(送信と受信の混在)に伴う通信状態コード: 04 = 通信制御電文のモード変更要求送出中、 05 = モード変更回答受信待ちまたは受信中。通信制御電文区分は 00 開局要求 / 01 開局回答 / 02 閉局要求 / 03 閉局回答 / 04 モード変更要求 / 05 モード変更回答

エラー発生時の動作(manual 3-4・逐語要旨):

  • 通信実行前に全送受信ファイルのパラメータチェックを行い、パラメータエラーが発生した場合は その時点で中断し、パスワード情報テーブル・終了情報ファイルにエラーコードを設定。通信は行わない
  • 送受信中にファイル無し(0-00-31「センター側ファイル無しを受信した」)以外のエラーが 発生した場合、その時点で通信を中断し、以降のファイル送受信は行わない
  • 全銀協の規定では、複数ファイル伝送で 0-00-31 以外のエラーが発生した場合、 すべての受信ファイルを破棄することになっており、Biware も規定通りの動作をする
  • Biware 独自の拡張仕様として、通信が異常終了しても、正常に受信できたファイルだけを 破棄せず残すように動作させることも可能(CONFIG.BIZ の設定)

複数ファイル伝送時の STATUS.BIZ 出力例(manual の該当箇所からの抜粋。 完全な 128 バイトの生データではなく省略表示。先頭 2 行は 28 バイト、 3 行目は 49 バイトで、3 行目の末尾 6 文字は連番ではなく 20320)。 config[114]='1' で閉局回答受信に電文区分コードエラーが発生した場合。 1 ファイルごとに 1 レコードが出力され、通信ファイル番号(本文の構成から 23–28 バイト目)が 000001→000002→000003 と増える。wl-b4r の受信 BOX 複数結果の取りこぼしはこの PR では直さないが、後で使う事実としてここに残す):

F400000000000010000100000001
F400000000000010000100000002
F400000000000010000100000003                20320

CONFIG.BIZ の追加項目(第 1 レコード・複数ファイル転送追加機能説明書 2.1):

項目 設定値
61〜113 予備(未使用エリア) スペース(X'20')を詰める・53 桁
114 ファイル正常終了の条件 0 … 閉局回答電文を受信した時点で正常終了とする / 1 … 終了回答電文を受信した時点、またはそれ以降
115 0 バイトファイルの受信(照会時のみ) 0 … エラーになる(ファイルを削除する) / 1 … 受信ファイルのサイズが 0 バイトの場合、パラメータ確認時に 0 バイトファイルとして残す
116 0 バイトファイルの送信(連絡時のみ) 0 … パラメータエラーになる / 1 … エラーとせずに「開始回答」受信後「終了要求(処理結果:正常)」を送出する
117 ファイル破棄基準(照会モードのみ有効) 0 … 閉局回答電文を正常に受信したとき以外は、すべての受信ファイルを破棄する / 1 … 終了回答電文を正常に送信したとき以外は受信ファイルを破棄する(1 ファイルごとに正常に受信したファイルが残る)

注意(manual に明記): 0 および 1 以外の値が設定されたときは 0 と見なす。 ただし今後保証されるものではない。

[117] は既定 0 のままにしない。 複数ファイル受信の途中で 0-00-31 以外の 中断エラーが起きたとき、既に正常受信できた分も全部破棄される。センターは受信済み結果を再受信できない (結果ファイルは作成日から 2 週間保管・受信済みの再受信は不可)ので、 破棄された分は回復できない。確定値は [117]='1'(正常受信分を残す。確定・禹さん 2026-09-20): 接続テストは 26 機能を 9 営業日で回し、1 件の取りこぼしが当該試行の結果証跡を失わせる。 全銀協規定どおりの全破棄は、再送のないセンター相手では回復不能だからである。 H8(9/29 の配備)で設定し、R-2 再実行時に反映を確認する。 当日の前提は手順書 13 章 §6・仕様書 §8 で確認する。 [114]1 を設定した通信では STATUS.BIZ [122]〜[126] に閉局系のエラーが 別途記録される(上記の ※2)。

圧縮と転送後処理フックの注記:

  • 要求ファイルは圧縮ありで作る。圧縮したデータが伸びる(繰返し文字が無い)場合、 通信エラーとなるため、R-2 で圧縮ありの実 CSV を通し、圧縮由来の通信エラーが 出ないことを確認する(H4/H5 行に既記)
  • 転送後処理の「受信データなし時」フックは、終了コード 31 の導出が R-2 で 外れた場合の代替検知として使える(複数転送業務では業務個別のフックは起動しない)

E-2.7 IPsec 状態エンドポイント(ルータ VM 側)

JWNET_GATEWAY_IPSEC_STATUS_URL の実体。swanctl --list-sas を解析して { established, childInstalled, checkedAt } の JSON を返す小さな常駐 (socket activation ではなく常駐を選ぶ。無視できる常駐コストで済み、 unit 1 つで起動・再起動・ログを systemd に任せられる)。 実装は repo の scripts/gateway/strongSwan runbook §7 の SA 確認が手動版の正典。本節はその常駐公開版)。

  1. Cloud Shell からルータ VM へ配置する(Linux なので scp が使える)。

    export PROJECT_ID='kyoei-jwnet-gw'
    gcloud compute scp scripts/gateway/ipsec-status.sh scripts/gateway/ipsec-status-server.py \
      scripts/gateway/jwnet-ipsec-status.service scripts/gateway/ipsec-status.sudoers \
      jwnet-ipsec-vm:/tmp/ --tunnel-through-iap --zone=asia-northeast1-b --project="$PROJECT_ID"
    gcloud compute ssh jwnet-ipsec-vm --tunnel-through-iap --zone=asia-northeast1-b --project="$PROJECT_ID"
    
  2. ルータ VM 内で user・sudoers・unit を入れる(swanctl は root 権限が要るため、 専用ユーザー + 引数固定の sudoers で最小権限にする)。

    sudo useradd --system --no-create-home --shell /usr/sbin/nologin jwnet-ipsec-status
    sudo mkdir -p /opt/jwnet-ipsec-status
    sudo install -o root -g root -m 0755 /tmp/ipsec-status.sh /tmp/ipsec-status-server.py /opt/jwnet-ipsec-status/
    sudo install -o root -g root -m 0644 /tmp/jwnet-ipsec-status.service /etc/systemd/system/
    sudo install -o root -g root -m 0440 /tmp/ipsec-status.sudoers /etc/sudoers.d/jwnet-ipsec-status
    sudo visudo -c -f /etc/sudoers.d/jwnet-ipsec-status
    sudo systemctl daemon-reload
    sudo systemctl enable --now jwnet-ipsec-status.service
    sudo systemctl status jwnet-ipsec-status.service --no-pager
    curl -s http://10.100.121.2:8788/ipsec-status
    

    unit の --bind はルータの内部 IP(外部 IP 側では listen しない二重化)。 異なる内部 IP の環境では unit と URL を実 IP に合わせる。

  3. ゲートウェイ VM の内部 IP からのみ許可する FW を作る(Phase B の書式)。

    gcloud compute firewall-rules create allow-gw-ipsec-status \
      --network=jwnet-vpc --direction=INGRESS --action=ALLOW \
      --rules=tcp:8788 --source-ranges=10.100.121.10/32 --target-tags=jwnet-ipsec
    

    10.100.121.10/32 は移設後の Windows VM の内部 IP(実 IP に合わせる)。 ルータ VM には jwnet-ipsec タグが付いていること(configure-network.sh が付ける)。

  4. ゲートウェイ VM(Windows・PowerShell)から到達確認する。

    Invoke-WebRequest http://10.100.121.2:8788/ipsec-status | Select-Object -ExpandProperty Content
    

    {"established":true,"childInstalled":true,...} で期間前の確認は完了。 false が混ざるのは期間前は正常(センターが IKE を受けない可能性)で、 JSON が返ること自体が本節の疎通確認である。

認証なしでよい根拠: 返すのは boolean 2 つと時刻だけで、PSK・鍵素材・payload を 含まず、bind を内部 IP に固定したうえで送信元を gateway VM の /32 に絞るため。

E-2.8 更新手順

  1. 新しい release commit の zip を E-2.2 手順 1 でバケットへ置く。対象版の commit SHA(短 SHA・版名に使う)と zip の SHA-256 (shasum -a 256 gateway-deploy.zip)を控える。
  2. VM 内で稼働中の版を保全してから止める(新版の取得より先。 新版の zip を取ってから保全すると、保全が新版で上書きされる)。

    # 前回の更新記録が稼働中の版と一致することを確認する。
    # 不一致なら前回の CURRENT.txt 更新漏れのため、先へ進まず判定者へ上げる。
    $currentCommit = (Get-Content C:\greco\releases\CURRENT.txt -TotalCount 1).Trim()
    $liveCommit = (Get-Content C:\greco\gateway\VERSION.txt -TotalCount 1).Trim()
    if ($currentCommit -ne $liveCommit) {
      throw 'CURRENT.txt と稼働中の VERSION.txt が不一致。前回の更新記録を確認して判定者へ上げる。'
    }
    # 稼働中の版を保全する。
    Copy-Item C:\greco\releases\CURRENT.txt C:\greco\releases\PREVIOUS.txt
    # 現在位置が C:\greco\gateway の中だと Rename-Item が拒否される(RenamedItemInUse)ため木の外へ出る。
    Set-Location C:\greco
    nssm stop GrecoJwnetGateway
    if (Test-Path C:\greco\gateway.prev) {
      Rename-Item -Path C:\greco\gateway.prev -NewName "gateway.prev-$(Get-Date -Format 'yyyyMMdd-HHmmss')" -ErrorAction Stop
    }
    Rename-Item -Path C:\greco\gateway -NewName 'gateway.prev' -ErrorAction Stop
    

    既存の gateway.prev(前々版)は時刻つきに退けて残す。 PREVIOUS.txt は直前の 1 世代だけを指す。 3. 新版の zip を取得し、手順 1 の控えと照合してから導入・起動する。

    $commit = '<新版の release commit(短 SHA)>'
    gcloud storage cp gs://kyoei-jwnet-gw-deploy/gateway-deploy.zip C:\greco\gateway-deploy.zip
    $hash = (Get-FileHash C:\greco\gateway-deploy.zip -Algorithm SHA256).Hash
    # $hash が手順 1 で控えた SHA-256 と一致することを確認してから進む。
    Copy-Item C:\greco\gateway-deploy.zip C:\greco\releases\gateway-deploy-$commit.zip
    Expand-Archive C:\greco\gateway-deploy.zip -DestinationPath C:\greco\gateway
    Remove-Item C:\greco\gateway-deploy.zip  # 用済みの一時名は残さない(古い zip の誤展開を防ぐ)。
    $commit | Set-Content C:\greco\gateway\VERSION.txt
    # 空の木に原本・ログ用のフォルダを作り、E-2.4 と同じ権限を付ける
    # (作り直したフォルダは親の継承だけでサービスアカウントの書込権が無い)。
    New-Item C:\greco\gateway\artifacts, C:\greco\gateway\logs -ItemType Directory -Force
    icacls C:\greco\gateway\artifacts /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls C:\greco\gateway\logs /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls C:\greco\gateway\artifacts /inheritance:r
    icacls C:\greco\gateway\logs /inheritance:r
    cd C:\greco\gateway
    pnpm install --frozen-lockfile
    pnpm --filter @waste-link/jwnet-gateway build
    nssm start GrecoJwnetGateway
    

    旧版の原本・証跡は gateway.prev\artifactsgateway.prev\logs に残る(消さない)。 展開・導入に失敗したら C:\greco\gateway を消して手順 3 からやり直す (gateway.prevPREVIOUS.txt は残っている)。 4. health(E-2.5)が 200 で polling-ok になることを確認する。 5. CURRENT.txt を新版で更新する。

    # CURRENT.txt を新版(手順 1 の控え)で更新する。
    $commit = '<新版の release commit(短 SHA・手順 1 と同じ)>'
    $hash = (Get-FileHash C:\greco\releases\gateway-deploy-$commit.zip -Algorithm SHA256).Hash
    # $hash が手順 1 で控えた SHA-256 と一致することを確認してから進む。
    "$commit`n$hash`n$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Set-Content C:\greco\releases\CURRENT.txt
    
  3. 1 サイクルの空回し: loop 間隔(既定 60 秒)を 2 回分待ち、polling-ok のまま・ サービスログに error なし・exchange 行に変化なしを確認する (claim が 0 件の cycle はログを出さず idle で終わる)。

E-2.9 運用の注意

  • 閉局(接続テストは平日 17:00)の直前は新しいサイクルを始めない実装である。 起動時の end-margin に加え、送信直前と各受信直前に再判定し、間に合わなければ cycle の結果を deferred で終える。deferred は失敗ではないが、行の状態で操作が 違う。未 claim の queued は翌営業日にそのまま送る。送信直前の判定に落ちた行は markError(..., 'closing-window-before-send')error になり自動再送されない (操作者が再生成する)。受信前の中断は行を sent のまま残し、翌営業日に resumePending で再開受信する(再生成は不要)。
  • 外側の送受信期限を大きくすると 1 サイクルの見積もりが伸び、閉局前に始められる 時刻が早まる(estimateMaxCycleMs / estimateRemainingCycleMs が外側の値を 使うため)。既定 30 秒ずつでは最大 24 分(残り 21 分)で、17:00 閉局なら 16:36 以降は新規送信を始めない(16:35 は開始可)。推奨値(送信 360 秒・受信 180 秒) では最大 54 分 30 秒(残り 51 分 30 秒)で、16:05:30 以降は始めない(16:05 は 開始可・16:06 は翌営業日へ)。受信期限は試行回数(10 回)だけ掛かるため、 伸ばすほど夕方の送信枠が前にずれる。Biware 側を延ばしたら外側も連動させる。
  • reconciliation-required の行は人が JWNET 側を確認して DB を直す運用で、 自動再送しない(gateway-restarted-during-send / receive-timeout / receive-drain-incomplete 等)。drain 切れは receivedDir の未取込回収が先で、 手順は Phase F の回収節。受理済みの報告を再送すると二重報告になる。
  • 送信前スキャンで脅威を検出したら送らず、行は errorvirus-check-failed)になる。 要求ファイルは Greco が生成した素の電文であり、検出時は混入経路を調べてから再生成する。

E-2.10 更新の切り戻し

新版の導入後に異常が出たら、直前の版へ切り戻す。判断基準(いずれかに当てはまれば切り戻す):

  • health(E-2.5)が 200 に戻らない
  • 送信サイクルが 2 回連続で失敗する
  • 期間中に新版で未知の error code が出る

主経路は E-2.8 で退避した gateway.prev(稼働実績のある木・node_modulesdist を含む)の入れ替えで、再 install / build は不要。手順 1〜2 の照合が通ってから止める。

  1. PREVIOUS.txt を読み、旧版の commit と zip の SHA-256 を確認する (無ければ切り戻し先なし → 切り戻しを始めず判定者へ上げる)。

    $prevPath = 'C:\greco\releases\PREVIOUS.txt'
    if (-not (Test-Path $prevPath)) {
      throw 'PREVIOUS.txt が無い(切り戻し先なし)。切り戻しを始めず判定者へ上げる。'
    }
    $prev = @(Get-Content $prevPath)
    $oldCommit = $prev[0]
    $oldHash = $prev[1]
    
  2. gateway.prev\VERSION.txt が旧版の commit と一致することを確認する。 一致しなければ(ファイルが無い場合も含む)止めて判定者へ上げる。 gateway.prev のディレクトリ自体が無い場合だけ、主経路を止めて手順 6 の 第二経路へ進む。

    $prevDir = 'C:\greco\gateway.prev'
    $prevVersion = "$prevDir\VERSION.txt"
    if (-not (Test-Path $prevDir)) {
      throw 'gateway.prev が無い。主経路を止め、手順 6 の第二経路へ進む。'
    }
    if (-not (Test-Path $prevVersion)) {
      throw 'gateway.prev\VERSION.txt が無い。切り戻しを止めて判定者へ上げる。'
    }
    $prevCommit = (Get-Content $prevVersion -TotalCount 1).Trim()
    if ($prevCommit -ne $oldCommit) {
      throw 'gateway.prev の VERSION.txt が PREVIOUS.txt と不一致。切り戻しを止めて判定者へ上げる。'
    }
    
  3. サービスを止め、新版を隔離して旧版の木を戻す。env(環境変数)は変えない。

    # 現在位置が C:\greco\gateway の中だと Rename-Item が拒否される(RenamedItemInUse)ため木の外へ出る。
    Set-Location C:\greco
    nssm stop GrecoJwnetGateway
    $stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
    Rename-Item -Path C:\greco\gateway -NewName "gateway.failed-$stamp" -ErrorAction Stop
    Rename-Item -Path C:\greco\gateway.prev -NewName 'gateway' -ErrorAction Stop
    nssm start GrecoJwnetGateway
    

    新版の木は gateway.failed-<時刻> に残る(原因調査用。消さない)。 artifacts/logs/ は旧版の木と一緒に戻る(原本と証跡が入る)。 改名が失敗したら(RenamedItemInUse / 宛先が既に存在)nssm start へ進まない (-ErrorAction Stop で止まる)。Get-LocationC:\greco\gateway の外に いることを確認し、C:\greco\gatewaygateway.prev の実在を Get-ChildItem C:\greco で見てから手順 3 をやり直す。サービスは停止した ままなので、判定者へ上げてから再試行する。 4. health が 200 で polling-ok になること、VERSION.txt が旧版の commit で あること、--list-sas を確認する。

    $oldCommit = (Get-Content C:\greco\releases\PREVIOUS.txt -TotalCount 1).Trim()
    $liveCommit = (Get-Content C:\greco\gateway\VERSION.txt -TotalCount 1).Trim()
    if ($liveCommit -ne $oldCommit) {
      throw '戻った木の VERSION.txt が PREVIOUS.txt と不一致。判定者へ上げる。'
    }
    

    --list-sas は strongSwan VM で確認する(Phase F の朝の確認と同じ)。 5. CURRENT.txt を旧版で上書きし、PREVIOUS.txt を削る (切り戻しを使い切ったので、次回 E-2.8 まで切り戻し先なし)。

    $prev = @(Get-Content C:\greco\releases\PREVIOUS.txt)
    $oldCommit = $prev[0]
    $oldHash = $prev[1]
    "$oldCommit`n$oldHash`n$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') (E-2.10 rollback)" | Set-Content C:\greco\releases\CURRENT.txt
    Remove-Item C:\greco\releases\PREVIOUS.txt
    
  4. 第二経路(gateway.prev が無い場合だけ): 版名つきの旧版 zip を PREVIOUS.txt の SHA-256 と照合し、一致したときだけ空ディレクトリへ展開して 導入し直す。照合が通るまで止めない。

    $prev = @(Get-Content C:\greco\releases\PREVIOUS.txt)
    $oldCommit = $prev[0]
    $oldHash = $prev[1]
    $zip = "C:\greco\releases\gateway-deploy-$oldCommit.zip"
    if (-not (Test-Path $zip)) {
      throw '旧版の zip が無い。切り戻しを止めて判定者へ上げる。'
    }
    $actual = (Get-FileHash $zip -Algorithm SHA256).Hash
    if ($actual -ne $oldHash) {
      throw '旧版 zip の SHA-256 が PREVIOUS.txt と不一致。切り戻しを止めて判定者へ上げる。'
    }
    # 現在位置が C:\greco\gateway の中だと Rename-Item が拒否される(RenamedItemInUse)ため木の外へ出る。
    Set-Location C:\greco
    nssm stop GrecoJwnetGateway
    $stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
    Rename-Item -Path C:\greco\gateway -NewName "gateway.failed-$stamp" -ErrorAction Stop
    Expand-Archive $zip -DestinationPath C:\greco\gateway
    $oldCommit | Set-Content C:\greco\gateway\VERSION.txt
    # 空の木に原本・ログ用のフォルダを作り、E-2.4 と同じ権限を付ける。
    New-Item C:\greco\gateway\artifacts, C:\greco\gateway\logs -ItemType Directory -Force
    icacls C:\greco\gateway\artifacts /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls C:\greco\gateway\logs /grant 'SYSTEM:(OI)(CI)F' 'Administrators:(OI)(CI)F' 'svc-greco-gw:(OI)(CI)M'
    icacls C:\greco\gateway\artifacts /inheritance:r
    icacls C:\greco\gateway\logs /inheritance:r
    cd C:\greco\gateway
    pnpm install --frozen-lockfile
    pnpm --filter @waste-link/jwnet-gateway build
    nssm start GrecoJwnetGateway
    

    以降は手順 4(health・VERSION・--list-sas)と手順 5(CURRENT.txt の 上書き・PREVIOUS.txt の削除)を行う。旧版の artifacts/logs/ の内容は gateway.prev が無いため戻らない(結果原本は C:\jwnet\received と DB に残る)。 新版の原本・証跡は gateway.failed-<時刻> に残る。

切り戻し後は Greco の exchange 行を reconciliation-required のまま残し、人が JWNET 側を 確認して判定する。自動再送しない(受理済みの報告を再送すると二重報告になる)。

E-2.11 外形監視の配置(ルータ VM)

接続テスト期間(平日 9:00–17:00)に Windows VM の常駐ゲートウェイが止まっても 人が気づけるよう、ルータ VM の systemd timer から gateway の /health を 5 分ごとに叩き、異常が 3 回連続したら Slack へ 1 回だけ通知する (復旧したら 1 回だけ通知する。鳴らし続けない)。 監視対象と同居させないため Windows 側ではなくルータ VM に置く。 実装は repo の scripts/gateway/health-watch.sh 本体・unit・timer・契約テスト)。 配置は人間が H8 と同日(9/29)に行う。

  1. 受信許可と bind 変更(順序どおりに行う。health は既定で 127.0.0.1 にだけ bind されるため、そのままではルータ VM から届かない)。

    a. GCP の ingress 規則を確認し、無ければ作る(Cloud Shell)。 Windows Firewall だけでは VPC ingress を通れない。

    export PROJECT_ID='kyoei-jwnet-gw'
    # ルータ VM の実 IP を読む(10.100.121.2・実 IP に合わせる)。
    ROUTER_IP=$(gcloud compute instances describe jwnet-ipsec-vm --project="$PROJECT_ID" \
      --zone=asia-northeast1-b --format='value(networkInterfaces[0].networkIP)')
    gcloud compute firewall-rules describe jwnet-allow-router-health --format='value(name)' 2>/dev/null \
      || gcloud compute firewall-rules create jwnet-allow-router-health \
           --network=jwnet-vpc --direction=INGRESS --action=ALLOW --priority=1000 \
           --rules=tcp:8787 --source-ranges="$ROUTER_IP/32" --target-tags=jwnet-biware
    

    b. Windows Firewall の許可(管理者 PowerShell)。

    # ルータ VM(10.100.121.2・実 IP に合わせる)からのみ TCP 8787 を許可する。
    New-NetFirewallRule -DisplayName 'Greco gateway health (router only)' -Direction Inbound -Protocol TCP -LocalPort 8787 -RemoteAddress 10.100.121.2 -Action Allow
    

    c. 内部 IP に bind し直す(管理者 PowerShell)。

    # E-2.4 手順の nssm set ... AppEnvironmentExtra に 1 項目足して実行し直す。
    # set は上書きなので既存項目を落とさないこと(現在値は nssm get で控える)。
    # 10.100.121.10 は移設後の Windows VM の内部 IP(実 IP に合わせる)。
    nssm set GrecoJwnetGateway AppEnvironmentExtra "<既存の全部>" "JWNET_GATEWAY_HOST=10.100.121.10"
    nssm restart GrecoJwnetGateway
    # 0.0.0.0 ではなく内部 IP に bind する(FW との二重化)。
    

    d. ルータ VM から疎通を確認する(手順 3 と同じ gcloud compute ssh で ルータ VM に入って実行。10.100.121.10 は Windows VM の内部 IP)。

    curl -sS --max-time 5 http://10.100.121.10:8787/health
    

    200 が返れば受信路の完成。E-2.5 の手順は既定(127.0.0.1)の確認用で、 ここでは http://<JWNET_GATEWAY_HOST>:8787/health(内部 IP)で確認する。

  2. Slack の Incoming Webhook URL を Secret Manager に直接登録する(Cloud Shell)。 URL は手順書・チャット・repo に出さない。

    export PROJECT_ID='kyoei-jwnet-gw'
    # docs/slack-notifications.md の Step 1 で発行した URL を登録する。
    printf '%s' '<Slack の Webhook URL>' | gcloud secrets create jwnet-gateway-watch-slack --data-file=- --project="$PROJECT_ID"
    # 既存の更新: printf '%s' '<URL>' | gcloud secrets versions add jwnet-gateway-watch-slack --data-file=- --project="$PROJECT_ID"
    

    ルータ VM の SA に対象 secret の roles/secretmanager.secretAccessor を付与する (strongSwan runbook §2 の手順 2 と同じ)。

  3. Cloud Shell からルータ VM へ配置する。

    export PROJECT_ID='kyoei-jwnet-gw'
    gcloud compute scp scripts/gateway/health-watch.sh scripts/gateway/install-health-watch-secrets.sh \
      scripts/gateway/jwnet-gateway-health-watch.service scripts/gateway/jwnet-gateway-health-watch.timer \
      jwnet-ipsec-vm:/tmp/ --tunnel-through-iap --zone=asia-northeast1-b --project="$PROJECT_ID"
    gcloud compute ssh jwnet-ipsec-vm --tunnel-through-iap --zone=asia-northeast1-b --project="$PROJECT_ID"
    
  4. ルータ VM 内で env・unit・timer を入れる(env を書いてから timer を有効化する。 GW_HEALTH_URL が空のまま有効化すると初回から失敗する)。

    sudo mkdir -p /opt/jwnet-gateway-watch
    sudo install -o root -g root -m 0755 /tmp/health-watch.sh /tmp/install-health-watch-secrets.sh /opt/jwnet-gateway-watch/
    sudo install -o root -g root -m 0644 /tmp/jwnet-gateway-health-watch.service /tmp/jwnet-gateway-health-watch.timer /etc/systemd/system/
    # 10.100.121.10 は Windows VM の内部 IP・8787 は E-2.3 の port(実値に合わせる)。
    # webhook URL 本体は書かない(secret 名だけ。URL は ExecStartPre が取得する)。
    sudo tee /etc/default/jwnet-gateway-health-watch > /dev/null <<'EOF'
    GW_HEALTH_URL=http://10.100.121.10:8787/health
    PROJECT_ID=kyoei-jwnet-gw
    SLACK_WEBHOOK_SECRET_NAME=jwnet-gateway-watch-slack
    SLACK_WEBHOOK_SECRET_VERSION=latest
    SLACK_WEBHOOK_URL_FILE=/run/jwnet-gateway-watch/slack.url
    WATCH_STATE_DIR=/run/jwnet-gateway-watch
    EOF
    sudo systemctl daemon-reload
    sudo systemctl enable --now jwnet-gateway-health-watch.timer
    sudo systemctl list-timers jwnet-gateway-health-watch.timer --no-pager
    # 初回を待たずに 1 周だけ手動実行する(env の確認。oneshot なので 1 周で終わる)。
    sudo systemctl start jwnet-gateway-health-watch.service
    sudo journalctl -u jwnet-gateway-health-watch --no-pager -n 5
    

    jwnet-gateway-watch time=... status=ok http_code=200 ... が出れば配置完了。 status=fail / status=unreachable なら手順 1 の bind・FW・VM 起動を見直す。 state は /run(tmpfs)にあるため、ルータ VM の再起動で連続回数は 0 に戻る。

鳴ったらどうするか: Slack の文面は異常(statuscode・連続回数・時刻)/ 到達不能 / 復旧の 3 種。code がある異常は Phase E-2.5 の表で切り分け、IPsec 由来の codeipsec-health-*)は strongSwan runbook §9 の表で 切り分ける。到達不能は VM 起動・サービス・FW・bind(本節の手順 1)・VPC route の 順に見る。送信中に鳴ったら Greco の送受信キュー(exchange 行)の状態を確認し、 自動再送はしない(E-2.9 と同じ。受理済みの報告を再送すると二重報告になる)。 運用時間外(平日 9:00–17:00 JST の外)は通知しない。朝の確認(Phase F 手順 1)で 直近ログを見る。祝日(10/12)も watch は動く: gateway は停止日に送信を defer するが health は 200 のはずで、到達不能や 503 なら通知される。 9/29 の配置時に運用者へ明示すること。

Phase F — 接続テスト(期間 2026-10-05〜10-18・平日 9:00–17:00・VM 内)

設定票の接続テスト期間内に実施する。運用時間は平日(月〜金・土日祝除く)9:00–17:00 (EDI 仕様書 §3-2(1) システム運用スケジュール・印刷ページ 3-12 の実測。本番 4:00–24:00 / デモ 8:00–18:00 とは別枠。 以前この手順書にあった「19:00–23:00」は電子契約機能の時間で、標準EDI 接続テストの時間ではない)。 メンテ停止日(1/1-3・5月第1日曜・8月第2/3土日・10月第2日曜=10/11・その他)は運用しないが、 接続テストは平日運用なので日曜の 10/11 は元々対象外。

接続テストは疎通の合否判定で、不合格だとシステム登録できない。準備の順序・確認先・ゲートは docs/superpowers/plans/2026-09-06-jwnet-connection-test-first-pass-plan.md(内部文書)に従い、 期間前に潰せる項目は Phase E までに終えておく。1 サイクルの手順は EDI 仕様書 3-1 の推奨フローに合わせる。

主経路は E2E(ゲートウェイ搬送) で、手渡しは保険として残す。 人がやるのは画面の操作と判定の確認である(運用手順書 10 章)。 テストケース・日別計画・証跡は テスト仕様書 が正典。

主経路 — E2E(Greco → ゲートウェイ → Biware → センター → Greco)

  1. 朝の確認(人): 生成端末・Windows VM・strongSwan VM の時刻が NTP で JST に 同期していることを確認する(HD1 の作成日付 / 作成時間はセンターが妥当性を 検査する・EE01009 / EE01010)。次に strongSwan VM で sudo swanctl --list-sas を実行し、jwnetESTABLISHED, IKEv2、child が INSTALLED, TUNNEL、TS が 10.100.121.0/24 === 210.164.154.24/30 であることを 確認する(strongSwan runbook §7)。 NG は同 runbook §9 の表で切り分ける。加えてゲートウェイの health (http://<JWNET_GATEWAY_HOST>:8787/healthE-2.11 で内部 IP に 変えた後の値。Phase E-2.5127.0.0.1 では応答しない)が 200 であることを確認する。watch の直近ログ (journalctl -u jwnet-gateway-health-watch)が 200(status=ok)かも確認する (E-2.11)。ついでにデモ環境の発信先への PING (Test-NetConnection 210.164.154.26)も試してよい。通らなくても失敗ではない。
  2. 疎通の確認(人): Windows VM(Windows App で接続)の PowerShell で Test-NetConnection 210.164.154.26 -Port 5020 (strongSwan VM 経由でトンネルに乗る・同 runbook §8)。

送信前条件(毎回・warn の有無に依存しない): 要求を投入・送信する前に C:\jwnet\inbox が空であることを 確認する。空でなければ次の要求を送らず、下記の edi-receive-partial の確認手順へ進み、当日体制の判定者へ 上げる。R3 は sending / sent の行がある場合だけ新しい行を渡さず、received の行やローカル inbox は 阻止条件にしないため、この残留ではゲートウェイは止まらない。 3. 要求の生成(人・画面操作): Greco の接続テスト画面で送り先に 「ゲートウェイで送信」を選び、「要求ファイルを生成」を押す (運用手順書 10 章)。要求コードは画面の採番を使う (30 日内で同じコードを再利用できない)。生成した要求は送受信キューに入り、 要求ファイル本文は暗号化して保存される(平文は Greco に残らない)。 4. 送信(ゲートウェイ): キューを 1 ファイルずつ claim → 復号 → 原本保存 → ウィルスチェック → Biware で送信する。前の結果を受信するまで次は送らない。 送信結果ステータスが正常でない全銀レベルの失敗は、JWNET の EDI 処理状況照会 ページには載らない(仕様書 7-3)。Biware コマンドの送信結果は 2 分岐である。 送信拒否(行が error/biware-send-rejected・REJECT 集合に載せた終了コード のときだけ)→ Biware のログを読んで直し、別の要求コードで再生成する (自動再送はしない)。送信不明(行は sending のまま残り、次回以降の R7 在庫確認(再起動時を含む)で gateway-restarted-during-send の照合待ちに なる。遷移を待つための再起動は不要)→ EDI 処理状況照会と Status.biz で受理の有無を確かめてから再生成を判断する(受理済みを再送すると 二重報告になる)。集合が空の間は送信の非 0 は全部この不明扱いである。 5. 5 分待ち(ゲートウェイ): 送信完了から 5 分以上待つ (仕様書 §3-1 推奨フロー (2.3)。加入者情報件数・照会件数で処理時間は延びる)。 6. 受信(ゲートウェイ): 結果ファイルを新規要求で受信する (「再送要求」にすると結果ファイルが受信できなくなる・仕様書 §3-3(4) No.19・ §3-3(7) 4)。受信結果が正常でないときは受信ステータスを分析して原因を除き、 再受信する(§3-1 推奨フロー (2.5))。「ファイルなしレスポンス」なら 5 へ戻り、 最大 10 回試す。受信非 0(Biware 受信コマンドの非 0 終了)は warn(edi-biware-receive-command-failed・集合外の code のとき)の上で 回収 → 再試行を続け、止めない。「ファイルなし」の終了コード(NOFILE 集合) を転記した後はその code だけ info 扱いになる。取れないまま終わった行は reconciliation-required (通常は receive-timeout、drain 切れは receive-drain-incomplete)になり、 人が下記の 4 分岐(drain 切れは上記の回収手順)で切り分ける。 HR1 の要求コードを照合し、結果を DB に記録する。 7. 判定(人・判定の確認): 受信済の行で「判定を表示」を押し、 HR1 → HR2 → 個票 → E01 の順に判定する (運用手順書 10 章「判定と E01 の読み方」)。結果は C:\jwnet\receivedartifacts/.result.*.edi)に原本のまま保全される(SHIFT_JIS・BOM なし・ エディタで開いて再保存しない)。要求は送信 cycle の finallydiscardRequestartifacts/ から消える(再起動時も残存 *.request.edi を 消す。平文を残さない設計)。監査に残るのは DB の request_sha256request_byte_length・exchange events 等である。要求原本を保全したい場合は Biware の archive(送信済みの退避。Biware 側の設定)を使う。 E01 があれば入力を直して別の要求コードで再送する。 HR1 3(一部エラー)で受理された分は再送しない。 8. 通す機能ごとに「E01 の付かない結果ファイルを 1 度得る」まで 3〜7 を繰り返す。 順序は手順書 §4 の TEST(画面から)→ 3101(E2E 主経路の初回)→ 3000 → 3102 → 1000 → 1300 → 0701 → 残りの収運・処分・排出(準備計画 §6-7 の順序)。3201 は手渡しで 1 回。 Day-1 の先頭は画面から TEST を送る(保険は手渡し)。結果は相関失敗(result-correlation-failed)または解析失敗(result-parse-failed)のどちらかで照合待ちになる(両方とも正)。画面の判定入口はファイル名 / HR1 要求コード検査で 422 になるので、受信原本の E01(フォーマット不正)を目視する。 日別の割付は13 章 §4「日別の進め方」に従う。 更新系は 16:00 まで(仕様書 §3-2(2)「日付の整合性」の推奨: 運用停止の 1 時間前)、 照会系は 16:30 まで(当方ルール)に送る。閉局直前はゲートウェイが新規サイクルを 始めず deferred で翌営業日に回す(Phase E-2.9)。 9. 日次(人): 要求・結果の原本、生成記録、Biware の応答、判定記録を要求コードで まとめる。PSK・全銀認証値・確認キー・鍵素材・通信 payload は記録に含めない。 10. ハンドシェイク成立が、ゲートウェイ設計(2026-07-25-jwnet-gateway-design.md) Phase 0-B(iv) の受入条件。 接続テスト通過後、本番用設定票(別値)を受領して本番へ切り替える

edi-receive-drain-incomplete の回収(人・wl-9txz R3): 行が reconciliation-required / receive-drain-incomplete(通常の枯渇 receive-timeout とは 別の code)で、ログに edi-receive-drain-incompletereceivedDirstreamedCountrequestCodetrigger 付き)が出たら、receivedDir(既定 C:\jwnet\received・ ログの receivedDir が正)に DB 未取込の結果が残っている。退避済みの分も DB に載せていないため、逐次引き渡しの「退避済みは載る」は当てはまらない。

  1. receivedDir の一覧を取り、ログの requestCode と突き合わせる。 streamedCount は abort 時点で通知済みだった件数で、未通知の残りが あることを示す。ファイルは原本のまま保全する(SHIFT_JIS・BOM なし・ エディタで開いて再保存しない)。
  2. 遅い I/O が settle したか確認する。ゲートウェイを止めた直後や ディスク高負荷時は、通知が drain(5 秒)に間に合わなかっただけで ファイル自体は揃っていることがある。inbox に残りが無いかも見る。
  3. 原本の HR1 要求コードがログの requestCode と一致することを確認する。 一致しないファイルは別 cycle の残りであり、この行の回収対象ではない。
  4. 既存の人による修復へ進む: reconciliation-required は retry 対象外で、 JWNET 画面で人が確認して DB を直す運用(Phase E-2.9)。受理済みの報告を 再送して二重報告にしない。確認キー・加入者番号はログに出ないため、 設定票で照合する。

edi-receive-partial の確認(人・wl-8mpz R2): ログ(C:\greco\gateway\logs\stderr.log)に edi-receive-partial (warn・submissionId 付き)が出たら、timeout / stop 後の bounded drain で確定した集合(abort 前に退避が 確定した分+drain 中に届いた遅い通知・resolve 値)を処理する。退避しきれなかった残りが C:\jwnet\inbox に残っている可能性がある(1 ファイルだけの受信でも全件退避後にこの warn が出て inbox が空の場合がある)。DB に載るかどうかはその後の原本保存・parse・HR1 相関・R5 の成否で決まり、 warn 自体は DB 取込の成功を意味しない。

  1. submissionId(= exchange 行の id)を起点に、該当行の要求コードと状態を exchange 自身から確認する。 管理者用 GET /jwnet/web-edi/test/exchanges/:id に id を指定して request_codestatus を読む (画面の一覧には id が表示されないため、id から行を特定する経路はこの API である。返すのは要求本文の 秘密を含まない非秘密列である)。得た要求コードで画面の一覧の該当行を確認する。結果原本の HR1 で 画面を検索しない — 原本は現在の id で保存した後に HR1 を照合するため、result-correlation-failed は HR1 が別の要求を指す場合、または結果ファイルが 1 件でない場合(同一 receive で 2 件以上届いた・HR1 が 読めない)に付き、raw-save-failed では原本自体が存在しない場合がある。行が 受信済 ならその 行の受信は完了であり、自動で受信試行へ戻る仕組みは無い(R7 は sending / sent だけを読む)。 行が 照合待ちresult-correlation-failed / result-parse-failed / raw-save-failed 等)なら後続の 保存・照合で落ちた分であり、残留確認と併せて下記の 4 分岐で切り分ける。そのうえで原本の HR1 を比較 する側の証拠として要求コードと比べる。一致なら通常の照合待ち処理へ進み、不一致なら HR1 が指す要求 コードの行を別途探して両方を判定者へ渡し、原本が無ければ inbox / received の残留と events の detail (sha256・エラーコード)を判定者へ渡す。HR1 が一致しているのに result-correlation-failed なら、 received / inbox に同じ要求コードの結果が複数件ないかを見て、件数と各ファイルの sha256 を判定者へ渡す。
  2. inbox の一覧を取り、残りの有無と件数を確認する。ファイルは原本のまま保全する (SHIFT_JIS・BOM なし・エディタで開いて再保存しない)。残留ファイルを消さない・移動しない。 センターは受信済みの結果を再送しないため、それが唯一の原本である。空の場合も「残り無し」と記録する (warn が出ても空は正常な場合である)。
  3. 残留がある場合、次の要求の送信を止めて先に処理する。R3 は sending / sent の行があるうちは 新しい行を渡さないが、部分受信で行が received(完了)になった後は次の要求を渡してしまう。 そのとき残留が次の receive で received へ消費されても、それは退避を意味するだけで DB 取込の成功では なく、HR1 が旧要求のコードなら新しい行が 照合待ちresult-correlation-failed)になり 1 サイクルを 焼く。キューが空の常駐ループの次の tick だけでは idle で終わり回収されないため、「次の poll で 消えるはず」とは判断しない。
  4. 残留の内容はコピーを Greco の「結果ファイルを判定」(parse-only・DB を変えない)で確認する。 判定入口は内容より先にファイル名を検査し、^([0-9A-Za-z]{9})-([^_]+)_([0-9]{14})\.csv$ の形式で なければ 422 で拒否するため、コピーの名前を形式に合わせて付け直す(原本は改名も移動もしない)。 422 になった場合は原本に触らず、ファイル名と HR1(読み取り専用で確認)を判定者へ渡す。 原本の HR1 要求コードがどの要求の結果か・E01 が何かを確かめる。併せて EDI 処理状況照会ページ (接続登録番号で検索)の 4 分岐で JWNET 側の状態を確認する(待機中 / 処理中 / 処理完了 / 該当の要求ファイルがない)。ローカル原本の判定と JWNET 側の確認は別の作業として両方行う。 ファイルの移動(inbox からの消失)と DB 取込の完了を同一視しない。
  5. 残留を見つけた操作者は、V6 で決めた当日体制の判定者へ上げる。渡す情報: 要求コード / 残留ファイル名 / HR1 の要求コード / 事象の時刻 / 該当 exchange 行の状態。判定者が JWNET 側の状態を EDI 処理状況照会で 確認し、必要なら JWNET 窓口(EDI 事業者サポート・平日 9:00〜17:00)へ照会する。ファイルの中身 そのものを外部へ送らない(加入者番号・EDI 利用確認キーが入りうる)。reconciliation-required の 発生を待たずに上げる。完了済み行が残留処理で自動で受信試行へ戻ることは無い。自動再送はしない (受理済みの報告を再送すると二重報告になる)。

結果が取れないときの切り分け(人): EDI 処理状況照会ページ(接続登録番号で検索)で 4 分岐を確認する: 待機中 / 処理中 / 処理完了(取れないならセンター側は送信済みの 可能性が高いので Biware の受信ログを見る)/ 該当の要求ファイルがない(=送信が失敗している。全銀レベルの失敗はこのページに載らない)

全銀レベルの処理結果コードの読み方: 終了情報ファイル(制御ファイルと同じフォルダの固定名 STATUS.BIZ。同時通信 OFF のとき。実パスは R-2 で確定)(固定長 128 桁)の 21〜22 桁目 = 制御電文の処理結果コード。エラーは 1/2-XX-23 / 1/2-XX-33 の形で出力される。出典 = 「プログラミング」6-3 終了情報ファイルのデータ形式 / 6.5.3 通信中のエラー、「クライアントアプリケーション」5 エラー終了の原因と対策 / 5.2 通信中エラー。全銀レベルの失敗は JWNET の EDI 処理状況照会に載らないので、このファイルが唯一の証跡(写しは認証値をマスク)。

停止・再起動時の行の扱い

  • sent(送信済・結果待ち)の行は停止で消えない。受信待ちの途中で止めても 照合待ちにせず、再起動後の R7 で受信を再開する(初回待機・残り試行は 送信時刻起点で再計算する)。例外として、受信予算(送信時刻から約 35 分)を 超えて「送信済・結果待ち」が続くときは、運用手順書 10 章の復旧手順 (d) に 従って再起動する。
  • sending(送信中)の行は送ったか不明のため、次回以降の R7 在庫確認 (再起動時は再起動後の最初の R7。再起動しなくても次のサイクルで遷移する)で 照合待ち(gateway-restarted-during-send)にする。再送はしない(受理済みの報告を 再送すると二重報告になる)。
  • command 受信の実行中に止めたときは、受信コマンドを kill せず完了まで 待ってから止まる(停止予算内に収める。予算不足は起動時に拒否する)。
  • 受信済み・未取込の原本は再起動後に自動で再適用される (<submission id>.received-*.edi が R5 成功後に applied になる)。 保存が間に合わなかった分だけ receivedDir から人が回収する。 receivedDir に残った未取込ファイルは従来どおり上の回収手順で目視回収する。

保険 — 手渡し(フォールバック)

ゲートウェイが unhealthy・claim が進まない・全銀レベルの切り分けが必要なときに使う。 主経路の手順 1・2(時刻・SA・疎通)と 8〜10(繰返し・記録・通過後)は共通で、 3〜7 を以下に置き換える。

  1. Greco の接続テスト画面で送り先に「ファイルをダウンロード」を選び、要求ファイルを 生成する(運用手順書 10 章)。要求コードは画面の採番を使う。
  2. 要求ファイルのウィルスチェックを行い、C:\jwnet\outbox原本のまま置く (SHIFT_JIS・BOM なし・エディタで開いて再保存しない)。
  3. Biware で送信し、送信結果ステータスが正常であることを確認する。異常は 全銀レベルの失敗で、JWNET の EDI 処理状況照会ページには載らない(仕様書 7-3)。 Biware のログを読んで直し、再送する。
  4. 送信完了から 5 分以上待つ(仕様書 §3-1 推奨フロー (2.3))。
  5. Biware で結果ファイルを新規要求で受信する (「再送要求」にすると結果ファイルが受信できなくなる・仕様書 §3-3(4) No.19・ §3-3(7) 4))。受信結果が正常でないときは受信ステータスを分析して原因を除き、 再受信する(§3-1 推奨フロー (2.5))。「ファイルなしレスポンス」なら 4 へ戻る。 取れないときは EDI 処理状況照会ページ(接続登録番号で検索)で 4 分岐を確認する: 待機中 / 処理中 / 処理完了(取れないならセンター側は送信済みの可能性が高いので Biware の受信ログを見る)/ 該当の要求ファイルがない(=送信が失敗している。全銀レベルの失敗はこのページに載らない)
  6. C:\jwnet\inbox の結果ファイルを原本のまま保全し、接続テスト画面の 「結果ファイル」で選んで「結果ファイルを判定」を押す。 HR1 → HR2 → 個票 → E01 の順に判定する (運用手順書 10 章「判定と E01 の読み方」)。E01 があれば入力を直して 別の要求コードで再送する。HR1 3(一部エラー)で受理された分は再送しない。
  7. TEST の手渡し: 送り先「ファイルをダウンロード」で TEST-<要求コード>.txt を ダウンロード → C:\jwnet\outbox → Biware 直送 → C:\jwnet\inbox の原本を目視 (フォーマット不正のエラーコードが正)。

照合待ちの復旧と受信周期の読み方(wl-t4ll・Q-T4/Q-T5)

既存の手順は変えず、照合待ちになった後の操作と周期の読み方を閉じる。

再稼働前チェック(主経路の手順 2 の前・再起動の前に毎回):

  1. C:\jwnet\outbox が空であること。残っていたら Biware が再稼働時に再送する 恐れがあるため、要求コードを控えて退避し、空にしてから再稼働する。
  2. C:\jwnet\inbox が空であること(主経路の手順 2 と同じ。残留があれば edi-receive-partial の確認手順へ進み、判定者へ上げる)。

照合待ちの復旧分岐(いずれも自動再送は無い。送り直すときは新しい要求コードで生成する):

  • (a) 結果ファイルが後から届いた / C:\jwnet\received(receivedDir)に残っていた: receivedDir の一覧を取り、ログや events の requestCode と突き合わせる。 原本は保持し(改名も移動もしない)、判定用のコピーだけを元の名前(退避時に 付いた先頭の日時・PID・連番を除いた部分)に付け直して、Greco の 「結果ファイルを判定」(アップロード)で HR1 → HR2 → 個票を読む(命名の 詳細は上の残留手順 4「コピーの名前を形式に合わせて付け直す」)。 判定者が受理の有無を記録する。行の状態は変えない(R6 は sending / sent からしか遷移できず、照合待ちの行を received にする 経路は無い。照合待ちは次の送信を阻止しない)。TEST は原本目視で記録し、 アップロードしない。
  • (b) 受信だけをやり直したい: ゲートウェイは R7 で sending / sent しか 再開しないので、照合待ちの行の受信は再実行できない。手動受信の前に ゲートウェイと排他する(照合待ちは R3 を阻止しないため、稼働中のまま手動 受信するとゲートウェイの受信サイクルが共有 inbox の結果を先に回収し、 別要求が result-correlation-failed になる。到着後に人が動かすだけでは 防げない)。
  • 同テナントに 待機中 の要求が無いことを画面で確認する(あれば 取り下げるか、受信が終わるまで待つ)。
  • 送信中 / 送信済・結果待ち が無いことを確認する。
  • ゲートウェイを停止する(v1.94.0 以降は停止契約で sent は保たれる。 それ以前は in-flight が無い状態で nssm stop)。
  • Biware の受信ジョブを手で起こす(command なら第 7 章のコマンド・ scheduler ならジョブの手動実行)。inbox に落ちたファイルを人が C:\jwnet\received\manual\ へ移す(manual フォルダが無ければ作る)。
  • nssm start で再開する。その後 (a) の手順で判定する。 代替: Biware の受信先をゲートウェイが監視しない一時フォルダに切り替えて 受信する。
  • (c) 確認済みの残留の保管: 日次記録の保管先へ移す。ファイル名は 要求コード_取得時刻(例: REQ001_20261005-090100・元の拡張子を保つ)にする。 (a)/(b) で判定・記録が終わったものだけを移し、未確認の残留は消さない・ 移動しない。
  • (d) 再開条件: sending / sent が無ければ次の送信は自動で進む(R3 の阻止は この 2 状態だけ)。sent が受信予算(約 35 分)超過で health が正常なら v1.94.0 は nssm restart で再開する(#831: 停止時も sent を保ち、R7 が 受信を再開する)。ゲートウェイが死んでいて sending のまま戻らないときだけ、 下記の service_role の R6 手順を使う。

service_role の R6(最終手段・判定者の承認が必要): 対象テナント・対象行・ reconciliation-required への遷移に限定する。判定者の承認を得て、当日体制で 定めた端末から Secret Manager の service-role キーで次の 1 文だけを実行する (表の直接 UPDATE / DELETE や無条件の再 enqueue はしない):

POST {SUPABASE_URL}/rest/v1/rpc/gateway_fail_jwnet_connection_test_exchange
apikey: <Secret Manager の service-role キー>
{
  "p_tenant_id": "<対象テナント UUID>",
  "p_id": "<対象行 id>",
  "p_to_status": "reconciliation-required",
  "p_error_code": "gateway-dead-manual",
  "p_error_message": "gateway-dead-manual"
}

打った理由・時刻・行 id・要求コードを日次記録に残す。sending / sent 以外の行には効かない(illegal transition で 42501)。

受信周期の読み方(Q-T5):

  • 「60 秒間隔」は各 receive 完了後の休止である。scheduler は 1 回の receive の 中で最大 120 秒待つため、空振りが続く間の開始間隔は約 180 秒になる。 command はコマンド所要時間 + 安定待ちの後に 60 秒休止する。
  • 主経路の手順 6 の「5 へ戻り」は「6 の受信試行へ戻る(最大 10 回)」と読み替える。 送信完了後の 5 分待ち(手順 5)には戻らない。
  • R-2 / V5 の証跡には、scheduler の受信ジョブの新規要求属性・起動条件・ 開始/終了時刻、file-moved の移動先(archive / error)を残す。command は 実 argv の新規要求設定の値を転記してから受け入れる。

費用と運用の注意

  • 概算 月 2 万円強: Windows VM e2-medium ~$27 + Windows ライセンス ~$58 + ディスク ~$5 + strongSwan VM e2-small ~$13 + static 外部 IP / NAT。Cloud VPN トンネル料金は不要(自前終端)。 評価・待機期間中は VM を停止すれば稼働時間課金(Windows ライセンス・VM)が浮く (コンソールの VM 一覧から停止/開始できる。ただし strongSwan VM は接続テスト中は起動)。
  • ディスクは日次スナップショットを設定する。
  • 接続テスト期間中は E-2.11 の外形監視(ルータ VM の timer → Slack)が担う。 ゲートウェイ統合後は /health を Cloud Monitoring の uptime check で監視する。
  • ゲートウェイのメンテカレンダーは fail-closed(未設定だと送信されない)。 本番切替時に必ず実値を設定する。

付録 A — Windows App(IAP TCP forwarding 経由の RDP)の接続手順と切り分け

Windows VM のデスクトップ操作は Windows App + IAP トンネルで行う(方針 2026-09-08)。 RDP をインターネットへ開けない・一時的な外部 IP や /32 の FW を作らない・Chrome リモート デスクトップは使わない。

  1. Mac の準備(初回のみ):
    1. App Store で「Windows App」(提供元 Microsoft・無料・旧称 Microsoft Remote Desktop)を入手する。
    2. gcloud CLI を入れる: brew install --cask google-cloud-sdkgcloud auth logingcloud config set project <プロジェクトID>
  2. GCP 側の前提(Cloud Shell): FW allow-iap-rdp35.235.240.0/20 → tcp:3389・target tag jwnet-biware)と VM のタグ jwnet-biware。無ければ Phase C 手順 3-1 のコマンドで作る(既存は再作成しない)。IAP の API(iap.googleapis.com)が有効で、接続する GCP アカウントに roles/iap.tunnelResourceAccessor があること。
  3. 接続(毎回): Mac のターミナルで

    gcloud compute start-iap-tunnel jwnet-gw-vm 3389 \
      --local-host-port=localhost:13389 --zone=asia-northeast1-b
    

    を実行したまま、Windows App の「PC の追加」で PC 名 localhost:13389、ユーザー名 jwnetadmin。 「証明書を検証できません」は「続行」。作業後はターミナルの Ctrl+C でトンネルを閉じる。 4. 切り分け: - Listening on port が出ない → IAP API の有効化・FW allow-iap-rdp・VM のタグ・自分の IAP 権限を順に確認する。 - Address already in use--local-host-port=localhost:13390 など別ポートにし、Windows App 側の PC 名も合わせる。 - Windows App がタイムアウト → トンネルのターミナルが生きているか(Mac のスリープで切れる)。VM が実行中か。 - パスワード不一致 → Cloud Shell で gcloud compute reset-windows-password jwnet-gw-vm --zone=asia-northeast1-b --user=jwnetadmin を再実行する(新しいパスワードは Cloud Shell 内に留め、転記しない)。 - 過去に一時的な外部 IP や /32 の FW(旧 allow-my-rdp・旧付録 A の tmp-bootstrap-rdp)を作った環境では、gcloud compute instances describe jwnet-gw-vm --format='value(networkInterfaces[0].accessConfigs)'gcloud compute firewall-rules list --filter='allowed[].ports:3389 AND NOT name=allow-iap-rdp' --format='value(name,sourceRanges)' で残っていないことを確認する。 3389 を公開網へ許可する FW が残っていれば NAT の有無に関係なく外し、外部 IP は Cloud NAT が確認できた場合だけ外す (strongSwan runbook §2 手順 5)。