diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index b269b99170..8c9dbe52ef 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1099,29 +1099,60 @@ - Configures how /etc/resolv.conf inside of the container (i.e. DNS - configuration synchronization from host to container) shall be handled. Takes one of off, - copy-host, copy-static, bind-host, - bind-static, delete or auto. If set to - off the /etc/resolv.conf file in the container is left as it is - included in the image, and neither modified nor bind mounted over. If set to copy-host, the - /etc/resolv.conf file from the host is copied into the container. Similar, if - bind-host is used, the file is bind mounted from the host into the container. If set to - copy-static the static resolv.conf file supplied with - systemd-resolved.service8 is - copied into the container, and correspondingly bind-static bind mounts it there. If set to - delete the /etc/resolv.conf file in the container is deleted if it - exists. Finally, if set to auto the file is left as it is if private networking is turned on - (see ). Otherwise, if systemd-resolved.service is - connectible its static resolv.conf file is used, and if not the host's - /etc/resolv.conf file is used. In the latter cases the file is copied if the image is - writable, and bind mounted otherwise. It's recommended to use copy if the container shall be - able to make changes to the DNS configuration on its own, deviating from the host's settings. Otherwise - bind is preferable, as it means direct changes to /etc/resolv.conf in - the container are not allowed, as it is a read-only bind mount (but note that if the container has enough - privileges, it might simply go ahead and unmount the bind mount anyway). Note that both if the file is bind - mounted and if it is copied no further propagation of configuration is generally done after the one-time early - initialization (this is because the file is usually updated through copying and renaming). Defaults to + Configures how /etc/resolv.conf inside of the container shall be + handled (i.e. DNS configuration synchronization from host to container). Takes one of + off, copy-host, copy-static, + copy-uplink, copy-stub, replace-host, + replace-static, replace-uplink, + replace-stub, bind-host, bind-static, + bind-uplink, bind-stub, delete or + auto. + + If set to off the /etc/resolv.conf file in the + container is left as it is included in the image, and neither modified nor bind mounted over. + + If set to copy-host, the /etc/resolv.conf file from the + host is copied into the container, unless the file exists already and is not a regular file (e.g. a + symlink). Similar, if replace-host is used the file is copied, replacing any + existing inode, including symlinks. Similar, if bind-host is used, the file is + bind mounted from the host into the container. + + If set to copy-static, replace-static or + bind-static the static resolv.conf file supplied with + systemd-resolved.service8 + (specifically: /usr/lib/systemd/resolv.conf) is copied or bind mounted into the + container. + + If set to copy-uplink, replace-uplink or + bind-uplink the uplink resolv.conf file managed by + systemd-resolved.service (specifically: + /run/systemd/resolve/resolv.conf) is copied or bind mounted into the + container. + + If set to copy-stub, replace-stub or + bind-stub the stub resolv.conf file managed by + systemd-resolved.service (specifically: + /run/systemd/resolve/stub-resolv.conf) is copied or bind mounted into the + container. + + If set to delete the /etc/resolv.conf file in the + container is deleted if it exists. + + Finally, if set to auto the file is left as it is if private networking is + turned on (see ). Otherwise, if + systemd-resolved.service is connectible its stub + resolv.conf file is used, and if not the host's + /etc/resolv.conf file is used. In the latter cases the file is copied if the + image is writable, and bind mounted otherwise. + + It's recommended to use copy-… or replace-… if the + container shall be able to make changes to the DNS configuration on its own, deviating from the + host's settings. Otherwise bind is preferable, as it means direct changes to + /etc/resolv.conf in the container are not allowed, as it is a read-only bind + mount (but note that if the container has enough privileges, it might simply go ahead and unmount the + bind mount anyway). Note that both if the file is bind mounted and if it is copied no further + propagation of configuration is generally done after the one-time early initialization (this is + because the file is usually updated through copying and renaming). Defaults to auto. diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 5fb5b49bbc..4b1115b6e8 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -821,8 +821,16 @@ static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = { [RESOLV_CONF_OFF] = "off", [RESOLV_CONF_COPY_HOST] = "copy-host", [RESOLV_CONF_COPY_STATIC] = "copy-static", + [RESOLV_CONF_COPY_UPLINK] = "copy-uplink", + [RESOLV_CONF_COPY_STUB] = "copy-stub", + [RESOLV_CONF_REPLACE_HOST] = "replace-host", + [RESOLV_CONF_REPLACE_STATIC] = "replace-static", + [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink", + [RESOLV_CONF_REPLACE_STUB] = "replace-stub", [RESOLV_CONF_BIND_HOST] = "bind-host", [RESOLV_CONF_BIND_STATIC] = "bind-static", + [RESOLV_CONF_BIND_UPLINK] = "bind-uplink", + [RESOLV_CONF_BIND_STUB] = "bind-stub", [RESOLV_CONF_DELETE] = "delete", [RESOLV_CONF_AUTO] = "auto", }; diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index f1a1a75466..6f2c1141e6 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -38,10 +38,18 @@ typedef enum UserNamespaceMode { typedef enum ResolvConfMode { RESOLV_CONF_OFF, - RESOLV_CONF_COPY_HOST, - RESOLV_CONF_COPY_STATIC, + RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */ + RESOLV_CONF_COPY_STATIC, /* /usr/lib/systemd/resolv.conf */ + RESOLV_CONF_COPY_UPLINK, /* /run/systemd/resolve/resolv.conf */ + RESOLV_CONF_COPY_STUB, /* /run/systemd/resolve/stub-resolv.conf */ + RESOLV_CONF_REPLACE_HOST, + RESOLV_CONF_REPLACE_STATIC, + RESOLV_CONF_REPLACE_UPLINK, + RESOLV_CONF_REPLACE_STUB, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC, + RESOLV_CONF_BIND_UPLINK, + RESOLV_CONF_BIND_STUB, RESOLV_CONF_DELETE, RESOLV_CONF_AUTO, _RESOLV_CONF_MODE_MAX, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 1d7136cee5..9888c9e294 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -79,6 +79,7 @@ #include "ptyfwd.h" #include "random-util.h" #include "raw-clone.h" +#include "resolve-util.h" #include "rlimit-util.h" #include "rm-rf.h" #if HAVE_SECCOMP @@ -100,12 +101,6 @@ #include "user-util.h" #include "util.h" -#if HAVE_SPLIT_USR -#define STATIC_RESOLV_CONF "/lib/systemd/resolv.conf" -#else -#define STATIC_RESOLV_CONF "/usr/lib/systemd/resolv.conf" -#endif - /* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path * nspawn_notify_socket_path is relative to the container * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ @@ -1850,12 +1845,13 @@ static int setup_resolv_conf(const char *dest) { if (arg_resolv_conf == RESOLV_CONF_AUTO) { if (arg_private_network) m = RESOLV_CONF_OFF; - else if (have_resolv_conf(STATIC_RESOLV_CONF) > 0 && resolved_listening() > 0) - m = etc_writable() ? RESOLV_CONF_COPY_STATIC : RESOLV_CONF_BIND_STATIC; + else if (have_resolv_conf(PRIVATE_STUB_RESOLV_CONF) > 0 && resolved_listening() > 0) + m = etc_writable() ? RESOLV_CONF_COPY_STUB : RESOLV_CONF_BIND_STUB; else if (have_resolv_conf("/etc/resolv.conf") > 0) m = etc_writable() ? RESOLV_CONF_COPY_HOST : RESOLV_CONF_BIND_HOST; else m = etc_writable() ? RESOLV_CONF_DELETE : RESOLV_CONF_OFF; + } else m = arg_resolv_conf; @@ -1877,12 +1873,16 @@ static int setup_resolv_conf(const char *dest) { return 0; } - if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_COPY_STATIC)) - what = STATIC_RESOLV_CONF; + if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_COPY_STATIC)) + what = PRIVATE_STATIC_RESOLV_CONF; + else if (IN_SET(m, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_COPY_UPLINK)) + what = PRIVATE_UPLINK_RESOLV_CONF; + else if (IN_SET(m, RESOLV_CONF_BIND_STUB, RESOLV_CONF_REPLACE_STUB, RESOLV_CONF_COPY_STUB)) + what = PRIVATE_STUB_RESOLV_CONF; else what = "/etc/resolv.conf"; - if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC)) { + if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_BIND_STUB)) { _cleanup_free_ char *resolved = NULL; int found; @@ -1898,17 +1898,22 @@ static int setup_resolv_conf(const char *dest) { r = mount_verbose(LOG_WARNING, what, resolved, NULL, MS_BIND, NULL); if (r >= 0) return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL); + + /* If that didn't work, let's copy the file */ } - /* If that didn't work, let's copy the file */ - r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK); + if (IN_SET(m, RESOLV_CONF_REPLACE_HOST, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_REPLACE_STUB)) + r = copy_file_atomic(what, where, 0644, 0, 0, COPY_REFLINK|COPY_REPLACE); + else + r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK); if (r < 0) { /* If the file already exists as symlink, let's suppress the warning, under the assumption that * resolved or something similar runs inside and the symlink points there. * * If the disk image is read-only, there's also no point in complaining. */ - log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC) && IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) && + IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); return 0; } diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 6afc193f69..c06213f486 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -18,15 +18,6 @@ #include "strv.h" #include "tmpfile-util-label.h" -/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */ -#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf" - -/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */ -#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf" - -/* A static resolv.conf file containing no domains, but only our own DNS server address */ -#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf" - int manager_check_resolv_conf(const Manager *m) { struct stat st, own; diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h index acf1f3dade..0524f1a74f 100644 --- a/src/shared/resolve-util.h +++ b/src/shared/resolve-util.h @@ -81,3 +81,12 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa); const char* dns_cache_mode_to_string(DnsCacheMode p) _const_; DnsCacheMode dns_cache_mode_from_string(const char *s) _pure_; + +/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */ +#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf" + +/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */ +#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf" + +/* A static resolv.conf file containing no domains, but only our own DNS server address */ +#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf"