]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
tree-wide: drop 'This file is part of systemd' blurb
[thirdparty/systemd.git] / src / nspawn / nspawn-settings.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2015 Lennart Poettering
4 ***/
5
6 #include "alloc-util.h"
7 #include "cap-list.h"
8 #include "conf-parser.h"
9 #include "cpu-set-util.h"
10 #include "hostname-util.h"
11 #include "nspawn-network.h"
12 #include "nspawn-settings.h"
13 #include "parse-util.h"
14 #include "process-util.h"
15 #include "rlimit-util.h"
16 #include "socket-util.h"
17 #include "string-table.h"
18 #include "string-util.h"
19 #include "strv.h"
20 #include "user-util.h"
21 #include "util.h"
22
23 int settings_load(FILE *f, const char *path, Settings **ret) {
24 _cleanup_(settings_freep) Settings *s = NULL;
25 int r;
26
27 assert(path);
28 assert(ret);
29
30 s = new0(Settings, 1);
31 if (!s)
32 return -ENOMEM;
33
34 s->start_mode = _START_MODE_INVALID;
35 s->personality = PERSONALITY_INVALID;
36 s->userns_mode = _USER_NAMESPACE_MODE_INVALID;
37 s->resolv_conf = _RESOLV_CONF_MODE_INVALID;
38 s->link_journal = _LINK_JOURNAL_INVALID;
39 s->timezone = _TIMEZONE_MODE_INVALID;
40 s->uid_shift = UID_INVALID;
41 s->uid_range = UID_INVALID;
42 s->no_new_privileges = -1;
43
44 s->read_only = -1;
45 s->volatile_mode = _VOLATILE_MODE_INVALID;
46 s->userns_chown = -1;
47
48 s->private_network = -1;
49 s->network_veth = -1;
50
51 r = config_parse(NULL, path, f,
52 "Exec\0"
53 "Network\0"
54 "Files\0",
55 config_item_perf_lookup, nspawn_gperf_lookup,
56 CONFIG_PARSE_WARN,
57 s);
58 if (r < 0)
59 return r;
60
61 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
62 * both fields shall be initialized or neither. */
63 if (s->userns_mode == USER_NAMESPACE_PICK)
64 s->userns_chown = true;
65 else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
66 s->userns_chown = false;
67
68 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
69 s->userns_mode = USER_NAMESPACE_NO;
70
71 *ret = TAKE_PTR(s);
72
73 return 0;
74 }
75
76 Settings* settings_free(Settings *s) {
77
78 if (!s)
79 return NULL;
80
81 strv_free(s->parameters);
82 strv_free(s->environment);
83 free(s->user);
84 free(s->pivot_root_new);
85 free(s->pivot_root_old);
86 free(s->working_directory);
87 strv_free(s->syscall_whitelist);
88 strv_free(s->syscall_blacklist);
89 rlimit_free_all(s->rlimit);
90 free(s->hostname);
91 s->cpuset = cpu_set_mfree(s->cpuset);
92
93 strv_free(s->network_interfaces);
94 strv_free(s->network_macvlan);
95 strv_free(s->network_ipvlan);
96 strv_free(s->network_veth_extra);
97 free(s->network_bridge);
98 free(s->network_zone);
99 expose_port_free_all(s->expose_ports);
100
101 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
102 return mfree(s);
103 }
104
105 bool settings_private_network(Settings *s) {
106 assert(s);
107
108 return
109 s->private_network > 0 ||
110 s->network_veth > 0 ||
111 s->network_bridge ||
112 s->network_zone ||
113 s->network_interfaces ||
114 s->network_macvlan ||
115 s->network_ipvlan ||
116 s->network_veth_extra;
117 }
118
119 bool settings_network_veth(Settings *s) {
120 assert(s);
121
122 return
123 s->network_veth > 0 ||
124 s->network_bridge ||
125 s->network_zone;
126 }
127
128 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
129
130 int config_parse_expose_port(
131 const char *unit,
132 const char *filename,
133 unsigned line,
134 const char *section,
135 unsigned section_line,
136 const char *lvalue,
137 int ltype,
138 const char *rvalue,
139 void *data,
140 void *userdata) {
141
142 Settings *s = data;
143 int r;
144
145 assert(filename);
146 assert(lvalue);
147 assert(rvalue);
148
149 r = expose_port_parse(&s->expose_ports, rvalue);
150 if (r == -EEXIST) {
151 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
152 return 0;
153 }
154 if (r < 0) {
155 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
156 return 0;
157 }
158
159 return 0;
160 }
161
162 int config_parse_capability(
163 const char *unit,
164 const char *filename,
165 unsigned line,
166 const char *section,
167 unsigned section_line,
168 const char *lvalue,
169 int ltype,
170 const char *rvalue,
171 void *data,
172 void *userdata) {
173
174 uint64_t u = 0, *result = data;
175 int r;
176
177 assert(filename);
178 assert(lvalue);
179 assert(rvalue);
180
181 for (;;) {
182 _cleanup_free_ char *word = NULL;
183 int cap;
184
185 r = extract_first_word(&rvalue, &word, NULL, 0);
186 if (r < 0) {
187 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
188 return 0;
189 }
190 if (r == 0)
191 break;
192
193 cap = capability_from_name(word);
194 if (cap < 0) {
195 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
196 continue;
197 }
198
199 u |= UINT64_C(1) << cap;
200 }
201
202 if (u == 0)
203 return 0;
204
205 *result |= u;
206 return 0;
207 }
208
209 int config_parse_id128(
210 const char *unit,
211 const char *filename,
212 unsigned line,
213 const char *section,
214 unsigned section_line,
215 const char *lvalue,
216 int ltype,
217 const char *rvalue,
218 void *data,
219 void *userdata) {
220
221 sd_id128_t t, *result = data;
222 int r;
223
224 assert(filename);
225 assert(lvalue);
226 assert(rvalue);
227
228 r = sd_id128_from_string(rvalue, &t);
229 if (r < 0) {
230 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
231 return 0;
232 }
233
234 *result = t;
235 return 0;
236 }
237
238 int config_parse_pivot_root(
239 const char *unit,
240 const char *filename,
241 unsigned line,
242 const char *section,
243 unsigned section_line,
244 const char *lvalue,
245 int ltype,
246 const char *rvalue,
247 void *data,
248 void *userdata) {
249
250 Settings *settings = data;
251 int r;
252
253 assert(filename);
254 assert(lvalue);
255 assert(rvalue);
256
257 r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
258 if (r < 0) {
259 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
260 return 0;
261 }
262
263 return 0;
264 }
265
266 int config_parse_bind(
267 const char *unit,
268 const char *filename,
269 unsigned line,
270 const char *section,
271 unsigned section_line,
272 const char *lvalue,
273 int ltype,
274 const char *rvalue,
275 void *data,
276 void *userdata) {
277
278 Settings *settings = data;
279 int r;
280
281 assert(filename);
282 assert(lvalue);
283 assert(rvalue);
284
285 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
286 if (r < 0) {
287 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
288 return 0;
289 }
290
291 return 0;
292 }
293
294 int config_parse_tmpfs(
295 const char *unit,
296 const char *filename,
297 unsigned line,
298 const char *section,
299 unsigned section_line,
300 const char *lvalue,
301 int ltype,
302 const char *rvalue,
303 void *data,
304 void *userdata) {
305
306 Settings *settings = data;
307 int r;
308
309 assert(filename);
310 assert(lvalue);
311 assert(rvalue);
312
313 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
314 if (r < 0) {
315 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
316 return 0;
317 }
318
319 return 0;
320 }
321
322 int config_parse_overlay(
323 const char *unit,
324 const char *filename,
325 unsigned line,
326 const char *section,
327 unsigned section_line,
328 const char *lvalue,
329 int ltype,
330 const char *rvalue,
331 void *data,
332 void *userdata) {
333
334 Settings *settings = data;
335 int r;
336
337 assert(filename);
338 assert(lvalue);
339 assert(rvalue);
340
341 r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
342 if (r < 0)
343 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
344
345 return 0;
346 }
347
348 int config_parse_veth_extra(
349 const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 unsigned section_line,
354 const char *lvalue,
355 int ltype,
356 const char *rvalue,
357 void *data,
358 void *userdata) {
359
360 Settings *settings = data;
361 int r;
362
363 assert(filename);
364 assert(lvalue);
365 assert(rvalue);
366
367 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
368 if (r < 0) {
369 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
370 return 0;
371 }
372
373 return 0;
374 }
375
376 int config_parse_network_zone(
377 const char *unit,
378 const char *filename,
379 unsigned line,
380 const char *section,
381 unsigned section_line,
382 const char *lvalue,
383 int ltype,
384 const char *rvalue,
385 void *data,
386 void *userdata) {
387
388 Settings *settings = data;
389 _cleanup_free_ char *j = NULL;
390
391 assert(filename);
392 assert(lvalue);
393 assert(rvalue);
394
395 j = strappend("vz-", rvalue);
396 if (!ifname_valid(j)) {
397 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
398 return 0;
399 }
400
401 free_and_replace(settings->network_zone, j);
402
403 return 0;
404 }
405
406 int config_parse_boot(
407 const char *unit,
408 const char *filename,
409 unsigned line,
410 const char *section,
411 unsigned section_line,
412 const char *lvalue,
413 int ltype,
414 const char *rvalue,
415 void *data,
416 void *userdata) {
417
418 Settings *settings = data;
419 int r;
420
421 assert(filename);
422 assert(lvalue);
423 assert(rvalue);
424
425 r = parse_boolean(rvalue);
426 if (r < 0) {
427 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
428 return 0;
429 }
430
431 if (r > 0) {
432 if (settings->start_mode == START_PID2)
433 goto conflict;
434
435 settings->start_mode = START_BOOT;
436 } else {
437 if (settings->start_mode == START_BOOT)
438 goto conflict;
439
440 if (settings->start_mode < 0)
441 settings->start_mode = START_PID1;
442 }
443
444 return 0;
445
446 conflict:
447 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
448 return 0;
449 }
450
451 int config_parse_pid2(
452 const char *unit,
453 const char *filename,
454 unsigned line,
455 const char *section,
456 unsigned section_line,
457 const char *lvalue,
458 int ltype,
459 const char *rvalue,
460 void *data,
461 void *userdata) {
462
463 Settings *settings = data;
464 int r;
465
466 assert(filename);
467 assert(lvalue);
468 assert(rvalue);
469
470 r = parse_boolean(rvalue);
471 if (r < 0) {
472 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
473 return 0;
474 }
475
476 if (r > 0) {
477 if (settings->start_mode == START_BOOT)
478 goto conflict;
479
480 settings->start_mode = START_PID2;
481 } else {
482 if (settings->start_mode == START_PID2)
483 goto conflict;
484
485 if (settings->start_mode < 0)
486 settings->start_mode = START_PID1;
487 }
488
489 return 0;
490
491 conflict:
492 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
493 return 0;
494 }
495
496 int config_parse_private_users(
497 const char *unit,
498 const char *filename,
499 unsigned line,
500 const char *section,
501 unsigned section_line,
502 const char *lvalue,
503 int ltype,
504 const char *rvalue,
505 void *data,
506 void *userdata) {
507
508 Settings *settings = data;
509 int r;
510
511 assert(filename);
512 assert(lvalue);
513 assert(rvalue);
514
515 r = parse_boolean(rvalue);
516 if (r == 0) {
517 /* no: User namespacing off */
518 settings->userns_mode = USER_NAMESPACE_NO;
519 settings->uid_shift = UID_INVALID;
520 settings->uid_range = UINT32_C(0x10000);
521 } else if (r > 0) {
522 /* yes: User namespacing on, UID range is read from root dir */
523 settings->userns_mode = USER_NAMESPACE_FIXED;
524 settings->uid_shift = UID_INVALID;
525 settings->uid_range = UINT32_C(0x10000);
526 } else if (streq(rvalue, "pick")) {
527 /* pick: User namespacing on, UID range is picked randomly */
528 settings->userns_mode = USER_NAMESPACE_PICK;
529 settings->uid_shift = UID_INVALID;
530 settings->uid_range = UINT32_C(0x10000);
531 } else {
532 const char *range, *shift;
533 uid_t sh, rn;
534
535 /* anything else: User namespacing on, UID range is explicitly configured */
536
537 range = strchr(rvalue, ':');
538 if (range) {
539 shift = strndupa(rvalue, range - rvalue);
540 range++;
541
542 r = safe_atou32(range, &rn);
543 if (r < 0 || rn <= 0) {
544 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
545 return 0;
546 }
547 } else {
548 shift = rvalue;
549 rn = UINT32_C(0x10000);
550 }
551
552 r = parse_uid(shift, &sh);
553 if (r < 0) {
554 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
555 return 0;
556 }
557
558 settings->userns_mode = USER_NAMESPACE_FIXED;
559 settings->uid_shift = sh;
560 settings->uid_range = rn;
561 }
562
563 return 0;
564 }
565
566 int config_parse_syscall_filter(
567 const char *unit,
568 const char *filename,
569 unsigned line,
570 const char *section,
571 unsigned section_line,
572 const char *lvalue,
573 int ltype,
574 const char *rvalue,
575 void *data,
576 void *userdata) {
577
578 Settings *settings = data;
579 bool negative;
580 const char *items;
581 int r;
582
583 assert(filename);
584 assert(lvalue);
585 assert(rvalue);
586
587 negative = rvalue[0] == '~';
588 items = negative ? rvalue + 1 : rvalue;
589
590 for (;;) {
591 _cleanup_free_ char *word = NULL;
592
593 r = extract_first_word(&items, &word, NULL, 0);
594 if (r == 0)
595 break;
596 if (r == -ENOMEM)
597 return log_oom();
598 if (r < 0) {
599 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
600 return 0;
601 }
602
603 if (negative)
604 r = strv_extend(&settings->syscall_blacklist, word);
605 else
606 r = strv_extend(&settings->syscall_whitelist, word);
607 if (r < 0)
608 return log_oom();
609 }
610
611 return 0;
612 }
613
614 int config_parse_hostname(
615 const char *unit,
616 const char *filename,
617 unsigned line,
618 const char *section,
619 unsigned section_line,
620 const char *lvalue,
621 int ltype,
622 const char *rvalue,
623 void *data,
624 void *userdata) {
625
626 char **s = data;
627
628 assert(rvalue);
629 assert(s);
630
631 if (!hostname_is_valid(rvalue, false)) {
632 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue);
633 return 0;
634 }
635
636 if (free_and_strdup(s, empty_to_null(rvalue)) < 0)
637 return log_oom();
638
639 return 0;
640 }
641
642 int config_parse_oom_score_adjust(
643 const char *unit,
644 const char *filename,
645 unsigned line,
646 const char *section,
647 unsigned section_line,
648 const char *lvalue,
649 int ltype,
650 const char *rvalue,
651 void *data,
652 void *userdata) {
653
654 Settings *settings = data;
655 int oa, r;
656
657 assert(rvalue);
658 assert(settings);
659
660 if (isempty(rvalue)) {
661 settings->oom_score_adjust_set = false;
662 return 0;
663 }
664
665 r = parse_oom_score_adjust(rvalue, &oa);
666 if (r == -ERANGE) {
667 log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
668 return 0;
669 }
670 if (r < 0) {
671 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
672 return 0;
673 }
674
675 settings->oom_score_adjust = oa;
676 settings->oom_score_adjust_set = true;
677
678 return 0;
679 }
680
681 int config_parse_cpu_affinity(
682 const char *unit,
683 const char *filename,
684 unsigned line,
685 const char *section,
686 unsigned section_line,
687 const char *lvalue,
688 int ltype,
689 const char *rvalue,
690 void *data,
691 void *userdata) {
692
693 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
694 Settings *settings = data;
695 int ncpus;
696
697 assert(rvalue);
698 assert(settings);
699
700 ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
701 if (ncpus < 0)
702 return ncpus;
703
704 if (ncpus == 0) {
705 /* An empty assignment resets the CPU list */
706 settings->cpuset = cpu_set_mfree(settings->cpuset);
707 settings->cpuset_ncpus = 0;
708 return 0;
709 }
710
711 if (!settings->cpuset) {
712 settings->cpuset = TAKE_PTR(cpuset);
713 settings->cpuset_ncpus = (unsigned) ncpus;
714 return 0;
715 }
716
717 if (settings->cpuset_ncpus < (unsigned) ncpus) {
718 CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset);
719 CPU_FREE(settings->cpuset);
720 settings->cpuset = TAKE_PTR(cpuset);
721 settings->cpuset_ncpus = (unsigned) ncpus;
722 return 0;
723 }
724
725 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset);
726
727 return 0;
728 }
729
730 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
731
732 static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = {
733 [RESOLV_CONF_OFF] = "off",
734 [RESOLV_CONF_COPY_HOST] = "copy-host",
735 [RESOLV_CONF_COPY_STATIC] = "copy-static",
736 [RESOLV_CONF_BIND_HOST] = "bind-host",
737 [RESOLV_CONF_BIND_STATIC] = "bind-static",
738 [RESOLV_CONF_DELETE] = "delete",
739 [RESOLV_CONF_AUTO] = "auto",
740 };
741
742 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode, ResolvConfMode, RESOLV_CONF_AUTO);
743
744 int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try) {
745 assert(s);
746 assert(ret_mode);
747 assert(ret_try);
748
749 if (streq(s, "auto")) {
750 *ret_mode = LINK_AUTO;
751 *ret_try = false;
752 } else if (streq(s, "no")) {
753 *ret_mode = LINK_NO;
754 *ret_try = false;
755 } else if (streq(s, "guest")) {
756 *ret_mode = LINK_GUEST;
757 *ret_try = false;
758 } else if (streq(s, "host")) {
759 *ret_mode = LINK_HOST;
760 *ret_try = false;
761 } else if (streq(s, "try-guest")) {
762 *ret_mode = LINK_GUEST;
763 *ret_try = true;
764 } else if (streq(s, "try-host")) {
765 *ret_mode = LINK_HOST;
766 *ret_try = true;
767 } else
768 return -EINVAL;
769
770 return 0;
771 }
772
773 int config_parse_link_journal(
774 const char *unit,
775 const char *filename,
776 unsigned line,
777 const char *section,
778 unsigned section_line,
779 const char *lvalue,
780 int ltype,
781 const char *rvalue,
782 void *data,
783 void *userdata) {
784
785 Settings *settings = data;
786 int r;
787
788 assert(rvalue);
789 assert(settings);
790
791 r = parse_link_journal(rvalue, &settings->link_journal, &settings->link_journal_try);
792 if (r < 0) {
793 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse link journal mode, ignoring: %s", rvalue);
794 return 0;
795 }
796
797 return 0;
798 }
799
800 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone, timezone_mode, TimezoneMode, "Failed to parse timezone mode");
801
802 static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
803 [TIMEZONE_OFF] = "off",
804 [TIMEZONE_COPY] = "copy",
805 [TIMEZONE_BIND] = "bind",
806 [TIMEZONE_SYMLINK] = "symlink",
807 [TIMEZONE_DELETE] = "delete",
808 [TIMEZONE_AUTO] = "auto",
809 };
810
811 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);