Added IP range restriction.

master
Nicolas Favre-Felix 14 years ago
parent 1209922c03
commit de5c2839e4

@ -21,6 +21,7 @@ curl -d "GET/hello" http://127.0.0.1:7379/
* Raw Redis 2.0 protocol output with `?format=raw`
* HTTP 1.1 pipelining (45 kqps on a desktop Linux machine.)
* Connects to Redis using a TCP or UNIX socket.
* Restricted commands by IP range (CIDR subnet + mask), returning 403 errors.
# Ideas, TODO...
* Add meta-data info per key (MIME type in a second key, for instance).
@ -30,9 +31,7 @@ curl -d "GET/hello" http://127.0.0.1:7379/
* Add logging.
* Enrich config file:
* Provide timeout (this needs to be added to hiredis first.)
* Restrict commands by IP range
* Get config file path from command line.
* Change config file to JSON format? That would be convenient.
* Send your ideas using the github tracker or on twitter [@yowgi](http://twitter.com/yowgi).
# HTTP error codes
@ -41,6 +40,7 @@ curl -d "GET/hello" http://127.0.0.1:7379/
* Could also be used:
* Timeout on the redis side: 503 Service Unavailable
* Missing key: 404 Not Found
* Unauthorized command (disabled in config file): 403 Forbidden
# Command format
The URI `/COMMAND/arg0/arg1/.../argN` executes the command on Redis and returns the response to the client. GET and POST are supported:

45
cmd.c

@ -1,5 +1,6 @@
#include "cmd.h"
#include "server.h"
#include "conf.h"
#include "formats/json.h"
#include "formats/raw.h"
@ -7,6 +8,8 @@
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
#include <netinet/in.h>
#include <arpa/inet.h>
struct cmd *
cmd_new(struct evhttp_request *rq, int count) {
@ -32,7 +35,39 @@ cmd_free(struct cmd *c) {
free(c);
}
void
int
cmd_authorized(struct conf *cfg, struct evhttp_request *rq, const char *verb, size_t verb_len) {
struct disabled_command *dc;
unsigned int i;
char *client_ip;
u_short client_port;
in_addr_t client_addr;
/* find client's address */
evhttp_connection_get_peer(rq->evcon, &client_ip, &client_port);
client_addr = ntohl(inet_addr(client_ip));
for(dc = cfg->disabled; dc; dc = dc->next) {
/* CIDR test */
if((client_addr & dc->mask) != (dc->subnet & dc->mask)) {
continue;
}
/* matched an ip */
for(i = 0; i < dc->count; ++i) {
if(strncasecmp(dc->commands[i], verb, verb_len) == 0) {
return 0;
}
}
}
return 1;
}
int
cmd_run(struct server *s, struct evhttp_request *rq,
const char *uri, size_t uri_len) {
@ -71,9 +106,14 @@ cmd_run(struct server *s, struct evhttp_request *rq,
cmd->argv[0] = uri;
cmd->argv_len[0] = cmd_len;
/* check that the client is able to run this command */
if(!cmd_authorized(s->cfg, rq, cmd->argv[0], cmd->argv_len[0])) {
return -1;
}
if(!slash) {
redisAsyncCommandArgv(s->ac, fun, cmd, 1, cmd->argv, cmd->argv_len);
return;
return 0;
}
p = slash + 1;
while(p < uri + uri_len) {
@ -97,6 +137,7 @@ cmd_run(struct server *s, struct evhttp_request *rq,
}
redisAsyncCommandArgv(s->ac, fun, cmd, param_count, cmd->argv, cmd->argv_len);
return 0;
}

@ -28,7 +28,7 @@ cmd_new(struct evhttp_request *rq, int count);
void
cmd_free(struct cmd *c);
void
int
cmd_run(struct server *s, struct evhttp_request *rq,
const char *uri, size_t uri_len);

@ -66,7 +66,7 @@ conf_disable_commands(json_t *jtab) {
unsigned int i, cur, n;
char *p, *ip;
const char *s;
in_addr_t mask_ip;
in_addr_t mask, subnet;
short mask_bits = 0;
struct disabled_command *dc;
@ -78,7 +78,7 @@ conf_disable_commands(json_t *jtab) {
/* parse key in format "ip/mask" */
s = json_object_iter_key(kv);
p = strchr(s, ':');
p = strchr(s, '/');
if(!p) {
ip = strdup(s);
} else {
@ -86,8 +86,8 @@ conf_disable_commands(json_t *jtab) {
memcpy(ip, s, p - s);
mask_bits = atoi(p+1);
}
mask_ip = inet_addr(ip);
mask = (mask_bits == 0 ? 0 : (0xffffffff << (32 - mask_bits)));
subnet = ntohl(inet_addr(ip)) & mask;
/* count strings in the array */
n = 0;
@ -101,8 +101,9 @@ conf_disable_commands(json_t *jtab) {
/* allocate block */
dc = calloc(1, sizeof(struct disabled_command));
dc->commands = calloc(n, sizeof(char*));
dc->mask_ip = mask_ip;
dc->mask_bits = mask_bits;
dc->subnet = subnet;
dc->mask = mask;
dc->count = n;
dc->next = root;
root = dc;

@ -6,9 +6,10 @@
struct disabled_command {
in_addr_t mask_ip;
short mask_bits;
in_addr_t subnet;
in_addr_t mask;
unsigned int count;
char **commands;
struct disabled_command *next;

@ -18,19 +18,22 @@ on_request(struct evhttp_request *rq, void *ctx) {
const char *uri = evhttp_request_uri(rq);
struct server *s = ctx;
int ret;
if(!s->ac) { /* redis is unavailable */
evhttp_send_reply(rq, 503, "Service Unavailable", NULL);
return;
}
/* check that the command can be executed */
switch(rq->type) {
case EVHTTP_REQ_GET:
cmd_run(s, rq, 1+uri, strlen(uri)-1);
ret = cmd_run(s, rq, 1+uri, strlen(uri)-1);
break;
case EVHTTP_REQ_POST:
cmd_run(s, rq,
ret = cmd_run(s, rq,
(const char*)EVBUFFER_DATA(rq->input_buffer),
EVBUFFER_LENGTH(rq->input_buffer));
break;
@ -39,6 +42,10 @@ on_request(struct evhttp_request *rq, void *ctx) {
evhttp_send_reply(rq, 405, "Method Not Allowed", NULL);
return;
}
if(ret < 0) {
evhttp_send_reply(rq, 403, "Forbidden", NULL);
}
}
int

@ -1,5 +0,0 @@
redis_host 127.0.0.1
redis_port 6379
http_host 0.0.0.0
http_port 7379

@ -6,6 +6,6 @@
"http_port": 7379,
"disable": {
"255.255.255.255/32": ["DEBUG", "FLUSHDB", "FLUSHALL"]
"0.0.0.0/0": ["DEBUG", "FLUSHDB", "FLUSHALL"]
}
}

Loading…
Cancel
Save