NAME Perlbal::Plugin::SessionAffinity - Sane session affinity (sticky sessions) for Perlbal VERSION version 0.001 SYNOPSIS LOAD SessionAffinity CREATE POOL backends POOL backends ADD 10.20.20.100 POOL backends ADD 10.20.20.101 POOL backends ADD 10.20.20.102 CREATE SERVICE balancer SET listen = 0.0.0.0:80 SET role = reverse_proxy SET pool = backends SET persist_client = on SET persist_backend = on SET verify_backend = on SET plugins = sessionaffinity ENABLE balancer DESCRIPTION Perlbal doesn't support session affinity (or otherwise known as "sticky sessions") out of the box. There is a plugin on CPAN called Perlbal::Plugin::StickySessions but there's a few problems with it. This plugin should be do a much better job. Go ahead and read why you should use this one and how it works. WHY YOU SHOULD USE IT Here is a list of problems with the other implementation of sticky sessions: * It only supports sticky sessions for files The hook it uses for adding cookies only applies to file fetching. This means that if you want sticky sessions for anything other than file fetching, you're fresh out of luck. :) However, this plugin uses (hopefully) the proper hook to accomplish sticky sessions on each and every request. * It requires patches It depends on patches the author has prepared (that were not integrated) into the Perlbal core distribution. This means you need to apply these patches on every new version of Perlbal you're installing. However, this plugin doesn't require any patches. * It's outdated At least one of the patches provided with it is simply unnecessary anymore because the hook it adds was already added to Perlbal (at a little different location, though). Clearly it hasn't been updated in a while. However, this plugin is up to date. * It's overkill/breakable/copy-pasta/ZOMGWTF?! It has a lot of code that is basically copy-pasted from some handling code in Perlbal itself. This means that any code that is changed, needs to be updated in the module (every, single, time) which will suddenly become incompatible with previous versions. It's a lot of code that mostly likely isn't necessary and instead of refactoring where needed, and submit that to Perlbal, it was simply copied over, which is more than horrible. However, this plugin is very thin and slim, contains no copy-pasted code and will not break future and previous versions every time Perlbal changes code. * Observed breakage After looking into this, it seems as though gentleness is necessary, since the connect-ahead doesn't seem to be cleaning up, and more and more closed sessions are mounted. However, this plugin does not use the method, keeping Perlbal itself in charge and which allows proper closing and releasing of connections. * Probable security risk It sets a cookie with a backend ID that correlates to the backend order in the pool. This means that you can simply run requests with different backend IDs and easily find the number of backends in a pool. You can also attack by a special-crafted cookie and measure the timings for requests to find when it probably does additional processing (since the backend is specified) to find how many backends exist in a given pool. I'll leave finding of more ways to exploit this security risk as an exercise to the reader. However, this plugin uses Digest::SHA with a randomly-created salt on each start-up (which the user can explicitly specify, if it seeks predictability) to keep the backend ID value in the cookie. By allowing you to change the cookie header name and the way the value is presented, it would be more difficult for an attacker to understand what the header represents, and how many backends exist (since there is no counter). * Limited features It does not provide the user with a way to control how the backend is picked. It only gets them using Perlbal. However, this plugin will give the user the ability to pick backends using either randomly, via an external class or others. HOW DOES IT WORK Basic stuff Basically, the module creates a SHA1 checksum for each backend node, and provides the user with a cookie request. If the user provides that cookie in return, it will try and find and provide the user with that specific node. If the node is no longer in the service's pool, or the cookie matches a node that doesn't exist, it will provide the user with a cookie again. Advanced stuff The plugin sets up dedicated pools and services for each service's node. This is required since Perlbal has no way of actually allowing you to specify the node a user will go to, only the service. Not to worry, this creation is done lazily so it saves as much memory as it can. In the future it might save even more. When a user comes in with a cookie of a node that exist in the service's pool it will create a pool for it (if one doesn't exist), and a matching service for it (if one doesn't exist) and then direct to user to it. The check against nodes and pools is done live and not against the static configuration file. This means that if you're playing some trickery on pools (changing them live), it will still work fine. A new service is created using configurations from the existing service. The more interesting details is that reuse is emphasized so no new sockets are created and instead this new service uses the already existing sockets (along with existing connections) instead of firing new ones. It doesn't open a new listening or anything like that. This also means your SSL connections work seamlessly. Yes, it's insanely cool, I know! :) ATTRIBUTES These are future attributes: affinity_cookie_header The name of the cookie header for the session. Default: X-SERVERID. affinity_salt The salt that is used to create the backend's SHA1 IDs. Default: the following code is run when you load Perlbal::Plugin::SessionAffinity to create the salt on start up: join q{}, map { $_ = rand 999; s/\.//; $_ } 1 .. 10; If you want predictability, you can override the salt. SUBROUTINES/METHODS register Registers our events. unregister Unregister our hooks and setters events. get_backend_id Get a backend ID number. This is currently simply sequential, but will be very dynamic in the near future. get_ip_port Parses a request's cookies and finds the specific cookie relating to session affinity and get the backend details via the ID in the cookie. find_backend_by_id Given a SHA1 ID, find the correct backend to which it belongs. DEPENDENCIES Perlbal Obviously. CGI::Cookies To parse and create cookies. Digest::SHA To provide a SHA1 checksum. Carp To provide croak. It's core, don't worry. SEE ALSO Perlbal::Plugin::StickySessions Try it and see why you would probably prefer this one. :) AUTHOR Sawyer X COPYRIGHT AND LICENSE This software is copyright (c) 2012 by Sawyer X. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.