管理された端末で業務アプリが失敗すると、誰かがVPNのトグルを確認し、オンになっているのを見て、経路は正常だと宣言する。これらの診断のほとんどはそこで止まるが、それは早すぎる。VPNトランスポートが有効でも、このアプリのリクエストがそれを使ったとは言えず、実際に適用されるプロキシはアプリ単位であったり、設定に表示されずPACファイルで選ばれたりする可能性がある。デバッグしている特定のアプリとプロファイルから始め、次にプラットフォーム自身の経路の見方を読む。

アプリとそのプロファイルから始める

業務バッジの付いたアプリは通常、独自のネットワークポリシーを持つ別のプロファイルで動作する。同じ端末の個人用ブラウザはその証拠にならず、管理方法が異なる同僚の端末の結果も証拠にならない。何かを読む前に、そのアプリがどのプロファイルに属するかを確認する。

設定で構成済みのVPNエントリを一覧にし、それぞれの所有者を確認する。Samsungのビルドではメニューのラベルがリリースごとに変わるため、エントリへの経路ではなくエントリ自体を説明する。グレー表示や管理対象のエントリは端末を管理する人のものであり、実験としてオフにすると測定したいものが変わり、ポリシーに反する可能性がある。

プラットフォームが報告する状態を読む

サポートツールの既存のアクティビティ内で、API 23以降なら一バイトも送らずにネットワーク状態を読める。この呼び出しには、機能を読むための通常の ACCESS_NETWORK_STATE マニフェスト権限が必要だ。

val cm = getSystemService(android.net.ConnectivityManager::class.java) ?: return
val network = cm.activeNetwork
val caps = network?.let { cm.getNetworkCapabilities(it) }
val hasVpn = caps?.hasTransport(android.net.NetworkCapabilities.TRANSPORT_VPN)
val proxy = cm.defaultProxy

それぞれの値は狭い問いに答える。アクティブネットワークは、その時点で呼び出し元プロセスが使うデフォルトネットワークだ。機能チェックは、そのデフォルトネットワークがVPN対応かどうかを教える。プロキシ呼び出しは有効なデフォルトHTTPプロキシを報告し、返されるオブジェクトは静的プロキシかPACスクリプトのどちらかを表すことがある。

デフォルトではなくネットワーク単位の見方が必要な場合は、同じネットワークのリンクプロパティを読み、cm.getLinkProperties(network)?.httpProxy と比較する。デバイス管理によってインストールされたプロキシはデフォルトプロキシ呼び出しに現れ、リンクプロパティには現れない。これはポリシーが与えたプロキシとネットワークが与えたプロキシを素早く見分ける方法だ。

出力は公開レポートではなくサポートチャネルに保管する。プロキシのホスト名、PACのURL、内部サフィックスは組織がトラフィックをどう経路付けするかを明かすもので、障害を説明するのにどれも必要ない。

各フィールドを狭く解釈する

ネットワークがnull、または機能オブジェクトがnullというのは、呼び出し元のネットワーク状態が取得できなかったか、読み取り中に変わったという意味だ。VPNがないという意味ではない。不明として扱い、アプリが前面に出た後に読み直すべきで、否定的な結果として扱ってはいけない。

VPNトランスポートは肯定的な指標の中で最も弱い。VpnService を持つアプリなら、あなたのトラフィックをどこにも送らないローカルなフィルタアプリであっても、どれでもこれを生成する。したがってtrueという値は、VPN対応のデフォルトネットワークが存在することを示すだけで、組織の出口が使われたことも、まして検査が行われたことも意味しない。

デフォルトプロキシがnullというのは、このAPIが有効な構成に対してデフォルトHTTPプロキシを報告しないという意味だ。除外できることはごくわずかだ。アプリは独自のSOCKS設定を持てるし、独自のHTTPクライアントを実装できるし、端末が決して知らないゲートウェイのプロキシに誘導されることもある。

単一の値では答えられないのがPACの場合だ。スクリプトは一方の宛先をプロキシ経由にし、別の宛先を直接送ることができるので、同じアプリの一秒以内の二つのリクエストが違う経路を通ることがある。また、ソケットを選んだネットワークに束縛するアプリや bindProcessToNetwork を呼ぶアプリは、デフォルトに従う義務がない。

このスナップショットが教えてくれないこと

これは履歴ではない。失敗したリクエストがどの経路を通ったかは何も記録しておらず、失敗後の成功した読み取りは失敗についての証拠にならない。目録でもない。得られるのは呼び出し元プロセスのデフォルトネットワークであり、各アプリのプライベート接続の一覧ではない。

ソケットより上で何が起きるかについても何も言わない。管理された端末で失敗するTLSハンドシェイクは、トラストストアの問題、ピンニングの不一致、プロキシが拒否する名前のいずれかかもしれず、どれも接続性の読み取りには現れない。

だからこそ両半分が揃って意味を持つ:スナップショットは見る場所を絞り、アプリ自身の診断が実際に何が失敗したかを語る。

管理者が使える形で報告する

影響を受けたアプリ、プロファイル、タイムスタンプ、失敗した正確な段階を示す。名前解決、TCP接続、TLSハンドシェイク、HTTPステータスのいずれかだ。スナップショットの値は読んだとおりに加え、nullは「なし」ではなくnullと書く。同じプロファイルの他のアプリも同じように失敗するかを記し、それがアプリの問題とプロファイル全体の経路の問題を切り分ける。

そして止まり、経路の所有者に応答を委ねる。企業証明書、プロキシのアドレス、VPNエントリを、管理を外す許可や承認された経路を迂回する許可と受け取ってはいけない。それはサポート案件をポリシーインシデントに変えてしまう。

デフォルトプロキシの契約は短く、一度読む価値がある:呼び出しがどのプロキシを報告するかを正確に定めており、このスナップショットを過大に読むすべての解釈は、もっと多くを報告しているという仮定から始まる。

関連記事