-
Notifications
You must be signed in to change notification settings - Fork 531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support REDIS AUTH command #576
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
dyn_o_mite: | ||
dyn_listen: 0.0.0.0:8101 | ||
data_store: 0 | ||
listen: 0.0.0.0:8102 | ||
dyn_seed_provider: simple_provider | ||
servers: | ||
- 127.0.0.1:22122:1 | ||
tokens: 437425602 | ||
stats_listen: 0.0.0.0:22222 | ||
redis_requirepass: helloworld |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -355,9 +355,117 @@ req_recv_next(struct context *ctx, struct conn *conn, bool alloc) | |
return req; | ||
} | ||
|
||
static struct mbuf * | ||
get_mbuf(struct msg *msg) | ||
{ | ||
struct mbuf *mbuf; | ||
mbuf = STAILQ_LAST(&msg->mhdr, mbuf, next); | ||
if (mbuf == NULL || mbuf_full(mbuf)) { | ||
mbuf = mbuf_get(); | ||
if (mbuf == NULL) { | ||
return NULL; | ||
} | ||
mbuf_insert(&msg->mhdr, mbuf); | ||
msg->pos = mbuf->pos; | ||
} | ||
|
||
return mbuf; | ||
} | ||
|
||
static void | ||
auth_reply(struct context *ctx, struct conn *conn, struct msg *smsg, const char *usr_msg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you move And have a top level function called |
||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the top of the new file src/proto/dyn_redis_auth.c, could you add some comments about how Dynomite performs this authentication. Eg: On Dynomite startup, the server authenticates with the datastore by itself. And on each client connection, it authenticates on the first AUTH command from the client. (add detail as you see fit) |
||
int n; | ||
struct mbuf *mbuf; | ||
struct msg *msg = msg_get(conn, false, __FUNCTION__); | ||
if (msg == NULL) { | ||
return; | ||
} | ||
|
||
mbuf = get_mbuf(msg); | ||
if (mbuf == NULL) { | ||
msg_put(msg); | ||
return; | ||
} | ||
|
||
smsg->peer = msg; | ||
smsg->selected_rsp = msg; | ||
msg->peer = smsg; | ||
|
||
n = (int)strlen(usr_msg); | ||
memcpy(mbuf->last, usr_msg, (size_t)n); | ||
mbuf->last += n; | ||
msg->mlen += (uint32_t)n; | ||
msg->done = 1; | ||
|
||
conn_event_add_out(conn); | ||
TAILQ_INSERT_TAIL(&conn->omsg_q, smsg, c_tqe); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a race. You'd need to insert it to the omsg_q before doing an event notification. Also, prefer to use the |
||
} | ||
|
||
static int | ||
get_password(unsigned char *string, unsigned int len, char *passwd, uint32_t *passwd_len) | ||
{ | ||
char *p; | ||
char *pos; | ||
char buff[128]; | ||
int cmdlen, nargc; | ||
size_t l; | ||
|
||
p = (char *)string; | ||
if (p[0] != '*') { | ||
return -1; | ||
} | ||
|
||
/* deal with nargc */ | ||
pos = strstr(p + 1, CRLF); | ||
if (!pos) return -1; | ||
l = pos - (p + 1); | ||
memcpy(buff, p + 1, l); | ||
buff[l] = '\0'; | ||
nargc = atoi(buff); | ||
if (nargc != 2) return -1; | ||
|
||
/* deal with cmd */ | ||
p = pos + 2; | ||
if (*p != '$') return -1; | ||
pos = strstr(p + 1, CRLF); | ||
if (!pos) return -1; | ||
l = pos - (p + 1); | ||
memcpy(buff, p + 1, l); | ||
buff[l] = '\0'; | ||
cmdlen = atoi(buff); | ||
p = pos + 2; | ||
pos = strstr(p, CRLF); | ||
if (!pos) return -1; | ||
l = pos - p; | ||
if (l != cmdlen) return -1; | ||
memcpy(buff, p, l); | ||
buff[l] = '\0'; | ||
if (strcasecmp("AUTH", buff) != 0) return -1; | ||
|
||
/* password */ | ||
p = pos + 2; | ||
if (*p != '$') return -1; | ||
pos = strstr(p + 1, CRLF); | ||
if (!pos) return -1; | ||
l = pos - (p + 1); | ||
memcpy(buff, p + 1, l); | ||
buff[l] = '\0'; | ||
*passwd_len = atoi(buff); | ||
p = pos + 2; | ||
pos = strstr(p, CRLF); | ||
if (!pos) return -1; | ||
l = pos - p; | ||
memcpy(passwd, p, l); | ||
passwd[l] = '\0'; | ||
return 0; | ||
} | ||
|
||
static bool | ||
req_filter(struct context *ctx, struct conn *conn, struct msg *req) | ||
{ | ||
struct server_pool *pool; | ||
|
||
ASSERT(conn->type == CONN_CLIENT); | ||
|
||
if (msg_empty(req)) { | ||
|
@@ -367,6 +475,33 @@ req_filter(struct context *ctx, struct conn *conn, struct msg *req) | |
return true; | ||
} | ||
|
||
/* | ||
* Handle "AUTH requirepass\r\n" | ||
*/ | ||
if (conn->authenticated) { | ||
if (req->type == MSG_REQ_REDIS_AUTH) { | ||
pool = &ctx->pool; | ||
char passwd[128]; | ||
uint32_t passwd_len; | ||
if (get_password(req->mhdr.stqh_first->start, | ||
req->mlen, passwd, &passwd_len) != 0) { | ||
auth_reply(ctx, conn, req, "-Unknown cmd"); | ||
return true; | ||
} | ||
if (passwd_len == pool->redis_requirepass.len) { | ||
if (memcmp(pool->redis_requirepass.data, passwd, passwd_len) == 0) { | ||
auth_reply(ctx, conn, req, "+OK\r\n"); | ||
conn->authenticated = 0; | ||
return true; | ||
} | ||
} | ||
auth_reply(ctx, conn, req, "-ERR invalid password\r\n"); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can remove this else condition and just do:
|
||
auth_reply(ctx, conn, req, "-NOAUTH Authentication required\r\n"); | ||
} | ||
return true; | ||
} | ||
|
||
/* | ||
* Handle "quit\r\n", which is the protocol way of doing a | ||
* passive close | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -296,17 +296,53 @@ server_close(struct context *ctx, struct conn *conn) | |
server_failure(ctx, datastore); | ||
} | ||
|
||
static void | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move |
||
redis_auth(struct context *ctx, struct conn *conn) | ||
{ | ||
#define REDIS_AUTH_CMD "*2\r\n$4\r\nAUTH\r\n" | ||
struct server_pool *pool; | ||
struct msg *msg; | ||
struct mbuf *mbuf; | ||
int n; | ||
char auth[1024]; | ||
|
||
pool = &ctx->pool; | ||
if (pool->redis_requirepass.len > 0) { | ||
msg = msg_get(conn, true, __FUNCTION__); | ||
if (msg == NULL) { | ||
return; | ||
} | ||
|
||
mbuf = mbuf_get(); | ||
if (mbuf == NULL) { | ||
return; | ||
} | ||
|
||
n = snprintf(auth, sizeof(auth), REDIS_AUTH_CMD "$%d\r\n%s\r\n", | ||
pool->redis_requirepass.len, pool->redis_requirepass.data); | ||
|
||
memcpy(mbuf->last, auth, (size_t)n); | ||
mbuf->last += n; | ||
mbuf_insert(&msg->mhdr, mbuf); | ||
msg->pos = mbuf->pos; | ||
msg->mlen = (uint32_t)n; | ||
TAILQ_INSERT_TAIL(&conn->imsg_q, msg, s_tqe); | ||
} | ||
#undef REDIS_AUTH_CMD | ||
} | ||
|
||
static void | ||
server_connected(struct context *ctx, struct conn *conn) | ||
{ | ||
ASSERT(conn->type == CONN_SERVER); | ||
ASSERT(conn->connecting && !conn->connected); | ||
ASSERT(conn->connecting && !conn->connected); | ||
|
||
conn->connecting = 0; | ||
conn->connected = 1; | ||
conn->connecting = 0; | ||
conn->connected = 1; | ||
conn_pool_connected(conn->conn_pool, conn); | ||
|
||
log_notice("%s connected ", print_obj(conn)); | ||
redis_auth(ctx, conn); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of calling It's fairly simple to do, and you can follow this example of how I added a datastore agnostic rewrite_query() function: grep for |
||
} | ||
|
||
static void | ||
|
@@ -419,6 +455,7 @@ server_pool_init(struct server_pool *sp, struct conf_pool *cp, struct context *c | |
/* sp->continuum = NULL; */ | ||
sp->next_rebuild = 0ULL; | ||
|
||
sp->redis_requirepass = cp->redis_requirepass; | ||
sp->name = cp->name; | ||
sp->proxy_endpoint.pname = cp->listen.pname; | ||
sp->proxy_endpoint.port = (uint16_t)cp->listen.port; | ||
|
@@ -832,7 +869,19 @@ server_rsp_forward(struct context *ctx, struct conn *s_conn, struct msg *rsp) | |
|
||
c_conn = req->owner; | ||
log_info("%s %s RECEIVED %s", print_obj(c_conn), print_obj(req), print_obj(rsp)); | ||
|
||
|
||
if (c_conn->type == CONN_SERVER) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this block of code necessary? |
||
static const char *AUTH_OK = "+OK\r\n"; | ||
int ret = memcmp(AUTH_OK, rsp->mhdr.stqh_first->start, rsp->mlen); | ||
if (ret == 0) { | ||
log_debug(LOG_INFO, "AUTH requirepass OK"); | ||
return; | ||
} else { | ||
log_debug(LOG_ERR, "%s", rsp->mhdr.stqh_first->start); | ||
ASSERT(ret == 0); | ||
} | ||
} | ||
|
||
ASSERT((c_conn->type == CONN_CLIENT) || | ||
(c_conn->type == CONN_DNODE_PEER_CLIENT)); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this
get_or_create_mbuf(struct msg*)
and move it to dyn_message.h/c.