1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.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"
17 #include "user-util.h"
20 Settings
*settings_new(void) {
28 .start_mode
= _START_MODE_INVALID
,
29 .personality
= PERSONALITY_INVALID
,
31 .resolv_conf
= _RESOLV_CONF_MODE_INVALID
,
32 .link_journal
= _LINK_JOURNAL_INVALID
,
33 .timezone
= _TIMEZONE_MODE_INVALID
,
35 .userns_mode
= _USER_NAMESPACE_MODE_INVALID
,
37 .uid_shift
= UID_INVALID
,
38 .uid_range
= UID_INVALID
,
40 .no_new_privileges
= -1,
43 .volatile_mode
= _VOLATILE_MODE_INVALID
,
45 .private_network
= -1,
48 .full_capabilities
= CAPABILITY_QUINTET_NULL
,
53 .console_mode
= _CONSOLE_MODE_INVALID
,
54 .console_width
= (unsigned) -1,
55 .console_height
= (unsigned) -1,
57 .clone_ns_flags
= (unsigned long) -1,
64 int settings_load(FILE *f
, const char *path
, Settings
**ret
) {
65 _cleanup_(settings_freep
) Settings
*s
= NULL
;
75 r
= config_parse(NULL
, path
, f
,
79 config_item_perf_lookup
, nspawn_gperf_lookup
,
85 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
86 * both fields shall be initialized or neither. */
87 if (s
->userns_mode
== USER_NAMESPACE_PICK
)
88 s
->userns_chown
= true;
89 else if (s
->userns_mode
!= _USER_NAMESPACE_MODE_INVALID
&& s
->userns_chown
< 0)
90 s
->userns_chown
= false;
92 if (s
->userns_chown
>= 0 && s
->userns_mode
== _USER_NAMESPACE_MODE_INVALID
)
93 s
->userns_mode
= USER_NAMESPACE_NO
;
99 static void free_oci_hooks(OciHook
*h
, size_t n
) {
104 for (i
= 0; i
< n
; i
++) {
106 strv_free(h
[i
].args
);
113 void device_node_array_free(DeviceNode
*node
, size_t n
) {
116 for (i
= 0; i
< n
; i
++)
122 Settings
* settings_free(Settings
*s
) {
126 strv_free(s
->parameters
);
127 strv_free(s
->environment
);
129 free(s
->pivot_root_new
);
130 free(s
->pivot_root_old
);
131 free(s
->working_directory
);
132 strv_free(s
->syscall_whitelist
);
133 strv_free(s
->syscall_blacklist
);
134 rlimit_free_all(s
->rlimit
);
136 cpu_set_reset(&s
->cpu_set
);
138 strv_free(s
->network_interfaces
);
139 strv_free(s
->network_macvlan
);
140 strv_free(s
->network_ipvlan
);
141 strv_free(s
->network_veth_extra
);
142 free(s
->network_bridge
);
143 free(s
->network_zone
);
144 expose_port_free_all(s
->expose_ports
);
146 custom_mount_free_all(s
->custom_mounts
, s
->n_custom_mounts
);
151 free_oci_hooks(s
->oci_hooks_prestart
, s
->n_oci_hooks_prestart
);
152 free_oci_hooks(s
->oci_hooks_poststart
, s
->n_oci_hooks_poststart
);
153 free_oci_hooks(s
->oci_hooks_poststop
, s
->n_oci_hooks_poststop
);
156 sd_bus_message_unref(s
->properties
);
158 free(s
->supplementary_gids
);
159 device_node_array_free(s
->extra_nodes
, s
->n_extra_nodes
);
160 free(s
->network_namespace_path
);
162 strv_free(s
->sysctl
);
165 seccomp_release(s
->seccomp
);
171 bool settings_private_network(Settings
*s
) {
175 s
->private_network
> 0 ||
176 s
->network_veth
> 0 ||
179 s
->network_interfaces
||
180 s
->network_macvlan
||
182 s
->network_veth_extra
;
185 bool settings_network_veth(Settings
*s
) {
189 s
->network_veth
> 0 ||
194 int settings_allocate_properties(Settings
*s
) {
195 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
203 r
= sd_bus_default_system(&bus
);
207 r
= sd_bus_message_new(bus
, &s
->properties
, SD_BUS_MESSAGE_METHOD_CALL
);
214 DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode
, volatile_mode
, VolatileMode
, "Failed to parse volatile mode");
216 int config_parse_expose_port(
218 const char *filename
,
221 unsigned section_line
,
235 r
= expose_port_parse(&s
->expose_ports
, rvalue
);
237 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Duplicate port specification, ignoring: %s", rvalue
);
241 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse host port %s: %m", rvalue
);
248 int config_parse_capability(
250 const char *filename
,
253 unsigned section_line
,
260 uint64_t u
= 0, *result
= data
;
268 _cleanup_free_
char *word
= NULL
;
270 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
272 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract capability string, ignoring: %s", rvalue
);
278 if (streq(word
, "all"))
281 r
= capability_from_name(word
);
283 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse capability, ignoring: %s", word
);
287 u
|= UINT64_C(1) << r
;
298 int config_parse_id128(
300 const char *filename
,
303 unsigned section_line
,
310 sd_id128_t t
, *result
= data
;
317 r
= sd_id128_from_string(rvalue
, &t
);
319 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue
);
327 int config_parse_pivot_root(
329 const char *filename
,
332 unsigned section_line
,
339 Settings
*settings
= data
;
346 r
= pivot_root_parse(&settings
->pivot_root_new
, &settings
->pivot_root_old
, rvalue
);
348 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid pivot root mount specification %s: %m", rvalue
);
355 int config_parse_bind(
357 const char *filename
,
360 unsigned section_line
,
367 Settings
*settings
= data
;
374 r
= bind_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
376 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bind mount specification %s: %m", rvalue
);
383 int config_parse_tmpfs(
385 const char *filename
,
388 unsigned section_line
,
395 Settings
*settings
= data
;
402 r
= tmpfs_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
);
404 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid temporary file system specification %s: %m", rvalue
);
411 int config_parse_inaccessible(
413 const char *filename
,
416 unsigned section_line
,
423 Settings
*settings
= data
;
430 r
= inaccessible_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
);
432 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid inaccessible file system specification %s: %m", rvalue
);
439 int config_parse_overlay(
441 const char *filename
,
444 unsigned section_line
,
451 Settings
*settings
= data
;
458 r
= overlay_mount_parse(&settings
->custom_mounts
, &settings
->n_custom_mounts
, rvalue
, ltype
);
460 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid overlay file system specification %s, ignoring: %m", rvalue
);
465 int config_parse_veth_extra(
467 const char *filename
,
470 unsigned section_line
,
477 Settings
*settings
= data
;
484 r
= veth_extra_parse(&settings
->network_veth_extra
, rvalue
);
486 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid extra virtual Ethernet link specification %s: %m", rvalue
);
493 int config_parse_network_zone(
495 const char *filename
,
498 unsigned section_line
,
505 Settings
*settings
= data
;
506 _cleanup_free_
char *j
= NULL
;
512 j
= strjoin("vz-", rvalue
);
513 if (!ifname_valid(j
)) {
514 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid network zone name, ignoring: %s", rvalue
);
518 free_and_replace(settings
->network_zone
, j
);
523 int config_parse_boot(
525 const char *filename
,
528 unsigned section_line
,
535 Settings
*settings
= data
;
542 r
= parse_boolean(rvalue
);
544 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue
);
549 if (settings
->start_mode
== START_PID2
)
552 settings
->start_mode
= START_BOOT
;
554 if (settings
->start_mode
== START_BOOT
)
557 if (settings
->start_mode
< 0)
558 settings
->start_mode
= START_PID1
;
564 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
568 int config_parse_pid2(
570 const char *filename
,
573 unsigned section_line
,
580 Settings
*settings
= data
;
587 r
= parse_boolean(rvalue
);
589 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue
);
594 if (settings
->start_mode
== START_BOOT
)
597 settings
->start_mode
= START_PID2
;
599 if (settings
->start_mode
== START_PID2
)
602 if (settings
->start_mode
< 0)
603 settings
->start_mode
= START_PID1
;
609 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
613 int config_parse_private_users(
615 const char *filename
,
618 unsigned section_line
,
625 Settings
*settings
= data
;
632 r
= parse_boolean(rvalue
);
634 /* no: User namespacing off */
635 settings
->userns_mode
= USER_NAMESPACE_NO
;
636 settings
->uid_shift
= UID_INVALID
;
637 settings
->uid_range
= UINT32_C(0x10000);
639 /* yes: User namespacing on, UID range is read from root dir */
640 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
641 settings
->uid_shift
= UID_INVALID
;
642 settings
->uid_range
= UINT32_C(0x10000);
643 } else if (streq(rvalue
, "pick")) {
644 /* pick: User namespacing on, UID range is picked randomly */
645 settings
->userns_mode
= USER_NAMESPACE_PICK
;
646 settings
->uid_shift
= UID_INVALID
;
647 settings
->uid_range
= UINT32_C(0x10000);
649 const char *range
, *shift
;
652 /* anything else: User namespacing on, UID range is explicitly configured */
654 range
= strchr(rvalue
, ':');
656 shift
= strndupa(rvalue
, range
- rvalue
);
659 r
= safe_atou32(range
, &rn
);
660 if (r
< 0 || rn
<= 0) {
661 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID range invalid, ignoring: %s", range
);
666 rn
= UINT32_C(0x10000);
669 r
= parse_uid(shift
, &sh
);
671 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "UID/GID shift invalid, ignoring: %s", range
);
675 settings
->userns_mode
= USER_NAMESPACE_FIXED
;
676 settings
->uid_shift
= sh
;
677 settings
->uid_range
= rn
;
683 int config_parse_syscall_filter(
685 const char *filename
,
688 unsigned section_line
,
695 Settings
*settings
= data
;
704 negative
= rvalue
[0] == '~';
705 items
= negative
? rvalue
+ 1 : rvalue
;
708 _cleanup_free_
char *word
= NULL
;
710 r
= extract_first_word(&items
, &word
, NULL
, 0);
716 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue
);
721 r
= strv_extend(&settings
->syscall_blacklist
, word
);
723 r
= strv_extend(&settings
->syscall_whitelist
, word
);
731 int config_parse_hostname(
733 const char *filename
,
736 unsigned section_line
,
748 if (!hostname_is_valid(rvalue
, false)) {
749 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid hostname, ignoring: %s", rvalue
);
753 if (free_and_strdup(s
, empty_to_null(rvalue
)) < 0)
759 int config_parse_oom_score_adjust(
761 const char *filename
,
764 unsigned section_line
,
771 Settings
*settings
= data
;
777 if (isempty(rvalue
)) {
778 settings
->oom_score_adjust_set
= false;
782 r
= parse_oom_score_adjust(rvalue
, &oa
);
784 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "OOM score adjust value out of range, ignoring: %s", rvalue
);
788 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
792 settings
->oom_score_adjust
= oa
;
793 settings
->oom_score_adjust_set
= true;
798 int config_parse_cpu_affinity(
800 const char *filename
,
803 unsigned section_line
,
810 Settings
*settings
= data
;
815 return parse_cpu_set_extend(rvalue
, &settings
->cpu_set
, true, unit
, filename
, line
, lvalue
);
818 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf
, resolv_conf_mode
, ResolvConfMode
, "Failed to parse resolv.conf mode");
820 static const char *const resolv_conf_mode_table
[_RESOLV_CONF_MODE_MAX
] = {
821 [RESOLV_CONF_OFF
] = "off",
822 [RESOLV_CONF_COPY_HOST
] = "copy-host",
823 [RESOLV_CONF_COPY_STATIC
] = "copy-static",
824 [RESOLV_CONF_COPY_UPLINK
] = "copy-uplink",
825 [RESOLV_CONF_COPY_STUB
] = "copy-stub",
826 [RESOLV_CONF_REPLACE_HOST
] = "replace-host",
827 [RESOLV_CONF_REPLACE_STATIC
] = "replace-static",
828 [RESOLV_CONF_REPLACE_UPLINK
] = "replace-uplink",
829 [RESOLV_CONF_REPLACE_STUB
] = "replace-stub",
830 [RESOLV_CONF_BIND_HOST
] = "bind-host",
831 [RESOLV_CONF_BIND_STATIC
] = "bind-static",
832 [RESOLV_CONF_BIND_UPLINK
] = "bind-uplink",
833 [RESOLV_CONF_BIND_STUB
] = "bind-stub",
834 [RESOLV_CONF_DELETE
] = "delete",
835 [RESOLV_CONF_AUTO
] = "auto",
838 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolv_conf_mode
, ResolvConfMode
, RESOLV_CONF_AUTO
);
840 int parse_link_journal(const char *s
, LinkJournal
*ret_mode
, bool *ret_try
) {
845 if (streq(s
, "auto")) {
846 *ret_mode
= LINK_AUTO
;
848 } else if (streq(s
, "no")) {
851 } else if (streq(s
, "guest")) {
852 *ret_mode
= LINK_GUEST
;
854 } else if (streq(s
, "host")) {
855 *ret_mode
= LINK_HOST
;
857 } else if (streq(s
, "try-guest")) {
858 *ret_mode
= LINK_GUEST
;
860 } else if (streq(s
, "try-host")) {
861 *ret_mode
= LINK_HOST
;
869 int config_parse_link_journal(
871 const char *filename
,
874 unsigned section_line
,
881 Settings
*settings
= data
;
887 r
= parse_link_journal(rvalue
, &settings
->link_journal
, &settings
->link_journal_try
);
889 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse link journal mode, ignoring: %s", rvalue
);
896 DEFINE_CONFIG_PARSE_ENUM(config_parse_timezone
, timezone_mode
, TimezoneMode
, "Failed to parse timezone mode");
898 static const char *const timezone_mode_table
[_TIMEZONE_MODE_MAX
] = {
899 [TIMEZONE_OFF
] = "off",
900 [TIMEZONE_COPY
] = "copy",
901 [TIMEZONE_BIND
] = "bind",
902 [TIMEZONE_SYMLINK
] = "symlink",
903 [TIMEZONE_DELETE
] = "delete",
904 [TIMEZONE_AUTO
] = "auto",
907 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode
, TimezoneMode
, TIMEZONE_AUTO
);