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