@cryptotaxi247 / netdata-1 / commits / bc21588a7

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

Vladimir Kobal committed Jul 18, 2019 at 20:38 UTC bc21588a7439d9e4ca89e622d080d91b3bb20c73
3 files changed +239 -10
README.md
+2
@@ -100,6 +100,8 @@ To try Netdata in a docker container, run this:
100 ```
101 docker run -d --name=netdata \
102 -p 19999:19999 \
103 + -v /etc/passwd:/host/etc/passwd:ro \
104 + -v /etc/group:/host/etc/group:ro \
105 -v /proc:/host/proc:ro \
106 -v /sys:/host/sys:ro \
107 -v /var/run/docker.sock:/var/run/docker.sock:ro \
collectors/apps.plugin/apps_plugin.c
+229 -10
@@ -499,6 +499,187 @@ static int
499 all_files_len = 0,
500 all_files_size = 0;
501
502 +// ----------------------------------------------------------------------------
503 +// read users and groups from files
504 +
505 +struct user_or_group_id {
506 + avl avl;
507 +
508 + union {
509 + uid_t uid;
510 + gid_t gid;
511 + } id;
512 +
513 + char *name;
514 +
515 + int updated;
516 +
517 + struct user_or_group_id * next;
518 +};
519 +
520 +enum user_or_group_id_type {
521 + USER_ID,
522 + GROUP_ID
523 +};
524 +
525 +struct user_or_group_ids{
526 + enum user_or_group_id_type type;
527 +
528 + avl_tree index;
529 + struct user_or_group_id *root;
530 +
531 + char filename[FILENAME_MAX + 1];
532 +};
533 +
534 +int user_id_compare(void* a, void* b) {
535 + if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid)
536 + return -1;
537 +
538 + else if(((struct user_or_group_id *)a)->id.uid > ((struct user_or_group_id *)b)->id.uid)
539 + return 1;
540 +
541 + else
542 + return 0;
543 +}
544 +
545 +struct user_or_group_ids all_user_ids = {
546 + .type = USER_ID,
547 +
548 + .index = {
549 + NULL,
550 + user_id_compare
551 + },
552 +
553 + .root = NULL,
554 +
555 + .filename = "",
556 +};
557 +
558 +int group_id_compare(void* a, void* b) {
559 + if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid)
560 + return -1;
561 +
562 + else if(((struct user_or_group_id *)a)->id.gid > ((struct user_or_group_id *)b)->id.gid)
563 + return 1;
564 +
565 + else
566 + return 0;
567 +}
568 +
569 +struct user_or_group_ids all_group_ids = {
570 + .type = GROUP_ID,
571 +
572 + .index = {
573 + NULL,
574 + group_id_compare
575 + },
576 +
577 + .root = NULL,
578 +
579 + .filename = "",
580 +};
581 +
582 +int file_changed(const struct stat *statbuf, struct timespec *last_modification_time) {
583 + if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec &&
584 + statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return 0;
585 +
586 + last_modification_time->tv_sec = statbuf->st_mtim.tv_sec;
587 + last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec;
588 +
589 + return 1;
590 +}
591 +
592 +int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
593 + struct stat statbuf;
594 + if(unlikely(stat(ids->filename, &statbuf)))
595 + return 1;
596 + else
597 + if(likely(!file_changed(&statbuf, last_modification_time))) return 0;
598 +
599 + procfile *ff = procfile_open(ids->filename, " :\t", PROCFILE_FLAG_DEFAULT);
600 + if(unlikely(!ff)) return 1;
601 +
602 + ff = procfile_readall(ff);
603 + if(unlikely(!ff)) return 1;
604 +
605 + size_t line, lines = procfile_lines(ff);
606 +
607 + for(line = 0; line < lines ;line++) {
608 + size_t words = procfile_linewords(ff, line);
609 + if(unlikely(words < 3)) continue;
610 +
611 + char *name = procfile_lineword(ff, line, 0);
612 + if(unlikely(!name || !*name)) continue;
613 +
614 + char *id_string = procfile_lineword(ff, line, 2);
615 + if(unlikely(!id_string || !*id_string)) continue;
616 +
617 +
618 + struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id));
619 +
620 + if(ids->type == USER_ID)
621 + user_or_group_id->id.uid = (uid_t)str2ull(id_string);
622 + else
623 + user_or_group_id->id.gid = (uid_t)str2ull(id_string);
624 +
625 + user_or_group_id->name = strdupz(name);
626 + user_or_group_id->updated = 1;
627 +
628 + struct user_or_group_id *existing_user_id = NULL;
629 +
630 + if(likely(ids->root))
631 + existing_user_id = (struct user_or_group_id *)avl_search(&ids->index, (avl *) user_or_group_id);
632 +
633 + if(unlikely(existing_user_id)) {
634 + freez(existing_user_id->name);
635 + existing_user_id->name = user_or_group_id->name;
636 + existing_user_id->updated = 1;
637 + freez(user_or_group_id);
638 + }
639 + else {
640 + if(unlikely(avl_insert(&ids->index, (avl *) user_or_group_id) != (void *) user_or_group_id)) {
641 + error("INTERNAL ERROR: duplicate indexing of id during realloc");
642 + };
643 +
644 + user_or_group_id->next = ids->root;
645 + ids->root = user_or_group_id;
646 + }
647 + }
648 +
649 + procfile_close(ff);
650 +
651 + // remove unused ids
652 + struct user_or_group_id *user_or_group_id = ids->root, *prev_user_id = NULL;
653 +
654 + while(user_or_group_id) {
655 + if(unlikely(!user_or_group_id->updated)) {
656 + if(unlikely((struct user_or_group_id *)avl_remove(&ids->index, (avl *) user_or_group_id) != user_or_group_id))
657 + error("INTERNAL ERROR: removal of unused id from index, removed a different id");
658 +
659 + if(prev_user_id)
660 + prev_user_id->next = user_or_group_id->next;
661 + else
662 + ids->root = user_or_group_id->next;
663 +
664 + freez(user_or_group_id->name);
665 + freez(user_or_group_id);
666 +
667 + if(prev_user_id)
668 + user_or_group_id = prev_user_id->next;
669 + else
670 + user_or_group_id = ids->root;
671 + }
672 + else {
673 + user_or_group_id->updated = 0;
674 +
675 + prev_user_id = user_or_group_id;
676 + user_or_group_id = user_or_group_id->next;
677 + }
678 + }
679 +
680 + return 0;
681 +}
682 +
683 // ----------------------------------------------------------------------------
684 // apps_groups.conf
685 // 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) {
697 snprintfz(w->id, MAX_NAME, "%u", uid);
698 w->idhash = simple_hash(w->id);
699
519 - struct passwd *pw = getpwuid(uid);
520 - if(!pw || !pw->pw_name || !*pw->pw_name)
521 - snprintfz(w->name, MAX_NAME, "%u", uid);
522 - else
523 - snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
700 + struct user_or_group_id user_id_to_find, *user_or_group_id = NULL;
701 + user_id_to_find.id.uid = uid;
702 +
703 + if(*netdata_configured_host_prefix) {
704 + static struct timespec last_passwd_modification_time;
705 + int ret = read_user_or_group_ids(&all_user_ids, &last_passwd_modification_time);
706 +
707 + if(likely(!ret && all_user_ids.index.root))
708 + user_or_group_id = (struct user_or_group_id *)avl_search(&all_user_ids.index, (avl *) &user_id_to_find);
709 + }
710 +
711 + if(user_or_group_id && user_or_group_id->name && *user_or_group_id->name) {
712 + snprintfz(w->name, MAX_NAME, "%s", user_or_group_id->name);
713 + }
714 + else {
715 + struct passwd *pw = getpwuid(uid);
716 + if(!pw || !pw->pw_name || !*pw->pw_name)
717 + snprintfz(w->name, MAX_NAME, "%u", uid);
718 + else
719 + snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
720 + }
721
722 netdata_fix_chart_name(w->name);
723
@@ -548,11 +745,27 @@ struct target *get_groups_target(gid_t gid)
745 snprintfz(w->id, MAX_NAME, "%u", gid);
746 w->idhash = simple_hash(w->id);
747
551 - struct group *gr = getgrgid(gid);
552 - if(!gr || !gr->gr_name || !*gr->gr_name)
553 - snprintfz(w->name, MAX_NAME, "%u", gid);
554 - else
555 - snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
748 + struct user_or_group_id group_id_to_find, *group_id = NULL;
749 + group_id_to_find.id.gid = gid;
750 +
751 + if(*netdata_configured_host_prefix) {
752 + static struct timespec last_group_modification_time;
753 + int ret = read_user_or_group_ids(&all_group_ids, &last_group_modification_time);
754 +
755 + if(likely(!ret && all_group_ids.index.root))
756 + group_id = (struct user_or_group_id *)avl_search(&all_group_ids.index, (avl *) &group_id_to_find);
757 + }
758 +
759 + if(group_id && group_id->name && *group_id->name) {
760 + snprintfz(w->name, MAX_NAME, "%s", group_id->name);
761 + }
762 + else {
763 + struct group *gr = getgrgid(gid);
764 + if(!gr || !gr->gr_name || !*gr->gr_name)
765 + snprintfz(w->name, MAX_NAME, "%u", gid);
766 + else
767 + snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
768 + }
769
770 netdata_fix_chart_name(w->name);
771
@@ -3826,6 +4039,12 @@ int main(int argc, char **argv) {
4039
4040 info("started on pid %d", getpid());
4041
4042 + snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix);
4043 + debug_log("passwd file: '%s'", all_user_ids.filename);
4044 +
4045 + snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix);
4046 + debug_log("group file: '%s'", all_group_ids.filename);
4047 +
4048 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
4049 all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
4050 #endif
packaging/docker/README.md
+8
@@ -24,6 +24,8 @@ This is good for an internal network or to quickly analyse a host.
24 ```bash
25 docker run -d --name=netdata \
26 -p 19999:19999 \
27 + -v /etc/passwd:/host/etc/passwd:ro \
28 + -v /etc/group:/host/etc/group:ro \
29 -v /proc:/host/proc:ro \
30 -v /sys:/host/sys:ro \
31 -v /var/run/docker.sock:/var/run/docker.sock:ro \
@@ -47,11 +49,15 @@ services:
49 security_opt:
50 - apparmor:unconfined
51 volumes:
52 + - /etc/passwd:/host/etc/passwd:ro
53 + - /etc/group:/host/etc/group:ro
54 - /proc:/host/proc:ro
55 - /sys:/host/sys:ro
56 - /var/run/docker.sock:/var/run/docker.sock:ro
57 ```
58
59 +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.
60 +
61 ### Docker container names resolution
62
63 If you want to have your container names resolved by netdata, you need to do two things:
@@ -132,6 +138,8 @@ services:
138 security_opt:
139 - apparmor:unconfined
140 volumes:
141 + - /etc/passwd:/host/etc/passwd:ro
142 + - /etc/group:/host/etc/group:ro
143 - /proc:/host/proc:ro
144 - /sys:/host/sys:ro
145 - /var/run/docker.sock:/var/run/docker.sock:ro