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