Merge pull request '阅读acl对应的源码' (#5) from dev into master

Reviewed-on: zeekling/redis#5
This commit is contained in:
LingZhaoHui 2020-11-26 15:05:30 +00:00
commit 6d671f58e0
5 changed files with 68 additions and 27 deletions

View File

@ -759,7 +759,7 @@ acllog-max-len 128
# The format of the external ACL user file is exactly the same as the # The format of the external ACL user file is exactly the same as the
# format that is used inside redis.conf to describe users. # format that is used inside redis.conf to describe users.
# #
# aclfile /etc/redis/users.acl aclfile /tmp/users.acl
# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatiblity # IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatiblity
# layer on top of the new ACL system. The option effect will be just setting # layer on top of the new ACL system. The option effect will be just setting

View File

@ -148,7 +148,9 @@ int time_independent_strcmp(char *a, char *b) {
} }
/* Given an SDS string, returns the SHA256 hex representation as a /* Given an SDS string, returns the SHA256 hex representation as a
* new SDS string. */ * new SDS string.
*
* * */
sds ACLHashPassword(unsigned char *cleartext, size_t len) { sds ACLHashPassword(unsigned char *cleartext, size_t len) {
SHA256_CTX ctx; SHA256_CTX ctx;
unsigned char hash[SHA256_BLOCK_SIZE]; unsigned char hash[SHA256_BLOCK_SIZE];
@ -232,7 +234,10 @@ void *ACLListDupSds(void *item) {
* of users (the Users global radix tree), and returns a reference to * of users (the Users global radix tree), and returns a reference to
* the structure representing the user. * the structure representing the user.
* *
* If the user with such name already exists NULL is returned. */ * If the user with such name already exists NULL is returned.
*
*
* */
user *ACLCreateUser(const char *name, size_t namelen) { user *ACLCreateUser(const char *name, size_t namelen) {
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL; if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
user *u = zmalloc(sizeof(*u)); user *u = zmalloc(sizeof(*u));
@ -255,7 +260,9 @@ user *ACLCreateUser(const char *name, size_t namelen) {
/* This function should be called when we need an unlinked "fake" user /* This function should be called when we need an unlinked "fake" user
* we can use in order to validate ACL rules or for other similar reasons. * we can use in order to validate ACL rules or for other similar reasons.
* The user will not get linked to the Users radix tree. The returned * The user will not get linked to the Users radix tree. The returned
* user should be released with ACLFreeUser() as usually. */ * user should be released with ACLFreeUser() as usually.
*
* * */
user *ACLCreateUnlinkedUser(void) { user *ACLCreateUnlinkedUser(void) {
char username[64]; char username[64];
for (int j = 0; ; j++) { for (int j = 0; ; j++) {
@ -354,9 +361,13 @@ int ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {
* but just the lowlevel bitmask. * but just the lowlevel bitmask.
* *
* If the bit overflows the user internal representation, zero is returned * If the bit overflows the user internal representation, zero is returned
* in order to disallow the execution of the command in such edge case. */ * in order to disallow the execution of the command in such edge case.
*
* command对应的bit位
* * */
int ACLGetUserCommandBit(user *u, unsigned long id) { int ACLGetUserCommandBit(user *u, unsigned long id) {
uint64_t word, bit; uint64_t word, bit;
// 计算命令在allowed_commands当中对应的bit位
if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0; if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;
return (u->allowed_commands[word] & bit) != 0; return (u->allowed_commands[word] & bit) != 0;
} }
@ -927,18 +938,22 @@ char *ACLSetUserStringError(void) {
} }
/* Initialize the default user, that will always exist for all the process /* Initialize the default user, that will always exist for all the process
* lifetime. */ * lifetime.
*
* */
void ACLInitDefaultUser(void) { void ACLInitDefaultUser(void) {
DefaultUser = ACLCreateUser("default",7); DefaultUser = ACLCreateUser("default",7);
ACLSetUser(DefaultUser,"+@all",-1); ACLSetUser(DefaultUser,"+@all",-1); // 默认用户赋予所有命令的权限
ACLSetUser(DefaultUser,"~*",-1); ACLSetUser(DefaultUser,"~*",-1); // 可以操作任何key
ACLSetUser(DefaultUser,"on",-1); ACLSetUser(DefaultUser,"on",-1); // 默认开启
ACLSetUser(DefaultUser,"nopass",-1); ACLSetUser(DefaultUser,"nopass",-1);// 默认不需要密码
} }
/* Initialization of the ACL subsystem. */ /* Initialization of the ACL subsystem.
* ACL子系统
* */
void ACLInit(void) { void ACLInit(void) {
Users = raxNew(); Users = raxNew(); // 初始化用户信息
UsersToLoad = listCreate(); UsersToLoad = listCreate();
ACLLog = listCreate(); ACLLog = listCreate();
ACLInitDefaultUser(); ACLInitDefaultUser();
@ -1056,7 +1071,9 @@ user *ACLGetUserByName(const char *name, size_t namelen) {
* ACL_DENIED_CMD or ACL_DENIED_KEY is returned: the first in case the * ACL_DENIED_CMD or ACL_DENIED_KEY is returned: the first in case the
* command cannot be executed because the user is not allowed to run such * command cannot be executed because the user is not allowed to run such
* command, the second if the command is denied because the user is trying * command, the second if the command is denied because the user is trying
* to access keys that are not among the specified patterns. */ * to access keys that are not among the specified patterns.
*
* * */
int ACLCheckCommandPerm(client *c, int *keyidxptr) { int ACLCheckCommandPerm(client *c, int *keyidxptr) {
user *u = c->user; user *u = c->user;
uint64_t id = c->cmd->id; uint64_t id = c->cmd->id;
@ -1069,7 +1086,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
c->cmd->proc != authCommand) c->cmd->proc != authCommand)
{ {
/* If the bit is not set we have to check further, in case the /* If the bit is not set we have to check further, in case the
* command is allowed just with that specific subcommand. */ * command is allowed just with that specific subcommand.
*
* */
if (ACLGetUserCommandBit(u,id) == 0) { if (ACLGetUserCommandBit(u,id) == 0) {
/* Check if the subcommand matches. */ /* Check if the subcommand matches. */
if (c->argc < 2 || if (c->argc < 2 ||
@ -1151,7 +1170,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
* *
* When an error is detected and C_ERR is returned, the function populates * When an error is detected and C_ERR is returned, the function populates
* by reference (if not set to NULL) the argc_err argument with the index * by reference (if not set to NULL) the argc_err argument with the index
* of the argv vector that caused the error. */ * of the argv vector that caused the error.
* user配置的ACL信息
* * * */
int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) { int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
if (argc < 2 || strcasecmp(argv[0],"user")) { if (argc < 2 || strcasecmp(argv[0],"user")) {
if (argc_err) *argc_err = 0; if (argc_err) *argc_err = 0;
@ -1183,7 +1204,9 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
/* This function will load the configured users appended to the server /* This function will load the configured users appended to the server
* configuration via ACLAppendUserForLoading(). On loading errors it will * configuration via ACLAppendUserForLoading(). On loading errors it will
* log an error and return C_ERR, otherwise C_OK will be returned. */ * log an error and return C_ERR, otherwise C_OK will be returned.
* user配置项中读取ACl信息
* * */
int ACLLoadConfiguredUsers(void) { int ACLLoadConfiguredUsers(void) {
listIter li; listIter li;
listNode *ln; listNode *ln;
@ -1192,11 +1215,12 @@ int ACLLoadConfiguredUsers(void) {
sds *aclrules = listNodeValue(ln); sds *aclrules = listNodeValue(ln);
sds username = aclrules[0]; sds username = aclrules[0];
// 检查ACL用户名当中是否存在空格
if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) { if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {
serverLog(LL_WARNING,"Spaces not allowed in ACL usernames"); serverLog(LL_WARNING,"Spaces not allowed in ACL usernames");
return C_ERR; return C_ERR;
} }
// 创建ACL用户
user *u = ACLCreateUser(username,sdslen(username)); user *u = ACLCreateUser(username,sdslen(username));
if (!u) { if (!u) {
u = ACLGetUserByName(username,sdslen(username)); u = ACLGetUserByName(username,sdslen(username));
@ -1206,6 +1230,7 @@ int ACLLoadConfiguredUsers(void) {
/* Load every rule defined for this user. */ /* Load every rule defined for this user. */
for (int j = 1; aclrules[j]; j++) { for (int j = 1; aclrules[j]; j++) {
// 添加当前用户的所有属性
if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) { if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {
char *errmsg = ACLSetUserStringError(); char *errmsg = ACLSetUserStringError();
serverLog(LL_WARNING,"Error loading ACL rule '%s' for " serverLog(LL_WARNING,"Error loading ACL rule '%s' for "
@ -1216,7 +1241,9 @@ int ACLLoadConfiguredUsers(void) {
} }
/* Having a disabled user in the configuration may be an error, /* Having a disabled user in the configuration may be an error,
* warn about it without returning any error to the caller. */ * warn about it without returning any error to the caller.
*
* */
if (u->flags & USER_FLAG_DISABLED) { if (u->flags & USER_FLAG_DISABLED) {
serverLog(LL_NOTICE, "The user '%s' is disabled (there is no " serverLog(LL_NOTICE, "The user '%s' is disabled (there is no "
"'on' modifier in the user description). Make " "'on' modifier in the user description). Make "
@ -1249,7 +1276,10 @@ int ACLLoadConfiguredUsers(void) {
* *
* At the end of the process, if no errors were found in the whole file then * At the end of the process, if no errors were found in the whole file then
* NULL is returned. Otherwise an SDS string describing in a single line * NULL is returned. Otherwise an SDS string describing in a single line
* a description of all the issues found is returned. */ * a description of all the issues found is returned.
*
* aclfile配置的文件里面读取ACL信息
* */
sds ACLLoadFromFile(const char *filename) { sds ACLLoadFromFile(const char *filename) {
FILE *fp; FILE *fp;
char buf[1024]; char buf[1024];
@ -1466,7 +1496,9 @@ cleanup:
* loaded, and we are ready to start, in order to load the ACLs either from * loaded, and we are ready to start, in order to load the ACLs either from
* the pending list of users defined in redis.conf, or from the ACL file. * the pending list of users defined in redis.conf, or from the ACL file.
* The function will just exit with an error if the user is trying to mix * The function will just exit with an error if the user is trying to mix
* both the loading methods. */ * both the loading methods.
* redis.conf配置项aclfile对应文件里面的ACL权限信息
* */
void ACLLoadUsersAtStartup(void) { void ACLLoadUsersAtStartup(void) {
if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) { if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
serverLog(LL_WARNING, serverLog(LL_WARNING,
@ -1477,7 +1509,7 @@ void ACLLoadUsersAtStartup(void) {
"directly in your redis.conf, but not both."); "directly in your redis.conf, but not both.");
exit(1); exit(1);
} }
// 加载user配置的ACL信息
if (ACLLoadConfiguredUsers() == C_ERR) { if (ACLLoadConfiguredUsers() == C_ERR) {
serverLog(LL_WARNING, serverLog(LL_WARNING,
"Critical error while loading ACLs. Exiting."); "Critical error while loading ACLs. Exiting.");

View File

@ -1872,7 +1872,9 @@ void processInputBuffer(client *c) {
break; break;
} }
/* We are finally ready to execute the command. */ /* We are finally ready to execute the command.
*
* */
if (processCommandAndResetClient(c) == C_ERR) { if (processCommandAndResetClient(c) == C_ERR) {
/* If the client is no longer valid, we avoid exiting this /* If the client is no longer valid, we avoid exiting this
* loop and trimming the client buffer later. So we return * loop and trimming the client buffer later. So we return

View File

@ -3418,7 +3418,9 @@ int processCommand(client *c) {
/* The QUIT command is handled separately. Normal command procs will /* The QUIT command is handled separately. Normal command procs will
* go through checking for replication and QUIT will cause trouble * go through checking for replication and QUIT will cause trouble
* when FORCE_REPLICATION is enabled and would be implemented in * when FORCE_REPLICATION is enabled and would be implemented in
* a regular command proc. */ * a regular command proc.
* quit命令
* * */
if (!strcasecmp(c->argv[0]->ptr,"quit")) { if (!strcasecmp(c->argv[0]->ptr,"quit")) {
addReply(c,shared.ok); addReply(c,shared.ok);
c->flags |= CLIENT_CLOSE_AFTER_REPLY; c->flags |= CLIENT_CLOSE_AFTER_REPLY;

View File

@ -153,7 +153,7 @@ typedef long long ustime_t; /* microsecond time type. */
/* Hash table parameters */ /* Hash table parameters */
#define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */ #define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */
/* Command flags. Please check the command table defined in the redis.c file /* Command flags. Please check the command table defined in the server.c file
* for more information about the meaning of every flag. */ * for more information about the meaning of every flag. */
#define CMD_WRITE (1ULL<<0) /* "write" flag */ #define CMD_WRITE (1ULL<<0) /* "write" flag */
#define CMD_READONLY (1ULL<<1) /* "read-only" flag */ #define CMD_READONLY (1ULL<<1) /* "read-only" flag */
@ -721,7 +721,9 @@ typedef struct readyList {
/* This structure represents a Redis user. This is useful for ACLs, the /* This structure represents a Redis user. This is useful for ACLs, the
* user is associated to the connection after the connection is authenticated. * user is associated to the connection after the connection is authenticated.
* If there is no associated user, the connection uses the default user. */ * If there is no associated user, the connection uses the default user.
* Redis ACL所有的用户不能超过1024个
* */
#define USER_COMMAND_BITS_COUNT 1024 /* The total number of command bits #define USER_COMMAND_BITS_COUNT 1024 /* The total number of command bits
in the user structure. The last valid in the user structure. The last valid
command ID we can set in the user command ID we can set in the user
@ -736,6 +738,7 @@ typedef struct readyList {
no AUTH is needed, and every no AUTH is needed, and every
connection is immediately connection is immediately
authenticated. */ authenticated. */
/* 用户信息 */
typedef struct { typedef struct {
sds name; /* The username as an SDS string. */ sds name; /* The username as an SDS string. */
uint64_t flags; /* See USER_FLAG_* */ uint64_t flags; /* See USER_FLAG_* */
@ -746,7 +749,9 @@ typedef struct {
* *
* If the bit for a given command is NOT set and the command has * If the bit for a given command is NOT set and the command has
* subcommands, Redis will also check allowed_subcommands in order to * subcommands, Redis will also check allowed_subcommands in order to
* understand if the command can be executed. */ * understand if the command can be executed.
*
* */
uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64]; uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64];
/* This array points, for each command ID (corresponding to the command /* This array points, for each command ID (corresponding to the command
@ -1428,7 +1433,7 @@ struct redisServer {
long long latency_monitor_threshold; long long latency_monitor_threshold;
dict *latency_events; dict *latency_events;
/* ACLs */ /* ACLs */
char *acl_filename; /* ACL Users file. NULL if not configured. */ char *acl_filename; /* ACL Users file. NULL if not configured.ACL文件路径需要配置已经存在的文件否则会报错启动失败 */
unsigned long acllog_max_len; /* Maximum length of the ACL LOG list. */ unsigned long acllog_max_len; /* Maximum length of the ACL LOG list. */
sds requirepass; /* Remember the cleartext password set with the sds requirepass; /* Remember the cleartext password set with the
old "requirepass" directive for backward old "requirepass" directive for backward