Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added IP range restriction.

  • Loading branch information...
commit de5c2839e4d12856fff99e390483caf20d4233b7 1 parent 1209922
@nicolasff authored
View
4 README.markdown
@@ -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:
View
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;
}
View
2  cmd.h
@@ -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);
View
13 conf.c
@@ -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;
View
5 conf.h
@@ -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;
View
11 turnip.c
@@ -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
View
5 turnip.conf
@@ -1,5 +0,0 @@
-redis_host 127.0.0.1
-redis_port 6379
-
-http_host 0.0.0.0
-http_port 7379
View
2  turnip.json
@@ -6,6 +6,6 @@
"http_port": 7379,
"disable": {
- "255.255.255.255/32": ["DEBUG", "FLUSHDB", "FLUSHALL"]
+ "0.0.0.0/0": ["DEBUG", "FLUSHDB", "FLUSHALL"]
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.