1 #include "../uwsgi.h"
2 
3 
4 extern struct uwsgi_server uwsgi;
5 
6 #ifndef CLONE_NEWUTS
7 #define CLONE_NEWUTS 0x04000000
8 #endif
9 
10 #ifndef CLONE_NEWPID
11 #define CLONE_NEWPID 0x20000000
12 #endif
13 
14 #ifndef CLONE_NEWIPC
15 #define CLONE_NEWIPC 0x08000000
16 #endif
17 
18 #ifndef CLONE_NEWNET
19 #define CLONE_NEWNET 0x40000000
20 #endif
21 
uwsgi_is_a_keep_mount(char * mp)22 int uwsgi_is_a_keep_mount(char *mp) {
23 	struct uwsgi_string_list *usl = uwsgi.ns_keep_mount;
24 	while(usl) {
25 		char *colon = strchr(usl->value, ':');
26 		if (colon) {
27 			if (!strcmp(colon+1, mp)) {
28 				return 1;
29 			}
30 		}
31 		else {
32 			// slip first part
33 			if (!uwsgi_startswith(usl->value, uwsgi.ns, strlen(uwsgi.ns))) {
34 				char *skipped = usl->value + strlen(uwsgi.ns);
35 				if (uwsgi.ns[strlen(uwsgi.ns)-1] == '/') {
36 					skipped--;
37 				}
38 				if (!strcmp(skipped, mp)) {
39 					return 1;
40 				}
41 			}
42 			else {
43 				if (!strcmp(usl->value, mp)) {
44 					return 1;
45 				}
46 			}
47 		}
48 		usl = usl->next;
49 	}
50 
51 	return 0;
52 
53 }
54 
uwsgi_ns_start(void * v_argv)55 static int uwsgi_ns_start(void *v_argv) {
56 	uwsgi_start(v_argv);
57 	return uwsgi_run();
58 }
59 
linux_namespace_start(void * argv)60 void linux_namespace_start(void *argv) {
61 	for (;;) {
62 		char stack[PTHREAD_STACK_MIN];
63 		int waitpid_status;
64 		char *pid_str = NULL;
65 		uwsgi_log("*** jailing uWSGI in %s ***\n", uwsgi.ns);
66 		int clone_flags = SIGCHLD | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNS;
67 		if (uwsgi.ns_net) {
68 			clone_flags |= CLONE_NEWNET;
69 		}
70 		pid_t pid = clone(uwsgi_ns_start, stack + PTHREAD_STACK_MIN, clone_flags, (void *) argv);
71 		if (pid == -1) {
72 			uwsgi_error("clone()");
73 			exit(1);
74 		}
75 #if defined(MS_REC) && defined(MS_PRIVATE)
76 		if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
77 			uwsgi_error("mount()");
78 			exit(1);
79 		}
80 #endif
81 		pid_str = uwsgi_num2str((int) pid);
82 		// run the post-jail scripts
83 		if (setenv("UWSGI_JAIL_PID", pid_str, 1)) {
84 			uwsgi_error("setenv()");
85 		}
86 		free(pid_str);
87 		uwsgi_hooks_run(uwsgi.hook_post_jail, "post-jail", 1);
88         	struct uwsgi_string_list *usl = uwsgi.exec_post_jail;
89         	while(usl) {
90                 	uwsgi_log("running \"%s\" (post-jail)...\n", usl->value);
91                 	int ret = uwsgi_run_command_and_wait(NULL, usl->value);
92                 	if (ret != 0) {
93                         	uwsgi_log("command \"%s\" exited with non-zero code: %d\n", usl->value, ret);
94                         	exit(1);
95                 	}
96                 	usl = usl->next;
97         	}
98 
99 		uwsgi_foreach(usl, uwsgi.call_post_jail) {
100                         if (uwsgi_call_symbol(usl->value)) {
101                                 uwsgi_log("unable to call function \"%s\"\n", usl->value);
102 				exit(1);
103                         }
104                 }
105 
106 		uwsgi_log("waiting for jailed master (pid: %d) death...\n", (int) pid);
107 		pid = waitpid(pid, &waitpid_status, 0);
108 		if (pid < 0) {
109 			uwsgi_error("waitpid()");
110 			exit(1);
111 		}
112 
113 		// in Linux this is reliable
114 		if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == 1) {
115 			exit(1);
116 		}
117 		else if (uwsgi.exit_on_reload && WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == 0) {
118 			uwsgi_log("jailed master process exited and exit-on-reload is enabled, shutting down\n");
119 			exit(0);
120 		}
121 
122 
123 		uwsgi_log("pid %d ended. Respawning...\n", (int) pid);
124 	}
125 
126 	// never here
127 }
128 
129 
130 
linux_namespace_jail()131 void linux_namespace_jail() {
132 
133 	char *ns_tmp_mountpoint = NULL, *ns_tmp_mountpoint2 = NULL;
134 
135 	if (getpid() != 1) {
136 		uwsgi_log("your kernel does not support linux pid namespace\n");
137 		exit(1);
138 	}
139 
140 	char *ns_hostname = strchr(uwsgi.ns, ':');
141 	if (ns_hostname) {
142 		ns_hostname[0] = 0;
143 		ns_hostname++;
144 		if (sethostname(ns_hostname, strlen(ns_hostname))) {
145 			uwsgi_error("sethostname()");
146 		}
147 	}
148 
149 	FILE *procmounts;
150 	char line[1024];
151 	int unmounted = 1;
152 	char *delim0, *delim1;
153 
154 	if (chdir(uwsgi.ns)) {
155 		uwsgi_error("chdir()");
156 		exit(1);
157 	}
158 
159 	if (strcmp(uwsgi.ns, "/")) {
160 		ns_tmp_mountpoint = uwsgi_concat2(uwsgi.ns, "/.uwsgi_ns_tmp_mountpoint");
161 		if (mkdir(ns_tmp_mountpoint, S_IRWXU) < 0) {
162 			uwsgi_error("mkdir() ns_tmp_mountpoint");
163 			exit(1);
164 		}
165 
166 		ns_tmp_mountpoint2 = uwsgi_concat2(ns_tmp_mountpoint, "/.uwsgi_ns_tmp_mountpoint");
167 		if (mkdir(ns_tmp_mountpoint2, S_IRWXU) < 0) {
168 			uwsgi_error("mkdir() ns_tmp_mountpoint2");
169 			exit(1);
170                 }
171 
172 		if (mount(uwsgi.ns, ns_tmp_mountpoint, "none", MS_BIND, NULL)) {
173 			uwsgi_error("mount()");
174 		}
175 		if (chdir(ns_tmp_mountpoint)) {
176 			uwsgi_error("chdir()");
177 		}
178 
179 		if (pivot_root(".", ns_tmp_mountpoint2)) {
180 			uwsgi_error("pivot_root()");
181 			exit(1);
182 		}
183 
184 
185 
186 		if (chdir("/")) {
187 			uwsgi_error("chdir()");
188 			exit(1);
189 		}
190 
191 	}
192 
193 	uwsgi_log("remounting /proc\n");
194 	if (mount("proc", "/proc", "proc", 0, NULL)) {
195 		uwsgi_error("mount()");
196 		exit(1);
197 	}
198 
199 	struct uwsgi_string_list *usl = uwsgi.ns_keep_mount;
200 	while(usl) {
201 		// bind mounting keep-mount items
202 		char *keep_mountpoint = usl->value;
203 		char *destination = strchr(usl->value, ':');
204 		if (destination) {
205 			keep_mountpoint = uwsgi_concat2n(usl->value, destination - usl->value, "", 0);
206 		}
207 		char *ks = uwsgi_concat2("/.uwsgi_ns_tmp_mountpoint", keep_mountpoint);
208 		if (!destination) {
209 			destination = usl->value;
210 			// skip first part of the name if under the jail
211 			if (!uwsgi_startswith(destination, uwsgi.ns, strlen(uwsgi.ns))) {
212 				if (uwsgi.ns[strlen(uwsgi.ns)-1] == '/') {
213 					destination += strlen(uwsgi.ns)-1;
214 				}
215 				else {
216 					destination += strlen(uwsgi.ns);
217 				}
218 			}
219 		}
220 		else {
221 			free(keep_mountpoint);
222 			destination++;
223 		}
224 
225 		uwsgi_log("remounting %s to %s\n", ks+25, destination);
226 		if (mount(ks, destination, "none", MS_BIND, NULL)) {
227 			uwsgi_error("mount()");
228 		}
229 		free(ks);
230 		usl = usl->next;
231 	}
232 
233 	while (unmounted) {
234 
235 		unmounted = 0;
236 		procmounts = fopen("/proc/self/mounts", "r");
237 		if (!procmounts)
238 			break;
239 		while (fgets(line, 1024, procmounts) != NULL) {
240 			delim0 = strchr(line, ' ');
241 			if (!delim0)
242 				continue;
243 			delim0++;
244 			delim1 = strchr(delim0, ' ');
245 			if (!delim1)
246 				continue;
247 			*delim1 = 0;
248 			// and now check for keep-mounts
249 			if (uwsgi_is_a_keep_mount(delim0)) continue;
250 			if (!strcmp(delim0, "/") || !strcmp(delim0, "/proc"))
251 				continue;
252 			if (!umount(delim0)) {
253 				unmounted++;
254 			}
255 		}
256 		fclose(procmounts);
257 	}
258 
259 	if (rmdir("/.uwsgi_ns_tmp_mountpoint/.uwsgi_ns_tmp_mountpoint")) {
260 		uwsgi_error("rmdir()");
261 	}
262 	if (rmdir("/.uwsgi_ns_tmp_mountpoint")) {
263 		uwsgi_error("rmdir()");
264 	}
265 
266 	if (strcmp(uwsgi.ns, "/")) {
267 		free(ns_tmp_mountpoint2);
268 		free(ns_tmp_mountpoint);
269 	}
270 
271 
272 
273 }
274