前言:

Twemproxy不支持Auth 指令。

而且细想之下Twemproxy不支持Auth是有道理的。
当然在一些特需下,还是需要Twemproxy支持Auth。
暂且不讨论Redis的弱验证模式与其他解决方案。
正文:
为Twemproxy添加Auth功能,实际上分为两个部分:
1.client to twemproxy (对外有安全需要)
2.twemproxy to redis  (对内redis有安全需要)
在实现2个auth功能前,我们需要为Twemproxy配置一个password
需要修改的地方如下:
1. 配置文件中添加password
password: yourpassword
2.nc_conf.h
2.1 为conf_pool 添加password成员
struct conf_pool {
...
    struct string      password;
...
}
2.2 为password配置声明解析方法
char *conf_set_password(struct conf *cf,struct command *cmd,void *conf);
3.nc_server.h
3.1 为server_pool添加成员password
struct server_pool {
...
    char               password[128];
...
}
4.nc_conf.c 
4.1 声明要解析的参数password
static struct command conf_commands[] = {
...
    { string("password"),
      conf_set_password,
      offsetof(struct conf_pool, password) },
...
}
4.2 初始化/反初始化 conf_pool中的password参数
static rstatus_t
conf_pool_init(struct conf_pool *cp, struct string *name)
{
...
    cp->password.data = CONF_UNSET_PTR;
...
}
static void
conf_pool_deinit(struct conf_pool *cp)
{
...
...
}
    string_deinit(&cp->password);
4.3 实现对password 配置的解析
char *
conf_set_password(struct conf *cf,struct command *cmd,void *conf)
{
    uint8_t *p;
    struct string *field, *value;
    p = conf;
    field = (struct string *)(p + cmd->offset);
    if (field->data != CONF_UNSET_PTR) {
        return "is a duplicate";
    }
    value = array_top(&cf->arg);
    if (value->len == 0 ) {
        return "password is null";
    }
    if (value->len >100 ) {
        return "password is too long";
    }
    field->data = (char*)malloc(128);
    memcpy(field->data,value->data,value->len);
    field->data[value->len] = '\0';
    field->len = value->len;
    printf("password is:%s\n",field->data);
    return CONF_OK;
}
4.4 将conf_pool中的password copy到server_pool中去
rstatus_t
conf_pool_each_transform(void *elem, void *data)
{
...
...
}
如此这番之后我们就为Twemproxy添加了配置密码的功能
下面我们实现两种Auth指令需求
    if(cp->password.data != CONF_UNSET_PTR)
    {
        strcpy(sp->password,cp->password.data);
    }else
    {
        sp->password[0] == '\0';//password 长度为0表示没有密码
    }
Client to Twemproxy:
1.为Twemproxy添加Auth指令支持
1.1 nc_message.h
1.1.1 在MSG_TYPE_CODEC中添加AUTH ACTION的声明
#define MSG_TYPE_CODEC(ACTION)
...
    ACTION( REQ_REDIS_AUTH ) /* 设置密码*/\
