4

I have 2 web servers running PHP and Nginx... And I have 1 Redis Server. I want the web servers to use the Redis database for user login data. This will allow me to scale out the web servers without forcing logged-in users to re-authenticate if/when they hit a new server.

Both PHP/Nginx servers are using Redis for session handling, and they both point to the same Redis database. When I visit either site in my browser it generates a session token and stores it in Redis (creating two Redis keys total).... But what I want is to create a new session when I visit Site #1, store the session in Redis, and have Site #2 recognize me when I visit because it queries Redis (so only one Redis key total).

All 3 of my servers are in AWS, in the same VPC. I'm using internal IP addresses for communication b/w the web servers and Redis (so there is some amount of trust b/w the machines).

I'm obviously missing something here, and it might be the load balancer (which I will implement). But I was attempting to develop a Redis-sharing design with the minimal number of components. Also, I'm not entirely sure the load balancer helps w/ this issue.

Here are the relevant portions of code:

PHP/Nginx Servers: (x2)

(1) /etc/php/7.2/fpm/php.ini

session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address

(2) /var/www/html/test.php

<?php 

ini_set('session.save_handler', 'redis'); // redundant; including for completeness
ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness

session_name('FOOBAR');
session_start();
echo nl2br(
    '<pre>' . session_save_path() . '</pre>' . PHP_EOL
);

echo nl2br(
    'Running PHP version: ' . phpversion() . PHP_EOL
);

if (!array_key_exists('visit', $_SESSION)) {
    $_SESSION['visit'] = 0;
}
$_SESSION['visit']++;

echo nl2br(
    'You have been here ' . $_SESSION['visit'] . ' times.'
);

Redis Server: (x1)

(3) /etc/redis/redis.conf

bind 0.0.0.0

When I hit the test.php page for both sites, they successfully generate and store their own session in the Redis database. And they correctly increment their respective session token... But I want these web servers to share the Redis session data b/w them, so they "remember" all logged-in users (even if it's the user's first time hitting a given web server).

Thanks!


EDIT: I also attempted to manually query Redis for logged-in user data. This yielded similar results to test.php above. Each website ended up creating and managing its own session token.

PHP/Nginx Servers: (x2)

(4) /var/www/html/redis-check.php

<?php

   $cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];

   // Connecting to Redis server on localhost 
   $redis = new Redis(); 
   $redis->connect('10.xxx.xx.xxx', 6379); 
   echo "Connection to server sucessfully";
   echo '<br />';

   // Check if cookie value exists in Redis 
   $seenuser = $redis->get($cookie);

   if( !empty($seenuser) )
   {
      echo 'Hello, old friend!<br />';
      echo '<br />';
      echo 'Cookie Value: ' . $cookie;
      echo '<br />';
      echo 'Session Value: ' . $seenuser;
   }
   else
   {
      // This is a new user, so create a new session
      ini_set('session.save_handler', 'redis'); // redundant; including for completeness
      ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness
      session_name('FOOBAR');
      session_start();

      if (!array_key_exists('visit', $_SESSION))
      {
          $_SESSION['visit'] = 0;
      }
      $_SESSION['visit']++;

      $cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];
      $seenuser = $redis->get($cookie);

      echo 'New Login<br />';
      echo '<br />';
      echo 'Cookie Value: ' . $cookie;
      echo '<br />';
      echo 'Session Value: ' . $seenuser;
   }
bjones1831
  • 285
  • 5
  • 13
  • 1
    Perhaps https://stackoverflow.com/a/4428094/1213708 may give some hints (not something I've tried though) – Nigel Ren Mar 29 '19 at 07:11
  • 2
    [This will be helpful for you](https://stackoverflow.com/questions/342378/cross-domain-login-how-to-login-a-user-automatically-when-transferred-from-one#answer-342498) – Saad Suri Mar 29 '19 at 07:17
  • Thank you both! I will definitely check these out. – bjones1831 Mar 29 '19 at 07:22
  • 2
    One thing could be worth trying out is to log into the redis server and query the keys being created. This can show if a prefix is being used (it's also good to learn out how to check cache data as well). – Nigel Ren Mar 29 '19 at 07:26
  • Yeah, I had the same exact thought. It looks like the prefix is "PHPREDIS_SESSION:" (no quotes). And I was able to run GET commands using the values from that redis-check.php page. But again, there would be two Redis keys, one for each site. – bjones1831 Mar 29 '19 at 07:33

1 Answers1

1

Looks like my issue was due to missing a load balancer. I put HAProxy in front of my two web servers, pointed to HAProxy's address in the broswer, and the web servers immediately began sharing the session data stored in Redis. I guess they're both using the HAProxy address. And my browser (Chrome) shows only a single cookie for the HAProxy address (which is what I want)... When I bypass HAProxy and point to the web servers directly in the browser they generate their own session token (which is bad for this scenario).

So here are the relevant code sections (you can disregard what's in my OP):

PHP/Nginx Servers: (x2)

(1) /etc/php/7.2/fpm/php.ini

session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address

(2) /var/www/html/test.php

<?php

session_name('FOOBAR');
session_start();

echo 'IP Address: ' . getHostByName(getHostName());
echo '<br />';
echo '*******************************************************************************';
echo '<br />';

if (!array_key_exists('visit', $_SESSION))
{

    $_SESSION['visit'] = 0;

    echo 'Hello, new friend!';
    echo '<br />';
}
else
{

    echo 'Glad you are still with us!!';
    echo '<br />';
}

$_SESSION['visit']++;

echo 'You have been here ' . $_SESSION['visit'] . ' times.';

Redis Server: (x1)

(3) /etc/redis/redis.conf

bind 0.0.0.0

HA Server: (x1)

(4) /etc/haproxy/haproxy.cfg

frontend proxy_in
    bind *:80
    default_backend web_farm

backend web_farm
    balance roundrobin
    mode http
    server web1 10.xxx.xx.111:80 check  # web server 1
    server web2 10.xxx.xx.222:80 check  # web server 2

PHP/Nginx Servers: (x2)

/var/www/html/redis-check.php

Ignore this file. The revised test.php file is all that is needed.

Nigel and Saad, thank you for the links! It's always good to get a different POV on the problem.

bjones1831
  • 285
  • 5
  • 13