Get user and group names from files (#6472)
* Read user names from file * Separate file modification check * Read group names from file * Update the documentation * Use files only inside a containter * Fix the volume mounting suggestions
This commit is contained in:
parent
6469cf9272
commit
bc21588a74
|
@ -100,6 +100,8 @@ To try Netdata in a docker container, run this:
|
|||
```
|
||||
docker run -d --name=netdata \
|
||||
-p 19999:19999 \
|
||||
-v /etc/passwd:/host/etc/passwd:ro \
|
||||
-v /etc/group:/host/etc/group:ro \
|
||||
-v /proc:/host/proc:ro \
|
||||
-v /sys:/host/sys:ro \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
|
|
|
@ -499,6 +499,187 @@ static int
|
|||
all_files_len = 0,
|
||||
all_files_size = 0;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// read users and groups from files
|
||||
|
||||
struct user_or_group_id {
|
||||
avl avl;
|
||||
|
||||
union {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
} id;
|
||||
|
||||
char *name;
|
||||
|
||||
int updated;
|
||||
|
||||
struct user_or_group_id * next;
|
||||
};
|
||||
|
||||
enum user_or_group_id_type {
|
||||
USER_ID,
|
||||
GROUP_ID
|
||||
};
|
||||
|
||||
struct user_or_group_ids{
|
||||
enum user_or_group_id_type type;
|
||||
|
||||
avl_tree index;
|
||||
struct user_or_group_id *root;
|
||||
|
||||
char filename[FILENAME_MAX + 1];
|
||||
};
|
||||
|
||||
int user_id_compare(void* a, void* b) {
|
||||
if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid)
|
||||
return -1;
|
||||
|
||||
else if(((struct user_or_group_id *)a)->id.uid > ((struct user_or_group_id *)b)->id.uid)
|
||||
return 1;
|
||||
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct user_or_group_ids all_user_ids = {
|
||||
.type = USER_ID,
|
||||
|
||||
.index = {
|
||||
NULL,
|
||||
user_id_compare
|
||||
},
|
||||
|
||||
.root = NULL,
|
||||
|
||||
.filename = "",
|
||||
};
|
||||
|
||||
int group_id_compare(void* a, void* b) {
|
||||
if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid)
|
||||
return -1;
|
||||
|
||||
else if(((struct user_or_group_id *)a)->id.gid > ((struct user_or_group_id *)b)->id.gid)
|
||||
return 1;
|
||||
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct user_or_group_ids all_group_ids = {
|
||||
.type = GROUP_ID,
|
||||
|
||||
.index = {
|
||||
NULL,
|
||||
group_id_compare
|
||||
},
|
||||
|
||||
.root = NULL,
|
||||
|
||||
.filename = "",
|
||||
};
|
||||
|
||||
int file_changed(const struct stat *statbuf, struct timespec *last_modification_time) {
|
||||
if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec &&
|
||||
statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return 0;
|
||||
|
||||
last_modification_time->tv_sec = statbuf->st_mtim.tv_sec;
|
||||
last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
|
||||
struct stat statbuf;
|
||||
if(unlikely(stat(ids->filename, &statbuf)))
|
||||
return 1;
|
||||
else
|
||||
if(likely(!file_changed(&statbuf, last_modification_time))) return 0;
|
||||
|
||||
procfile *ff = procfile_open(ids->filename, " :\t", PROCFILE_FLAG_DEFAULT);
|
||||
if(unlikely(!ff)) return 1;
|
||||
|
||||
ff = procfile_readall(ff);
|
||||
if(unlikely(!ff)) return 1;
|
||||
|
||||
size_t line, lines = procfile_lines(ff);
|
||||
|
||||
for(line = 0; line < lines ;line++) {
|
||||
size_t words = procfile_linewords(ff, line);
|
||||
if(unlikely(words < 3)) continue;
|
||||
|
||||
char *name = procfile_lineword(ff, line, 0);
|
||||
if(unlikely(!name || !*name)) continue;
|
||||
|
||||
char *id_string = procfile_lineword(ff, line, 2);
|
||||
if(unlikely(!id_string || !*id_string)) continue;
|
||||
|
||||
|
||||
struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id));
|
||||
|
||||
if(ids->type == USER_ID)
|
||||
user_or_group_id->id.uid = (uid_t)str2ull(id_string);
|
||||
else
|
||||
user_or_group_id->id.gid = (uid_t)str2ull(id_string);
|
||||
|
||||
user_or_group_id->name = strdupz(name);
|
||||
user_or_group_id->updated = 1;
|
||||
|
||||
struct user_or_group_id *existing_user_id = NULL;
|
||||
|
||||
if(likely(ids->root))
|
||||
existing_user_id = (struct user_or_group_id *)avl_search(&ids->index, (avl *) user_or_group_id);
|
||||
|
||||
if(unlikely(existing_user_id)) {
|
||||
freez(existing_user_id->name);
|
||||
existing_user_id->name = user_or_group_id->name;
|
||||
existing_user_id->updated = 1;
|
||||
freez(user_or_group_id);
|
||||
}
|
||||
else {
|
||||
if(unlikely(avl_insert(&ids->index, (avl *) user_or_group_id) != (void *) user_or_group_id)) {
|
||||
error("INTERNAL ERROR: duplicate indexing of id during realloc");
|
||||
};
|
||||
|
||||
user_or_group_id->next = ids->root;
|
||||
ids->root = user_or_group_id;
|
||||
}
|
||||
}
|
||||
|
||||
procfile_close(ff);
|
||||
|
||||
// remove unused ids
|
||||
struct user_or_group_id *user_or_group_id = ids->root, *prev_user_id = NULL;
|
||||
|
||||
while(user_or_group_id) {
|
||||
if(unlikely(!user_or_group_id->updated)) {
|
||||
if(unlikely((struct user_or_group_id *)avl_remove(&ids->index, (avl *) user_or_group_id) != user_or_group_id))
|
||||
error("INTERNAL ERROR: removal of unused id from index, removed a different id");
|
||||
|
||||
if(prev_user_id)
|
||||
prev_user_id->next = user_or_group_id->next;
|
||||
else
|
||||
ids->root = user_or_group_id->next;
|
||||
|
||||
freez(user_or_group_id->name);
|
||||
freez(user_or_group_id);
|
||||
|
||||
if(prev_user_id)
|
||||
user_or_group_id = prev_user_id->next;
|
||||
else
|
||||
user_or_group_id = ids->root;
|
||||
}
|
||||
else {
|
||||
user_or_group_id->updated = 0;
|
||||
|
||||
prev_user_id = user_or_group_id;
|
||||
user_or_group_id = user_or_group_id->next;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// apps_groups.conf
|
||||
// aggregate all processes in groups, to have a limited number of dimensions
|
||||
|
@ -516,11 +697,27 @@ static struct target *get_users_target(uid_t uid) {
|
|||
snprintfz(w->id, MAX_NAME, "%u", uid);
|
||||
w->idhash = simple_hash(w->id);
|
||||
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if(!pw || !pw->pw_name || !*pw->pw_name)
|
||||
snprintfz(w->name, MAX_NAME, "%u", uid);
|
||||
else
|
||||
snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
|
||||
struct user_or_group_id user_id_to_find, *user_or_group_id = NULL;
|
||||
user_id_to_find.id.uid = uid;
|
||||
|
||||
if(*netdata_configured_host_prefix) {
|
||||
static struct timespec last_passwd_modification_time;
|
||||
int ret = read_user_or_group_ids(&all_user_ids, &last_passwd_modification_time);
|
||||
|
||||
if(likely(!ret && all_user_ids.index.root))
|
||||
user_or_group_id = (struct user_or_group_id *)avl_search(&all_user_ids.index, (avl *) &user_id_to_find);
|
||||
}
|
||||
|
||||
if(user_or_group_id && user_or_group_id->name && *user_or_group_id->name) {
|
||||
snprintfz(w->name, MAX_NAME, "%s", user_or_group_id->name);
|
||||
}
|
||||
else {
|
||||
struct passwd *pw = getpwuid(uid);
|
||||
if(!pw || !pw->pw_name || !*pw->pw_name)
|
||||
snprintfz(w->name, MAX_NAME, "%u", uid);
|
||||
else
|
||||
snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
|
||||
}
|
||||
|
||||
netdata_fix_chart_name(w->name);
|
||||
|
||||
|
@ -548,11 +745,27 @@ struct target *get_groups_target(gid_t gid)
|
|||
snprintfz(w->id, MAX_NAME, "%u", gid);
|
||||
w->idhash = simple_hash(w->id);
|
||||
|
||||
struct group *gr = getgrgid(gid);
|
||||
if(!gr || !gr->gr_name || !*gr->gr_name)
|
||||
snprintfz(w->name, MAX_NAME, "%u", gid);
|
||||
else
|
||||
snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
|
||||
struct user_or_group_id group_id_to_find, *group_id = NULL;
|
||||
group_id_to_find.id.gid = gid;
|
||||
|
||||
if(*netdata_configured_host_prefix) {
|
||||
static struct timespec last_group_modification_time;
|
||||
int ret = read_user_or_group_ids(&all_group_ids, &last_group_modification_time);
|
||||
|
||||
if(likely(!ret && all_group_ids.index.root))
|
||||
group_id = (struct user_or_group_id *)avl_search(&all_group_ids.index, (avl *) &group_id_to_find);
|
||||
}
|
||||
|
||||
if(group_id && group_id->name && *group_id->name) {
|
||||
snprintfz(w->name, MAX_NAME, "%s", group_id->name);
|
||||
}
|
||||
else {
|
||||
struct group *gr = getgrgid(gid);
|
||||
if(!gr || !gr->gr_name || !*gr->gr_name)
|
||||
snprintfz(w->name, MAX_NAME, "%u", gid);
|
||||
else
|
||||
snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
|
||||
}
|
||||
|
||||
netdata_fix_chart_name(w->name);
|
||||
|
||||
|
@ -3826,6 +4039,12 @@ int main(int argc, char **argv) {
|
|||
|
||||
info("started on pid %d", getpid());
|
||||
|
||||
snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix);
|
||||
debug_log("passwd file: '%s'", all_user_ids.filename);
|
||||
|
||||
snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix);
|
||||
debug_log("group file: '%s'", all_group_ids.filename);
|
||||
|
||||
#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
|
||||
all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,8 @@ This is good for an internal network or to quickly analyse a host.
|
|||
```bash
|
||||
docker run -d --name=netdata \
|
||||
-p 19999:19999 \
|
||||
-v /etc/passwd:/host/etc/passwd:ro \
|
||||
-v /etc/group:/host/etc/group:ro \
|
||||
-v /proc:/host/proc:ro \
|
||||
-v /sys:/host/sys:ro \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
|
@ -47,11 +49,15 @@ services:
|
|||
security_opt:
|
||||
- apparmor:unconfined
|
||||
volumes:
|
||||
- /etc/passwd:/host/etc/passwd:ro
|
||||
- /etc/group:/host/etc/group:ro
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
```
|
||||
|
||||
If you don't want to use the apps.plugin functionality, you can remove the mounts of `/etc/passwd` and `/etc/group` (they are used to get proper user and group names for the monitored host) to get slightly better security.
|
||||
|
||||
### Docker container names resolution
|
||||
|
||||
If you want to have your container names resolved by netdata, you need to do two things:
|
||||
|
@ -132,6 +138,8 @@ services:
|
|||
security_opt:
|
||||
- apparmor:unconfined
|
||||
volumes:
|
||||
- /etc/passwd:/host/etc/passwd:ro
|
||||
- /etc/group:/host/etc/group:ro
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
|
Loading…
Reference in New Issue