本文剖析下遇到的一个问题,即"设置requirepass不生效"这个小问题,本文目录如下:
requirepass字段介绍
requirepass字段是redis.conf中的一个字段,可以看下redis.conf中的注释
# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatibility
# layer on top of the new ACL system. The option effect will be just setting
# the password for the default user. Clients will still authenticate using
# AUTH as usually, or more explicitly with AUTH default
# if they follow the new protocol: both will work.
#
# The requirepass is not compatable with aclfile option and the ACL LOAD
# command, these will cause requirepass to be ignored.
#
# requirepass foobared
即这个字段是用来设置默认用户default的密码的,用户可以通过auth 或者auth default 来认证,同时说明了不能跟aclfile兼容,如果启动acl,则该字段会被忽略,会使用acl文件中的default用户,如果没有配置default用户,则会新建一个nopass的default用户并使用,哈哈,这就是为什么redis.conf配置了requirepass而不生效的原因,提前说了。
如何启用requirepass
- 启用redis.conf中的requirepass,改为自己的密码password,同时启用logfile,注意不要启用aclfile,否则会不生效
- 启动redis-server ./redis.conf
- redis-cli -h localhost -p 6379访问,发现需要进行认证,输入auth password或者auth default password即可进行访问了,默认登录用户就是default用户
- default用户的密码就是requirepass配置的密码,在initServer中会调用ACLUpdateDefaultUserPassword(server.requirepass)函数设置default用户的密码
/* Set the password for the "default" ACL user. This implements supports for
* requirepass config, so passing in NULL will set the user to be nopass. */
void ACLUpdateDefaultUserPassword(sds password) {
ACLSetUser(DefaultUser,"resetpass",-1);
if (password) {
sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
} else {
ACLSetUser(DefaultUser,"nopass",-1);
}
}
至于为什么启用aclfile时会不生效,请继续看
启用requirepass时requirepass不生效?
现象
requirepass是default用户的密码,配置密码后,aclfile也启用时,修改redis.conf配置后重启redis后,redis-cli -h localhost -p port 无需认证仍然可以访问,即没有生效
看下redis.conf中注释可以知道跟aclfile是不兼容的,启用aclfile时,会忽略requirepass
原因
redis.conf中同时启用requirepass和aclfile,redis在加载配置时,会读取aclfile,重新新建全局Users对象,调用ACLInitDefaultUser函数重新新建nopass的default用户,先前已加载的defaultUser对象(密码从requirepass来)不会被用到,即default用户是nopass的,但是如果acl文件中配置了default用户以及配置了密码,则还是需要认证的
sds ACLLoadFromFile(const char *filename) {
...
/* The default user pointer is referenced in different places: instead
* of replacing such occurrences it is much simpler to copy the new
* default user configuration in the old one. */
user *new_default = ACLGetUserByName("default",7);
if (!new_default) {
new_default = ACLCreateDefaultUser(); // nopass的default用户
}
ACLCopyUser(DefaultUser,new_default);
ACLFreeUser(new_default);
raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
raxRemove(old_users,(unsigned char*)"default",7,NULL);
ACLFreeUsersSet(old_users);
sdsfree(errors);
return NULL;
...
}
解决方法
- 不启用aclfile,只使用requirepass,即只有default用户了
- 启用aclfile,redis-cli登录后,用config set requirepass xxx,会生效,然后重新redis-cli登录访问即可,如果需要重启redis也生效,则进行acl save(会写default的user规则到aclfile中)
注意点:config set requirepass xxx会调用updateRequirePass函数,该函数会继续调用ACLUpdateDefaultUserPassword更新default用户的密码(nopass变为有密码状态),注意redis最新版本(7.0以上)只会在更新的内容发生变化时才会调用到updateRequirePass函数,如下面的sdsConfigSet函数,在内容有变化时才返回1
configSetCommand -> performInterfaceSet -> sdsConfigSet函数
performInterfaceSet会将sdsConfigSet函数返回值作为自己的返回值,
configSetCommand函数判断performInterfaceSet返回值,如果为1,则
会调用到updateRequirePass函数
- 使用上还是直接使用aclfile即可,将requirepass注释掉,登录后新增用户,然后acl save
总结
本文主要介绍了如何启用requirepass,以及启用requirepass为什么不会生效,从代码层面分析了不生效的原因,是因为同时启用了aclfile导致requirepass中的密码不会被用到,最后介绍了解决方法,建议使用上直接使用aclfile即可。