master
c 139 lines 5.35 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "api_v1_calls.h"
4
5 char *api_secret;
6
7 static char *get_mgmt_api_key(void) {
8 char filename[FILENAME_MAX + 1];
9 snprintfz(filename, FILENAME_MAX, "%s/netdata.api.key", netdata_configured_varlib_dir);
10 const char *api_key_filename = inicfg_get_filename(&netdata_config, CONFIG_SECTION_REGISTRY, "netdata management api key file", filename);
11 static char guid[GUID_LEN + 1] = "";
12
13 if(likely(guid[0]))
14 return guid;
15
16 // read it from disk
17 #ifdef O_NOFOLLOW
18 int fd = open(api_key_filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
19 #else
20 int fd = open(api_key_filename, O_RDONLY | O_CLOEXEC);
21 #endif
22 if(fd != -1) {
23 struct stat st;
24 if(fstat(fd, &st) != 0 || !S_ISREG(st.st_mode))
25 netdata_log_error("Management API key file '%s' is not a regular file, regenerating.", api_key_filename);
26 else {
27 char buf[GUID_LEN + 1];
28 if(read(fd, buf, GUID_LEN) != GUID_LEN)
29 netdata_log_error("Failed to read management API key from '%s'", api_key_filename);
30 else {
31 buf[GUID_LEN] = '\0';
32 if(regenerate_guid(buf, guid) == -1) {
33 netdata_log_error("Failed to validate management API key '%s' from '%s'.",
34 buf, api_key_filename);
35
36 guid[0] = '\0';
37 }
38 }
39 }
40 close(fd);
41 }
42
43 // generate a new one?
44 if(!guid[0]) {
45 nd_uuid_t uuid;
46
47 uuid_generate(uuid);
48 uuid_unparse_lower(uuid, guid);
49 guid[GUID_LEN] = '\0';
50
51 // save it
52 #ifdef O_NOFOLLOW
53 struct stat st;
54 // O_RDWR avoids blocking on FIFOs (O_WRONLY blocks until a reader arrives).
55 // O_TRUNC is omitted so truncation only happens after fstat() confirms a regular file.
56 fd = open(api_key_filename, O_RDWR|O_CREAT|O_CLOEXEC|O_NOFOLLOW, 0600);
57 if(fd == -1) {
58 netdata_log_error("Cannot create unique management API key file '%s'. Please adjust config parameter 'netdata management api key file' to a proper path and file.", api_key_filename);
59 goto temp_key;
60 }
61
62 if(fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) {
63 netdata_log_error("Management API key file '%s' is not a regular file.", api_key_filename);
64 close(fd);
65 goto temp_key;
66 }
67
68 if(ftruncate(fd, 0) != 0) {
69 netdata_log_error("Cannot truncate management API key file '%s'.", api_key_filename);
70 close(fd);
71 goto temp_key;
72 }
73
74 if(write(fd, guid, GUID_LEN) != GUID_LEN) {
75 netdata_log_error("Cannot write the unique management API key file '%s'. Please adjust config parameter 'netdata management api key file' to a proper path and file with enough space left.", api_key_filename);
76 close(fd);
77 goto temp_key;
78 }
79
80 close(fd);
81 #else
82 // Without O_NOFOLLOW: write to a uniquely named temp file then rename atomically.
83 // Use mkstemp() so the temporary filename is not predictable.
84 // rename() replaces the destination entry without following symlinks there,
85 // so a symlink planted at api_key_filename cannot redirect truncation to another file.
86 char tmp_filename[FILENAME_MAX + 1];
87 snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp.XXXXXX", api_key_filename);
88
89 fd = mkstemp(tmp_filename);
90 if(fd == -1) {
91 netdata_log_error("Cannot create temporary management API key file '%s'. Please adjust config parameter 'netdata management api key file' to a proper path and file.", tmp_filename);
92 goto temp_key;
93 }
94
95 if(write(fd, guid, GUID_LEN) != GUID_LEN) {
96 netdata_log_error("Cannot write the unique management API key file '%s'. Please adjust config parameter 'netdata management api key file' to a proper path and file with enough space left.", tmp_filename);
97 close(fd);
98 unlink(tmp_filename);
99 goto temp_key;
100 }
101 close(fd);
102
103 if(rename(tmp_filename, api_key_filename) != 0) {
104 netdata_log_error("Cannot rename temporary API key file '%s' to '%s'. Please adjust config parameter 'netdata management api key file' to a proper path and file.", tmp_filename, api_key_filename);
105 unlink(tmp_filename);
106 goto temp_key;
107 }
108 #endif
109 }
110
111 return guid;
112
113 temp_key:
114 netdata_log_info("You can still continue to use the alarm management API using the authorization token %s during this Netdata session only.", guid);
115 return guid;
116 }
117
118 void api_v1_management_init(void) {
119 api_secret = get_mgmt_api_key();
120 }
121
122 #define HLT_MGM "manage/health"
123 int api_v1_manage(RRDHOST *host, struct web_client *w, char *url) {
124 const char *haystack = buffer_tostring(w->url_path_decoded);
125 char *needle;
126
127 buffer_flush(w->response.data);
128
129 if ((needle = strstr(haystack, HLT_MGM)) == NULL) {
130 buffer_strcat(w->response.data, "Invalid management request. Curently only 'health' is supported.");
131 return HTTP_RESP_NOT_FOUND;
132 }
133 needle += strlen(HLT_MGM);
134 if (*needle != '\0') {
135 buffer_strcat(w->response.data, "Invalid management request. Currently only 'health' is supported.");
136 return HTTP_RESP_NOT_FOUND;
137 }
138 return web_client_api_request_v1_mgmt_health(host, w, url);
139 }