ผ่าน nginx/apache proxy จะใช้วิธีไหนในการดึง IP จริงของลูกค้า?

การใช้งาน nginx/apache เป็น proxy สำหรับ workerman นั้น nginx/apache จะทำหน้าที่เป็น client ของ workerman ดังนั้น IP ที่ได้จาก workerman จะเป็น IP ของเซิร์ฟเวอร์ nginx/apache ไม่ใช่ IP ที่แท้จริงของลูกค้า วิธีการในการดึง IP จริงของลูกค้าสามารถดูได้จากวิธีการด้านล่าง

หลักการ:

nginx/apache จะส่ง IP จริงของลูกค้าผ่าน http header เข้ามา เช่น ในการตั้งค่า nginx ให้เพิ่ม proxy_set_header X-Real-IP $remote_addr; ลงใน location ที่เราต้องการ จากนั้น workerman จะอ่านค่า header นี้และบันทึกลงใน $connection object (GatewayWorker สามารถบันทึกลงในตัวแปร $_SESSION ได้) และสามารถเข้าถึงค่าได้โดยการอ่านตัวแปรนั้น

หมายเหตุ:

การตั้งค่าเหล่านี้ใช้ได้กับ protocol http/https ws/wss วิธีการดึง IP ของลูกค้าใน protocol อื่นๆ จะคล้ายกัน โดย proxy server จะต้องใส่ข้อมูล IP ลงใน packet เพื่อส่งต่อ 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 header เพื่อส่งต่อ IP จริงของลูกค้า
    proxy_set_header X-Real-IP $remote_addr;
  }

  # location / {} การตั้งค่าอื่น ๆ ของไซต์...
}

workerman อ่าน IP ของลูกค้าจาก header ที่ตั้งค่าใน nginx

<?php

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

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

/**
 * callback onWebSocketConnect ขณะลูกค้า handshake websocket
 * ใน callback onWebSocketConnect นี้จะได้รับค่า X_REAL_IP ผ่าน http header ที่ส่งมาจาก nginx
 */
$worker->onWebSocketConnect = function(TcpConnection $connection, $request){
    /**
     * ใน object ของ connection ไม่มี property realIP ดังนั้นจะต้องเพิ่ม property realIP ให้กับ connection object
     * จำไว้ว่าใน php สามารถเพิ่ม property ลงใน object ได้ตามต้องการ
     */
    //$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 ดึง IP ของลูกค้าจาก header ที่ตั้งค่าใน nginx

เพิ่มโค้ดด้านล่างใน Events.php

class Events
{
   public static function onWebsocketConnect($client_id, $data)
   {    
        $_SESSION['realIP'] = $data['server']['HTTP_X_REAL_IP'];
   }
   // .... โค้ดอื่น ๆ ถูกตัดออก....
}

หลังจากเพิ่มโค้ดแล้วต้องรีสตาร์ท GatewayWorker

เช่นนั้นคุณจะสามารถได้รับ IP จริงของลูกค้าใน method onMessage และ onClose ใน Events.php ผ่าน $_SESSION['realIP'] ได้

หมายเหตุ: ใน Events.php method onWorkerStart, onConnect, onWorkerStop ไม่สามารถใช้ $_SESSION['realIP'] ได้โดยตรง