Skip to content

Commit

Permalink
RedisCluster auth
Browse files Browse the repository at this point in the history
  • Loading branch information
yatsukhnenko authored and Pavel Yatsukhnenko committed Jan 17, 2019
1 parent e145f85 commit c5994f2
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 25 deletions.
7 changes: 6 additions & 1 deletion cluster.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ $obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5)
// persistent connections to each node.
$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true);

// Connect with cluster using password.
$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password");

</pre>

#### Loading a cluster configuration by name
Expand All @@ -29,6 +32,7 @@ In order to load a named array, one must first define the seed nodes in redis.in
redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001"
redis.clusters.timeout = "mycluster=5"
redis.clusters.read_timeout = "mycluster=10"
redis.clusters.auth = "mycluster=password"
</pre>

Then, this cluster can be loaded by doing the following
Expand Down Expand Up @@ -161,7 +165,7 @@ To do this, you must configure your `session.save_handler` and `session.save_pat

~~~
session.save_handler = rediscluster
session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1"
session.save_path = "seed[]=host1:port1&seed[]=host2:port2&seed[]=hostN:portN&timeout=2&read_timeout=2&failover=error&persistent=1&auth=password"
~~~

### session.session_handler
Expand All @@ -175,5 +179,6 @@ The save path for cluster based session storage takes the form of a PHP GET requ
* _persistent_: Tells phpredis whether persistent connections should be used.
* _distribute_: phpredis will randomly distribute session reads between masters and any attached slaves (load balancing).
* _failover (string)_: How phpredis should distribute session reads between master and slave nodes.
* _auth (string, empty by default)_: The password used to authenticate with the server prior to sending commands.
* * _none_ : phpredis will only communicate with master nodes
* * _error_: phpredis will communicate with master nodes unless one failes, in which case an attempt will be made to read session information from a slave.
31 changes: 25 additions & 6 deletions cluster_library.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,17 @@ cluster_read_sock_resp(RedisSock *redis_sock, REDIS_REPLY_TYPE type,
return r;
}

/* Helper to open connection and send AUTH if necessary */
static zend_always_inline int
cluster_sock_open(RedisSock *redis_sock TSRMLS_DC)
{
zend_bool need_auth = (redis_sock->auth && redis_sock->status != REDIS_SOCK_STATUS_CONNECTED);
if (!redis_sock_server_open(redis_sock TSRMLS_CC) && (!need_auth || !redis_sock_auth(redis_sock TSRMLS_CC))) {
return SUCCESS;
}
return FAILURE;
}

/*
* Helpers to send various 'control type commands to a specific node, e.g.
* MULTI, ASKING, READONLY, READWRITE, etc
Expand Down Expand Up @@ -654,6 +665,10 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
node->sock = redis_sock_create(host, host_len, port, c->timeout,
c->read_timeout, c->persistent, NULL, 0);

if (c->auth) {
node->sock->auth = zend_string_copy(c->auth);
}

return node;
}

Expand Down Expand Up @@ -824,6 +839,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
c->read_timeout = read_timeout;
c->failover = failover;
c->persistent = persistent;
c->auth = NULL;
c->err = NULL;

/* Set up our waitms based on timeout */
Expand Down Expand Up @@ -858,6 +874,9 @@ cluster_free(redisCluster *c, int free_ctx TSRMLS_DC)
efree(c->seeds);
efree(c->nodes);

/* Free auth info we've got */
if (c->auth) zend_string_release(c->auth);

/* Free any error we've got */
if (c->err) zend_string_release(c->err);

Expand Down Expand Up @@ -925,6 +944,11 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {
(unsigned short)atoi(psep+1), cluster->timeout,
cluster->read_timeout, cluster->persistent, NULL, 0);

// Set auth information if specified
if (cluster->auth) {
redis_sock->auth = zend_string_copy(cluster->auth);
}

// Index this seed by host/port
key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(redis_sock->host),
redis_sock->port);
Expand All @@ -948,7 +972,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) {
// Iterate over seeds until we can get slots
ZEND_HASH_FOREACH_PTR(c->seeds, seed) {
// Attempt to connect to this seed node
if (seed == NULL || redis_sock_connect(seed TSRMLS_CC) != 0) {
if (seed == NULL || cluster_sock_open(seed TSRMLS_CC) != 0) {
continue;
}

Expand Down Expand Up @@ -1119,11 +1143,6 @@ static int cluster_dist_write(redisCluster *c, const char *cmd, size_t sz,
redis_sock = cluster_slot_sock(c, c->cmd_slot, nodes[i]);
if (!redis_sock) continue;

/* Connect to this node if we haven't already */
if(redis_sock_server_open(redis_sock TSRMLS_CC)) {
continue;
}

/* If we're not on the master, attempt to send the READONLY command to
* this slave, and skip it if that fails */
if (nodes[i] == 0 || redis_sock->readonly ||
Expand Down
4 changes: 3 additions & 1 deletion cluster_library.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

/* Protected sending of data down the wire to a RedisSock->stream */
#define CLUSTER_SEND_PAYLOAD(sock, buf, len) \
(sock && !redis_sock_server_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \
(sock && !cluster_sock_open(sock TSRMLS_CC) && sock->stream && !redis_check_eof(sock, 1 TSRMLS_CC) && \
php_stream_write(sock->stream, buf, len)==len)

/* Macro to read our reply type character */
Expand Down Expand Up @@ -170,6 +170,8 @@ typedef struct redisCluster {
zend_object std;
#endif

zend_string *auth;

/* Timeout and read timeout (for normal operations) */
double timeout;
double read_timeout;
Expand Down
2 changes: 2 additions & 0 deletions common.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ zend_string_realloc(zend_string *s, size_t len, int persistent)
return zstr;
}

#define zend_string_copy(s) zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), 0)

#define zend_string_equal_val(s1, s2) !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1))
#define zend_string_equal_content(s1, s2) (ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2))
#define zend_string_equals(s1, s2) (s1 == s2 || zend_string_equal_content(s1, s2))
Expand Down
6 changes: 4 additions & 2 deletions library.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ static int reselect_db(RedisSock *redis_sock TSRMLS_DC) {
}

/* Helper to resend AUTH <password> in the case of a reconnect */
static int resend_auth(RedisSock *redis_sock TSRMLS_DC) {
PHP_REDIS_API int
redis_sock_auth(RedisSock *redis_sock TSRMLS_DC)
{
char *cmd, *response;
int cmd_len, response_len;

Expand Down Expand Up @@ -205,7 +207,7 @@ redis_check_eof(RedisSock *redis_sock, int no_throw TSRMLS_DC)
errno = 0;
if (php_stream_eof(redis_sock->stream) == 0) {
/* If we're using a password, attempt a reauthorization */
if (redis_sock->auth && resend_auth(redis_sock TSRMLS_CC) != 0) {
if (redis_sock->auth && redis_sock_auth(redis_sock TSRMLS_CC) != 0) {
errmsg = "AUTH failed while reconnecting";
break;
}
Expand Down
1 change: 1 addition & 0 deletions library.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *
PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval);
PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force TSRMLS_DC);
PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer,
Expand Down
1 change: 1 addition & 0 deletions redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ PHP_INI_BEGIN()
PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL)

/* redis cluster */
PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL)
PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL)
Expand Down
44 changes: 33 additions & 11 deletions redis_cluster.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, read_timeout)
ZEND_ARG_INFO(0, persistent)
ZEND_ARG_INFO(0, auth)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
Expand Down Expand Up @@ -397,8 +398,10 @@ free_cluster_context(zend_object *object)
#endif

/* Attempt to connect to a Redis cluster provided seeds and timeout options */
void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
double read_timeout, int persistent TSRMLS_DC)
static void
redis_cluster_init(redisCluster *c, HashTable *ht_seeds,
double timeout, double read_timeout, int persistent,
char *auth, strlen_t auth_len TSRMLS_DC)
{
// Validate timeout
if (timeout < 0L || timeout > INT_MAX) {
Expand All @@ -417,6 +420,11 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
zend_throw_exception(redis_cluster_exception_ce,
"Must pass seeds", 0 TSRMLS_CC);
}

if (auth && auth_len > 0) {
c->auth = zend_string_init(auth, auth_len, 0);
}

/* Set our timeout and read_timeout which we'll pass through to the
* socket type operations */
c->timeout = timeout;
Expand All @@ -438,8 +446,9 @@ void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,

/* Attempt to load a named cluster configured in php.ini */
void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) {
zval z_seeds, z_timeout, z_read_timeout, z_persistent, *z_value;
char *iptr;
zval z_seeds, z_timeout, z_read_timeout, z_persistent, z_auth, *z_value;
char *iptr, *auth = NULL;
strlen_t auth_len = 0;
double timeout = 0, read_timeout = 0;
int persistent = 0;
HashTable *ht_seeds = NULL;
Expand Down Expand Up @@ -500,14 +509,27 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) {
}
}

/* Cluster auth */
array_init(&z_auth);
if ((iptr = INI_STR("redis.clusters.auth")) != NULL) {
sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_auth TSRMLS_CC);
}
if ((z_value = zend_hash_str_find(Z_ARRVAL(z_auth), name, name_len)) != NULL &&
Z_TYPE_P(z_value) == IS_STRING && Z_STRLEN_P(z_value) > 0
) {
auth = Z_STRVAL_P(z_value);
auth_len = Z_STRLEN_P(z_value);
}

/* Attempt to create/connect to the cluster */
redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent TSRMLS_CC);
redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, auth, auth_len TSRMLS_CC);

/* Clean up our arrays */
zval_dtor(&z_seeds);
zval_dtor(&z_timeout);
zval_dtor(&z_read_timeout);
zval_dtor(&z_persistent);
zval_dtor(&z_auth);
}

/*
Expand All @@ -517,17 +539,17 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) {
/* Create a RedisCluster Object */
PHP_METHOD(RedisCluster, __construct) {
zval *object, *z_seeds = NULL;
char *name;
strlen_t name_len;
char *name, *auth = NULL;
strlen_t name_len, auth_len = 0;
double timeout = 0.0, read_timeout = 0.0;
zend_bool persistent = 0;
redisCluster *context = GET_CONTEXT();

// Parse arguments
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(),
"Os!|addb", &object, redis_cluster_ce, &name,
&name_len, &z_seeds, &timeout,
&read_timeout, &persistent) == FAILURE)
"Os!|addbs", &object, redis_cluster_ce, &name,
&name_len, &z_seeds, &timeout, &read_timeout,
&persistent, &auth, &auth_len) == FAILURE)
{
RETURN_FALSE;
}
Expand All @@ -543,7 +565,7 @@ PHP_METHOD(RedisCluster, __construct) {
* to a named cluster, stored in php.ini, otherwise we'll need manual seeds */
if (ZEND_NUM_ARGS() > 1) {
redis_cluster_init(context, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
persistent TSRMLS_CC);
persistent, auth, auth_len TSRMLS_CC);
} else {
redis_cluster_load(context, name, name_len TSRMLS_CC);
}
Expand Down
19 changes: 15 additions & 4 deletions redis_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -992,9 +992,9 @@ PS_OPEN_FUNC(rediscluster) {
zval z_conf, *z_val;
HashTable *ht_conf, *ht_seeds;
double timeout = 0, read_timeout = 0;
int persistent = 0;
int retval, prefix_len, failover = REDIS_FAILOVER_NONE;
char *prefix;
int retval, persistent = 0, failover = REDIS_FAILOVER_NONE;
strlen_t prefix_len, auth_len = 0;
char *prefix, *auth = NULL;

/* Parse configuration for session handler */
array_init(&z_conf);
Expand Down Expand Up @@ -1041,7 +1041,7 @@ PS_OPEN_FUNC(rediscluster) {

/* Look for a specific failover setting */
if ((z_val = zend_hash_str_find(ht_conf, "failover", sizeof("failover") - 1)) != NULL &&
Z_TYPE_P(z_val) == IS_STRING
Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0
) {
if (!strcasecmp(Z_STRVAL_P(z_val), "error")) {
failover = REDIS_FAILOVER_ERROR;
Expand All @@ -1050,7 +1050,18 @@ PS_OPEN_FUNC(rediscluster) {
}
}

/* Look for a specific auth setting */
if ((z_val = zend_hash_str_find(ht_conf, "auth", sizeof("auth") - 1)) != NULL &&
Z_TYPE_P(z_val) == IS_STRING && Z_STRLEN_P(z_val) > 0
) {
auth = Z_STRVAL_P(z_val);
auth_len = Z_STRLEN_P(z_val);
}

c = cluster_create(timeout, read_timeout, failover, persistent);
if (auth && auth_len > 0) {
c->auth = zend_string_init(auth, auth_len, 0);
}
if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) {
/* Set up our prefix */
c->flags->prefix = zend_string_init(prefix, prefix_len, 0);
Expand Down

0 comments on commit c5994f2

Please sign in to comment.