Laravel + Redis Cache via SSL?
Asked Answered
C

4

14

I am trying to connect to Redis with predis 1.1 and SSL, using information https://github.com/nrk/predis, where in the example the following configuration is used:

// Named array of connection parameters:
$client = new Predis\Client([
  'scheme' => 'tls',
  'ssl'    => ['cafile' => 'private.pem', 'verify_peer' => true],
]);

My Laravel configuration looks like below:

'redis' => [
        'client' => 'predis',
        'cluster' => env('REDIS_CLUSTER', false),

        'default' => [
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ],

        'options' => [
            'cluster' => 'redis',
            'parameters' => ['password' => env('REDIS_PASSWORD', null)],
            'scheme' => 'tls',
        ],
    ],

Unfortunately I am getting the following error:

ConnectionException in AbstractConnection.php line 155:
Error while reading line from the server. [tcp://MY_REDIS_SERVER_URL:6380]

Suggestions are appreciated :)

Compositor answered 20/1, 2017 at 11:35 Comment(5)
Please post your .env file. The error is where it says MY_REDIS_SERVER_URL in your .envKimberleekimberley
MY_REDIS_SERVER_URL is simply URL of the Redis server. I can reach it through the port 6379, but not via 6380. Both should be accessible (this is Azure Redis Cache, with 6380 opened by default).Compositor
Did you find a solution for this?Meimeibers
Unfortunately not yet, any suggestions? :)Compositor
Same here, I just can't make it work with SSLMeimeibers
L
35

I was able to get it to work!

You need to move 'scheme' from 'options' to 'default':

My working config:

'redis' => [
    'client' => 'predis',
    'cluster' => env('REDIS_CLUSTER', false),

    'default' => [
        'scheme' => 'tls',
        'host' => env('REDIS_HOST', 'localhost'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => 0,
    ],

    'options' => [
        'parameters' => ['password' => env('REDIS_PASSWORD', null)],
    ],
],

Note: I had also removed the 'cluster' option from 'options', but I don't suspect this to be the make-or-break with this problem.

In my final-final config, I changed it to: 'scheme' => env('REDIS_SCHEME', 'tcp'), and then defined REDIS_SCHEME=tls in my env file instead.

Tested with AWS ElastiCache with TLS enabled.

Edit: The above config only works with single-node redis. If you happen to enable clustering and TLS then you'll need a different config entirely.

'redis' => [
        'client' => 'predis',
        'cluster' => env('REDIS_CLUSTER', false),

        // Note! for single redis nodes, the default is defined here.
        // keeping it here for clusters will actually prevent the cluster config
        // from being used, it'll assume single node only.
        //'default' => [
        //    ...
        //],

        // #pro-tip, you can use the Cluster config even for single instances!
        'clusters' => [
            'default' => [
                [
                    'scheme'   => env('REDIS_SCHEME', 'tcp'),
                    'host'     => env('REDIS_HOST', 'localhost'),
                    'password' => env('REDIS_PASSWORD', null),
                    'port'     => env('REDIS_PORT', 6379),
                    'database' => env('REDIS_DATABASE', 0),
                ],
            ],
            'options' => [ // Clustering specific options
                'cluster' => 'redis', // This tells Redis Client lib to follow redirects (from cluster)
            ]
        ],
        'options' => [
            'parameters' => [ // Parameters provide defaults for the Connection Factory
                'password' => env('REDIS_PASSWORD', null), // Redirects need PW for the other nodes
                'scheme'   => env('REDIS_SCHEME', 'tcp'),  // Redirects also must match scheme
            ],
        ]
    ]

Explaining the above:

  • 'client' => 'predis': This specifies the PHP Library Redis driver to use (predis).
  • 'cluster' => 'redis': This tells Predis to assume server-side clustering. Which just means "follow redirects" (e.g. -MOVED responses). When running with a cluster, a node will respond with a -MOVED to the node that you must ask for a specific key.
  • If you don't have this enabled with Redis Clusters, Laravel will throw a -MOVED exception 1/n times, n being the number of nodes in Redis cluster (it'll get lucky and ask the right node every once in awhile)
  • 'clusters' => [...] : Specifies a list of nodes, but setting just a 'default' and pointing it to the AWS 'Configuration endpoint' will let it find any/all other nodes dynamically (recommended for Elasticache, because you don't know when nodes are comin' or goin').
  • 'options': For Laravel, can be specified at the top-level, cluster-level, and node option. (they get combined in Illuminate before being passed off to Predis)
  • 'parameters': These 'override' the default connection settings/assumptions that Predis uses for new connections. Since we set them explicitly for the 'default' connection, these aren't used. But for a cluster setup, they are critical. A 'master' node may send back a redirect (-MOVED) and unless the parameters are set for password and scheme it'll assume defaults, and that new connection to the new node will fail.
Lehmann answered 20/2, 2018 at 0:57 Comment(3)
Excellent breakdown! Somebody correct me if I'm wrong but after looking through the Laravel 5.5 source code, the top level 'cluster' => env('REDIS_CLUSTER', false), does nothing. To use clustering, you just have to have a top-level clusters array and, like @Lehmann mentions, remove the top-level default connection config. If Laravel finds a top-level key with the name of your connection (default by default), it won't look for the connection in the clusters config. github.com/laravel/framework/blob/5.5/src/Illuminate/Redis/…Venter
Also per-cluster options that @Lehmann mentions requires you to unintuitively mix in an associative options value into your non-associative array of nodes, e.g.: 'redis' => [ 'clusters' => [ 'default' => [ 'options' => [ 'cluster' => 'redis' ], [ 'scheme' => env('REDIS_SCHEME', 'tcp'), /* ... */ ], ], ], ]Venter
A gist with better formatting and more info related to my previous comment.Venter
Z
4

Thank you CenterOrbit!!

I can confirm the first solution does allow Laravel to connect to a Redis server over TLS. Tested with Redis 3.2.6 on AWS ElastiCache with TLS, configured as single node and single shard.

I can also confirm the second solution does allow Laravel to connect to a Redis Cluster over TLS. Tested with Redis 3.2.6 on AWS ElastiCache with TLS, configured with "Cluster Mode Enabled", 1 shard, 1 replica per shard.

I was receiving the following error when I first tried to implement the cluster solution:

Error: Unsupported operand types

I missed the additional set of array brackets when I moved the "default" settings into the "clusters" array.

INCORRECT

'clusters' => [
  'default' => [
    'scheme' ...
  ]
]

CORRECT

'clusters' => [
  'default' => [
    [
      'scheme' ...
    ]
  ]
]

I hope this saves someone else a bit of troubleshooting time.

Ziwot answered 14/5, 2018 at 0:59 Comment(0)
R
0

The accepted solution by CenterOrbit worked for me, as I was using AWS I had to add tls:// in my .env Laravel

Rendezvous answered 18/5, 2022 at 13:39 Comment(0)
I
0

tls://username:password@URL:PORT?database=0 Try it. It will work

Incense answered 11/10, 2022 at 8:16 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Crossland

© 2022 - 2024 — McMap. All rights reserved.