วิธีรับหมายเลข IP จริงของผู้ใช้จาก nginx/apache ใน workerman

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

หลักการ:

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

หมายเลขโปรโทคอลที่ใช้:

การตั้งค่าต่อไปนี้ใช้สำหรับโปรโตคอล http/https ws/wss เท่านั้น สำหรับโปรโตคอลอื่นๆ การรับหมายเลข 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";
    # ส่วนนี้เป็นการส่งหมายเลข IP จริงของลูกค้าผ่าน header http
    proxy_set_header X-Real-IP $remote_addr;
  }

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

การอ่านหมายเลข 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');

// เมื่อลูกค้าเชื่อมต่อเข้ามา หลังเสร็จสิ้นการต่อ TCP 3 ครั้ง จะมีการเรียกใช้ callback ต่อไปนี้
$worker->onConnect = function(TcpConnection $connection) {
   /**
    * เมื่อทำการ handshake แบบ websocket กับลูกค้า สามารถได้รับค่า X_REAL_IP ที่ถูกส่งมาจาก nginx ผ่าน header http
    */
   $connection->onWebSocketConnect = function(TcpConnection $connection){
       /**
        * ออบเจค connection จริง ๆ ไม่มี property realIP ซึ่งในส่วนนี้จะให้ออบเจค connection เพิ่ม property realIP
        * จำไว้ว่าใน php สามารถเพิ่ม property ให้กับออบเจคได้ และสามารถตั้งชื่อ property ให้กับไอบเจคได้ตามต้องการ
        */
       $connection->realIP = $_SERVER['HTTP_X_REAL_IP'];
   };
};
$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 ใหม่

จากนี้จะสามารถใช้ $_SESSION['realIP'] ใน onMessage และ onClose ใน Events.php เพื่อประมาณหมายเลข IP จริงของผู้ใช้ได้

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