...
1.2 nc_redis.c
1.2.1 在没有arg(不包含hash key与action key)的指令中注册
static bool
redis_arg0(struct msg *r)
{
    switch (r->type) {
...
    case MSG_REQ_REDIS_AUTH:
...
    }
    return false;
}
1.2.2 在request解析中添加action auth的解析
void
redis_parse_req(struct msg *r)
{
    ...
    for (p = r->pos; p < b->last; p++) {
        ch = *p;
        switch (state) {
        ...
        case SW_REQ_TYPE:
            ...
            switch (p - m) {
            ...
            case 4:
                ...
                if (str4icmp(m, 'a', 'u', 't', 'h'))
                {//将auth的解析放在最后,因为auth指令总不是常用的
                    r->type = MSG_REQ_REDIS_AUTH;
                    break;
                }
                break;
            ...
            default:
                break;
            }
            ...
            break;
        ...
        case SW_SENTINEL:
        default:
            NOT_REACHED();
            break;
        }
    }
    ...
}
2. 为conn添加password设置是否成功的标记
2.1 nc_connection.h
2.1.1 为conn添加password设置成功与否的标记变量
struct conn {
...
    unsigned           setpassword:1; /* 标记是否设置了密码 */
...
}
2.2 nc_connection.c
2.2.2 初始化conn的setpassword成员
struct conn *
conn_get(void *owner, bool client, bool redis)
{
...
    conn->setpassword = 0;
...
}
3. Auth功能实现
3.1 nc_request.c
3.1.1 过滤掉没有Auth的conn的请求,验证Auth
static bool
req_filter(struct context *ctx, struct conn *conn, struct msg *msg)
{
...
    if(msg->type == MSG_REQ_REDIS_AUTH)
    {//验证密码
        struct server_pool *pool = conn->owner;
        struct mbuf* p_buf = STAILQ_FIRST(&msg->mhdr);
        char* p_start =p_buf->pos;
        char* p_end = p_buf->last;
        char passwordinfo[256];
        sprintf(passwordinfo,
                "*2\r\n$4\r\nauth\r\n$%d\r\n%s\r\n",
                strlen(pool->password),
                pool->password);
        if(msg->mlen != strlen(passwordinfo)
                || p_end - p_start != msg->mlen//如果密码被分段了也认定为错误
                || memcmp(p_start+13,passwordinfo+13,msg->mlen-13)!=0)
        {//密码错误
            struct msg* pong = fGetAuthErrMsg();
            conn->setpassword = 0;
            fReturnMsgToClient(ctx,conn,pong,msg);
            return true;
        }else
        {
            conn->setpassword = 1;
            struct msg* ok = fGetOKMsg();
            fReturnMsgToClient(ctx,conn,ok,msg);
            return true;
        }
    }
    if(conn->setpassword == 0)
    {//没有密码检测下是否需要密码
        struct server_pool *pool = conn->owner;
        if(pool->password[0]!=0)
        {//表示需要密码
            struct msg* pong = fGetNeedAuthMsg();
            fReturnMsgToClient(ctx,conn,pong,msg);
            return true;
        }
    }

...
}
3.2 nc_message.h
3.2.1 声明ACTION Auth 使用到的静态响应包
struct msg* fGetNeedAuthMsg();
struct msg* fGetAuthErrMsg();
struct msg* fGetOKMsg();
3.3 nc_message.c
3.3.1 实现ACTION Auth 使用到的静态响应包
struct msg* fGetAuthErrMsg()
{
    struct msg* pong = _msg_get();
    struct mbuf *mbuf;
    if(pong == NULL)
    {
        return NULL;
    }
    pong->mlen = 0;
    mbuf = mbuf_get();
    if (mbuf == NULL) {
        msg_put(pong);
        return NULL;
    }
    mbuf_insert(&(pong->mhdr), mbuf);
    pong->p_key_info = NULL;
    static int noauthlen = strlen("-ERR invalid password\r\n");
    memcpy(mbuf->last,
           "-ERR invalid password\r\n",
           noauthlen);
    mbuf->last += noauthlen;
    pong->mlen += noauthlen;
    return pong;
}
struct msg* fGetNeedAuthMsg()
{
    struct msg* pong = _msg_get();
    struct mbuf *mbuf;
    if(pong == NULL)
    {
        return NULL;
    }
    pong->mlen = 0;
    mbuf = mbuf_get();
    if (mbuf == NULL) {
        msg_put(pong);
        return NULL;
    }
    mbuf_insert(&(pong->mhdr), mbuf);
    pong->p_key_info = NULL;
    static int noauthlen = strlen("-NOAUTH Authentication required.\r\n");
    memcpy(mbuf->last,
           "-NOAUTH Authentication required.\r\n",
           noauthlen);
    mbuf->last += noauthlen;
    pong->mlen += noauthlen;
    return pong;
}
struct msg* fGetOKMsg()
{
    struct msg* OK = _msg_get();
    struct mbuf *mbuf;
    if(OK == NULL)
    {
        return NULL;
    }
    OK->mlen = 0;
    mbuf = mbuf_get();
    if (mbuf == NULL) {
        msg_put(OK);
        return NULL;
    }
    mbuf_insert(&(OK->mhdr), mbuf);
    OK->p_key_info = NULL;
    memcpy(mbuf->last,
           "+OK\r\n",
           5);
    mbuf->last += 5;
    OK->mlen += 5;
    return OK;
}
Twemproxy To Redis:
1. 初始化conn,
1.1 nc_server.c
1.1.1 每当conn需要重连的时候都需要重置一下
rstatus_t
server_connect(struct context *ctx, struct server *server, struct conn *conn)
{
    rstatus_t status;
    ASSERT(!conn->client && !conn->proxy);
    if (conn->sd > 0) {
        /* already connected on server connection */
        return NC_OK;
    }
    conn->setpassword = 0;//标记为没有设置密码
 ...

}
2. 发送Auth指令
2.1 nc_request.c
2.2.1 每当conn发送一个message但是又没有标记Auth的时候发送Auth指令
void
req_server_enqueue_imsgq(struct context *ctx,
                         struct conn *conn,
                         struct msg *msg)
{
    ASSERT(msg->request);
    ASSERT(!conn->client && !conn->proxy);
    struct server *ser = (struct server *)conn->owner;
    if(ser->owner->password[0]!=0 && conn->setpassword==0)
    {//要设置密码,但是还没有设置成功
        struct msg* p_password = fGetAuthMsg(ser->owner->password);
        conn->setpassword = 1;//只管发送第一个请求前发送一次auth指令,其他的就不管了
        req_server_enqueue_imsgq(ctx,conn,p_password);
    }
    //广播命令只有在最后一次发送时,加入timeout中
    if ((!msg->noreply)
            &&(!msg->broadcast
               ||msg->ui32_broadcast_number==0
                )) {
        msg_tmo_insert(msg, conn);
    }
    TAILQ_INSERT_TAIL(&conn->imsg_q, msg, s_tqe);
    stats_server_incr(ctx, conn->owner, in_queue);
    stats_server_incr_by(ctx, conn->owner, in_queue_bytes, msg->mlen);
}
2.3 nc_message.h
2.3.1 声明AuthMsg
struct msg* fGetAuthMsg(char* password);
2.4 nc_message.c
2.4.1 实现AuthMsg
struct msg* fGetAuthMsg(char* password)
{
    struct msg* AuthPassword = _msg_get();
    struct mbuf *mbuf;
    if(AuthPassword == NULL)
    {
        return NULL;
    }
    AuthPassword->mlen = 0;
    mbuf = mbuf_get();
    if (mbuf == NULL) {
        msg_put(AuthPassword);
        return NULL;
    }
    mbuf_insert(&(AuthPassword->mhdr), mbuf);
    AuthPassword->p_key_info = NULL;
    sprintf(mbuf->last,"*2\r\n$4\r\nauth\r\n$%d\r\n%s\r\n",strlen(password),password);
    mbuf->last += strlen(mbuf->last);
    AuthPassword->mlen += strlen(mbuf->last);
    AuthPassword->swallow = 1;
    return AuthPassword;
}
到此为Twemproxy添加Auth功能就已经实现了
后记:
当然这里面还是有很多地方并不完善,比如Twemproxy to Reid 的Auth 并没有管失败的情况。
没有将各个Redis节点之间的Auth分开,也没有与Client to Twemproxy分离。
至于这些就根据实际需求实现吧。

