フックスクリプトで $_ENV 配列が空だと、OpenVPN が何も渡さなかった証拠に見えます。たいていはまったく別のことの証拠で、PHP がそのスーパーグローバルを埋めない設定になっており、値はずっとプロセスの環境にあったのです。二つ目の誤った結論はより微妙で、接続フックの二つのアドレス変数は別のものを指すのに、互いの代わりに使われます。どちらの誤りも、間違ったほうから始めると半日を費やします。

二つのアドレス、二つの異なる主張

外側の端点はクライアントの接続時にサーバーが観測したもので、IPv4 では trusted_ip、IPv6 では trusted_ip6 として利用できます。仮想アドレスはプールがこのセッションに割り当てたもので、ifconfig_pool_remote_ip として利用できます。

両者は異なる問いに答えるもので、記録には通常どちらか一方だけが必要です。一つ目は接続がどこから来たかで、何百人もが使う共有 NAT アドレスのこともあります。二つ目はこのセッションがトンネル内のどこに住むかで、後から別のセッションに再割り当てできます。両者を混同すると、もっともらしく見えて間違った記録ができます。

最小限の読み取りは次のようになります。

$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner  = getenv('ifconfig_pool_remote_ip');
$outer  = $outer4 !== false ? $outer4 : $outer6;

どちらの値も個人を特定しません。一方はネットワークの観測、他方はリースであり、どちらかを安定した端末識別子として扱う記録は、クライアントが別のネットワークから再接続するか別のプールアドレスを受け取った最初の時点で誤りになります。記録が答えるべき問いでフィールドを選び、その問いを列の隣に書き留めてください。

PHP の起動方法を確認し、あなたのシェルは見ない

フックは VPN デーモンの子として、デーモンが構築した環境で動きます。対話シェルの環境は別物で、スクリプトを手で実行するとフックではなくシェルの環境を試すことになります。

まず、フックが想定した段階で本当に発火したかを確認します。誤った段階に紐づいたスクリプトや、ディレクティブが別のパスを指しているために一度も動かなかったスクリプトは、変数が欠けているときとまったく同じ症状を出します。スクリプトの先頭に意図的なマーカー行を追加し、他の何かを調べる前にそれが現れることを確かめてください。タイムスタンプ付きの一行が、一度も動かなかった場合と値なしで動いた場合を分けます。

次に、その起動がどのインタプリタとどの設定ファイルを使うのかを確認します。Web サーバー向けの PHP 設定を編集しても、OpenVPN が起動した CLI プロセスには何の影響もなく、両者はまったく別の php.ini ファイルを読み込めます。

空の $_ENV が何も証明しない理由

PHP の getenv リファレンス はプロセスの環境を直接読む方法を記述しており、名前がないときは false を返します。スーパーグローバル $_ENV は別に埋められ、どのスーパーグローバルを埋めるかはディレクティブ variables_order が決めます。

そのディレクティブが E を省くと、環境に何が入っていても $_ENV は空のままです。それを参照するコードは、実際には存在する変数を欠けていると報告し、答えが php.ini にあるのに調査は OpenVPN へ向かいます。ディレクティブは、Web サーバーではなく CLI 設定について確認してください。

getenv で読めばこの種の混乱を丸ごと避けられますが、インタプリタを呼ぶ前に環境を消すラッパースクリプトは、別の形で同じ混乱を呼び戻します。

行の更新ではなく関連を保存する

書き込む前に、記録に必要な値を決めます。目的がセッションの出所を記すことなら、外側の端点を保存し、観測としてラベル付けします。接続の間だけセッションをトンネルアドレスに対応づけるのが目的なら、プールアドレスを保存し、セッションに結び付けます。

プールアドレスは再利用され、遅れて届いた切断イベントが新しい接続のデータを上書きしうるので、トンネルアドレスを共有するすべての行ではなく、認証された身元と現在のセッションで更新します。IPv4 の値だけを黙って書くのではなく、IPv6 を明示的に扱ってください。外側アドレスが NULL の行は、前者が目に見えて不完全であるため、誤った値の行よりましです。

書き込みは OpenVPN が提供するセッション識別子に結び付け、再接続が古い記録の編集ではなく新しい記録を作るようにします。パラメータ化された文と、操作に必要な権限だけを持つアカウントを使ってください。フックはデーモンの権限で動くため、不注意なクエリは誤ったログ行より広い問題になります。

環境を捨てずに診断する

どの名前が存在したか、どの段階が動いたか、どのインタプリタが使われたかを記録します。フックのプロセスがログファイルに入るべきでない無関係な値を継承することがあるため、環境全体を出力しないでください。

一つの接続を端から端まで検証するには、外側の端点を読み、割り当てられたアドレスを読み、そのセッションについてサーバー自身が見ている内容と突き合わせて確認します。一つのクライアントで組が確認できれば、その関連は他所でも信頼できます。

関連記事