nginx/apacheを介してクライアントの実際のIPを取得する方法

nginx/apacheをworkermanのプロキシとして使用すると、nginx/apacheは実際にはworkermanのクライアント役割を果たします。そのため、workermanで取得されるクライアントのIPはnginx/apacheサーバーのIPであり、実際のクライアントのIPではありません。クライアントの実際のIPを取得する方法は以下の通りです。

原理:

nginx/apacheは、クライアントの実際のIPをHTTPヘッダーとして渡します。例えば、nginxの設定の中でlocationに次のように追加します:

proxy_set_header X-Real-IP $remote_addr;

workermanはこのヘッダーの値を読み取り、$connectionオブジェクトに保存します(GatewayWorkerでは$_SESSION変数に保存できます)。使用する際には、変数を直接参照すればよいです。

注意:

以下の設定は、HTTP/HTTPSおよびWS/WSSプロトコルに適用されます。他のプロトコルでクライアントのIPを取得する方法は類似しており、プロキシサーバーがデータパケットにIPデータを挿入して実際のクライアントIPを透過させる必要があります。

nginxの設定は以下のようになります

server {
  listen 443;

  ssl on;
  ssl_certificate /etc/ssl/server.pem;
  ssl_certificate_key /etc/ssl/server.key;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

  location /wss
  {
    proxy_pass http://127.0.0.1:8282;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    # ここでHTTPヘッダーを利用して実際のクライアントIPを透過させます
    proxy_set_header X-Real-IP $remote_addr;
  }

  # location / {} サイトの他の設定...
}

workermanはnginxが設定したヘッダーからクライアントIPを読み取ります

<?php

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker('websocket://0.0.0.0:7272');

/**
 * クライアントのWebSocket接続時のコールバックonWebSocketConnect
 * onWebSocketConnectコールバック内でnginxからHTTPヘッダー中のX_REAL_IP値を取得します
 */
$worker->onWebSocketConnect = function(TcpConnection $connection, $request){
    /**
     * connectionオブジェクトにはrealIP属性が本来ありませんが、
     * ここでconnectionオブジェクトにダイナミックにrealIP属性を追加します。
     * PHPオブジェクトには動的に属性を追加できることを覚えておいてください。
     * お好きな属性名を使っても構いません。
     */
    //$connection->realIP = $_SERVER['HTTP_X_REAL_IP']; // workerman v4 での使用法
    $connection->realIP = $request->header('x-real-ip'); // workerman v5 での使用法
};
$worker->onMessage = function(TcpConnection $connection, $data)
{
    // クライアントの実際のIPを使用する場合は、$connection->realIPを直接使用します。
    $connection->send($connection->realIP);
};
Worker::runAll();

GatewayWorkerはnginxが設定したヘッダーからクライアントIPを取得します

Events.phpに次のコードを追加します:

class Events
{
   public static function onWebsocketConnect($client_id, $data)
   {    
        $_SESSION['realIP'] = $data['server']['HTTP_X_REAL_IP'];
   }
   // .... その他のコードは省略....
}

コードを追加した後はGatewayWorkerを再起動する必要があります。

こうすることで、Events.phpのonMessageおよびonCloseメソッドの中で$_SESSION['realIP']を介してクライアントの実際のIPを取得できるようになります。

注意:Events.phpのonWorkerStart onConnect onWorkerStopでは、直接$_SESSION['realIP']を使用することはできません。