为Twemproxy 添加 Auth相关推荐

  1. Redis集群~StackExchange.Redis(10月6号版1.1.608.0)连接Twemproxy支持Auth指令了

    对于StackExchange.Redis这个驱动来说,之前的版本在使用Proxy为Twemproxy代理时,它是不支持Password属性的,即不支持原始的Auth指令,而我也修改过源代码,为Com ...

  2. mongodb 安装和配置auth验证

    为什么80%的码农都做不了架构师?>>>    安装 brew install mongodb mongodb 配置 启动 brew services start mongodb 创 ...

  3. Asterisk在mysql数据库中添加sip账号

    Asterisk版本:15.5.0 系统版本:Ubuntu 14.04 添加sip账号有好几种方法,本文中描述的只是其中的一种方法.在网上我也找了好多配置sippeers账号的,始终没有配置成功,最后 ...

  4. laravel auth.php,Laravel 用户认证 Auth

    很多应用是需要登陆后才能操作,Laravel提供了一个auth工具来实现用户的认证功能.并且有一个config/auth.php来配置auth工具.大概看一下auth工具的常用方法 Auth::che ...

  5. python后端需要什么基础_一个六年经验的python后端是怎么学习用java写API的(6) 基本的Auth...

    描述 实现了依赖注入之后就可以方便的实现各种API的业务逻辑了,下一部的问题就在于权限,我们知道大部分的系统API并不是开放的,需要基本的用户体系(注册.登录.购买.会员.不同的role等等),例如管 ...

  6. mongo报错:not authorized on bb to execute command { create: \“xxx\“...}

    mongo报错: {"ok" : 0,"errmsg" : "not authorized on bb to execute command { cr ...

  7. Python:Resquest模块

    Requests: 让 HTTP 服务人类 虽然Python的标准库中 urllib2 模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests 自称 & ...

  8. CentOS7.4 安装mongodb

    温馨提示:我的环境是腾讯云自带的CentOS7.4 x64 镜像,本地环境是win10 x64 专业版,ssh工具是用的win10 自带的cmd, 远程工具版本是Robo 3T 1.2.1 . 如果环 ...

  9. docker使用mongo_如何使用Docker在AWS上部署Mongo:初学者的权威指南

    docker使用mongo 为什么需要这个? (Why you need this?) 因为JS + Python + Mongo =完整的数据开发 (Because JS + Python + Mo ...

最新文章

  1. 如何高效获取无线充电电能-无线节能组
  2. FPGA中建立时间和保持时间不满足如何解决
  3. 基于X-Engine引擎的实时历史数据库解决方案揭秘
  4. TPC-C中跑赢Oracle的OceanBase,最近有何惊艳?
  5. html页面判断其他div为空,将外部html加载到div - 页面加载然后变为空白
  6. 调用k8s api遇到CERTIFICATE_VERIFY_FAILED的问题解决方法
  7. numpy常用的一种高效切片方式
  8. setImageResource导致的ANR
  9. 计算机附件常用工具,Windows附件常用工具
  10. 通过iis发布网站、并添加ssl证书
  11. iOS - 毛玻璃效果
  12. Emitter使用方法
  13. 联想笔记本修复计算机还原系统失败,联想电脑重置电脑失败怎么办
  14. daytime协议的服务器和客户端程序,用socket套接字实现daytime协议服务器和客户端程序.doc...
  15. 游戏陪玩App,如雨后春笋般冒出,直接导致整个游戏陪玩源码市场的持续火爆
  16. 2020起重机司机(限桥式起重机)作业考试题库及起重机司机(限桥式起重机)实操考试视频
  17. Java 将Word保存为WPS和WPT格式
  18. 编程修养 值得看一下,先mark一下,回头看
  19. 【嵌入式物联网】NodeMCU开发板引脚介绍和主要技术参数
  20. Word中关于参考文献连续引用

热门文章

  1. TypeScript方法重载
  2. OpenStake组件
  3. 清理win7系统闲置服务器,教你还原win7系统WICleanup清理Windows Installer冗余文件的方案...
  4. SolidWorks使用教程
  5. FFmpeg的avcodec_decode_video2()函数
  6. 对网站服务器日志进行分析
  7. 《FFmpeg从入门到精通》读书笔记(一)
  8. c语言 求幂级数展开的部分和
  9. Twisted核心(官方文档)——3
  10. 因更新驱动致“win7重启后无法正常启动、无法通过系统还原修复”的解决方案。