PHP sessions using Redis instead of disk

Discussion in 'General' started by Gwyneth Llewelyn, Mar 7, 2022.

  1. Hi everybody,
    I've been so frustrated with the machine offered by my previous hosting provider that when I moved to the current one, for about the same price, I ordered a bare metal server with 64 GB of RAM — which is a lot for me, considering that the previous one had only 16 :)
    Anyway, since I don't have that many traffic/requests, I have a lot of memory to waste ;-) and one thing I'm trying out is to push whatever I can into Redis, memcached, and similar memory-based services.
    One thing I learned was that, surprisingly, PHP is not limited to storing session data on disk (usually set to the user's 'special' /var/www/clients/clientX/webY/tmp directory). Instead, it can use different backends, Redis being one of them (!).
    On Ubuntu/Debian, you can easily install Redis (and the necessary support for PHP) with:
    Code:
    sudo apt-get install redis php-redis
    Redis barely needs any configuration, unless you need to tweak it to deal with replications, clusters, serving it from different ports and/or interfaces (by default it binds to localhost only). php-redis also has quite sensible defaults.
    Now for connecting PHP Sessions with Redis: in essence, you need to change your php.ini with the following:
    Code:
    [Session]
    ; Handler used to store/retrieve data.
    ; https://php.net/session.save-handler
    ; session.save_handler = files
    session.save_handler = redis
    
    ; session.save_path = "/var/lib/php/sessions"
    session.save_path = "tcp://localhost:6379"
    Note that if you have some form of authentication, or wish to store sessions in any other database (Redis comes with a handful of separate databases; 16 I think, numbered from 0 to 15, default is 0), you need to append some extra parameters on the session.save_path (you can use this article as a guideline — I surely did! — but beware, it's a bit old & outdated, make sure you look at the comments as well).
    So far, so good. After reloading PHP with the configuration changes, it was time for doing some tests — and they failed under my nginx + php-fpm configuration. Why? Because, by default, the ISPConfig templates will override session.save_path, since it's a good practice not to share session data across different users which will have SSH access to your server. As such, ISPConfig will, as said, set session.save_path on the php-fpm pool configuration, thus overriding whatever you've got on your php.ini.
    Not good.
    Ok, for now, my simple tweak is just to make a copy of /usr/local/ispconfig/server/conf/php_fpm_pool.conf.master to /usr/local/ispconfig/server/conf-custom/php_fpm_pool.conf.master and remove the lines there that set the session.save_path (actually, I just prefixed them with a semicolon to flag them as a comment) — thus letting the defaults from php.ini be used instead; from now on, this means that 'my' template will work correctly when adding new websites (or changing old ones), and I can force a refresh of all existing websites, so that they use 'my' template instead.
    That's all very good and nice until, of course, ISPConfig gets an upgrade, and I have to remember to update the custom templates :) (I've already got a bunch of them that require constant manual upgrading...)
    However, while looking at that template, I've noticed that it checks if the flag custom_session_save_path is set or not. If it's not set to Y, well, then the session.save_path will not get overridden! That's exactly what I want!
    Big question: how do I set that variable?
    I've been through the whole source code, and it's anything but obvious. It seems that it's set from some sort of configuration file (or database table? I couldn't figure it out...), somewhere in php_fpm_pool_update() in file plugins-available/nginx_plugin.inc.php (remember, I'm using nginx, but the same reasoning will apply to Apache as well, under its own template), where, at some point, the code checks for the presence of custom php.ini settings. If there are any, and if these override session.save_path, then custom_session_save_path gets set to Y and will be processed later in the template. So far, so good: if I add a php.ini snippet for all my websites, overriding session.save_path with the required Redis connection setting, then, in theory, I wouldn't need to have a custom template for generating the php-fpm configuration; I could simply use the default one.
    Confused? Well, I am for sure. I've taken a look at the ISPConfig 3.1 manual, and it doesn't explicitly mention anything about custom_session_save_path. Therefore, as far as I can understand it, I can either configure it 'globally' — by changing the php-fpm configuration template — or 'individually', by manually changing each and every site that uses PHP.
    Unless I'm missing something — are these my only two options?
    I have to say that I'm not happy with either of them! That's why I was wondering if I haven't missed anything...
    Thanks in advance for any suggestions/comments; who knows, this might even become a HowtoForge tutorial at some point :)
    Cheers,

    - Gwyn
     
  2. Jesse Norell

    Jesse Norell ISPConfig Developer Staff Member ISPConfig Developer

    In a quick look at that plugin, a variable of the same name is set when processing the custom php.ini directives and the session.save_path key is found (and a few lines lower, it sets the template custom_session_save_path according to that variable); you should be able to just set session.save_path in the custom directives and it will work.

    One issue to consider in your setup is redis is listening on the loopback interface with no password in use by default - that means that all data stored in redis is available to any process which can connect to the redis tcp socket - and that is of course any of your websites which have php enabled or otherwise can execute scripts/code. I've not looked into what is available to address that, but redis does support unix sockets where you can apply filesystem permissions which might help (assuming it can be configured so the php daemon itself can connect to redis but your website php code cannot) and redis can use passwords (assuming the php daemon can be configured with the redis password, and the website php code cannot read that configuration file to obtain the password).
     
    Gwyneth Llewelyn likes this.
  3. Thanks for your reply, Jesse! :D

    (emphasis mine)
    Indeed... for each and every one of all websites... which I'll have to do manually... and not forget afterwards, when I create a new website, to properly update it!
    Obviously, I can add that to a php.ini snippet, but, again, I'd have to remember to select that snippet by default on all PHP-enabled websites (which are not all of them, in my case; I have several cases where nginx is just used as a reverse proxy for a Go application).
    I guess that's something for ISPConfig 4.0... having a way to add more default snippets to all websites of specific classes... but that also goes well beyond my original post :rolleyes:
    Anyway, thanks for pointing out your security concerns — you're quite right, for the sake of simplicity, I omitted any authentication for Redis, which, even for a server that is accessed only locally, is at least as bad as having a common /tmp for writing session files to disk...
    Aye, technically Redis can work with different authentication tokens, one for each website and/or client, whatever. But then there will be the issue of dealing with those tokens on the backoffice side of ISPConfig 3, since, again, it will mean having to make sure that each instance has its own session.save_path in the custom directives...
    Hmm... I guess all of this is more complex to manage than I thought... :confused:
     

Share This Page