diff options
author | Pavlo Yatsukhnenko <yatsukhnenko@gmail.com> | 2019-01-15 11:07:30 +0300 |
---|---|---|
committer | Pavel Yatsukhnenko <p.yatsukhnenko@digitalscreens.com.ua> | 2019-01-18 00:05:52 +0300 |
commit | c5994f2a42b8a348af92d3acb4edff1328ad8ce1 (patch) | |
tree | b2f4cdc3d7986860ef4bf5e311001820cb57bd2b | |
parent | e145f8582ab9aa5bb6e986298be2e47d8063c8a0 (diff) |
RedisCluster auth
-rw-r--r-- | cluster.markdown | 7 | ||||
-rw-r--r-- | cluster_library.c | 31 | ||||
-rw-r--r-- | cluster_library.h | 4 | ||||
-rw-r--r-- | common.h | 2 | ||||
-rw-r--r-- | library.c | 6 | ||||
-rw-r--r-- | library.h | 1 | ||||
-rw-r--r-- | redis.c | 1 | ||||
-rw-r--r-- | redis_cluster.c | 44 | ||||
-rw-r--r-- | redis_session.c | 19 |
9 files changed, 90 insertions, 25 deletions
diff --git a/cluster.markdown b/cluster.markdown index 4dcbc2ba..3260bb79 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -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 @@ -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 @@ -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 @@ -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. diff --git a/cluster_library.c b/cluster_library.c index 1f6d4160..b0f7b53a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -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 @@ -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; } @@ -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 */ @@ -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); @@ -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); @@ -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; } @@ -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 || diff --git a/cluster_library.h b/cluster_library.h index 979b228d..366b395a 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -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 */ @@ -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; @@ -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)) @@ -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; @@ -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; } @@ -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, @@ -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) diff --git a/redis_cluster.c b/redis_cluster.c index e8cd4038..9bc42547 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -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) @@ -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) { @@ -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; @@ -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; @@ -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); } /* @@ -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; } @@ -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); } diff --git a/redis_session.c b/redis_session.c index 2127f3e6..31f6b149 100644 --- a/redis_session.c +++ b/redis_session.c @@ -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); @@ -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; @@ -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); |