Compare commits

..

6 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 4ee40eefce
Merge pull request #15516 from poettering/nspawn-resolv-conf
beef up --resolv-conf= options of systemd-nspawn
2020-04-23 08:01:46 +02:00
Lennart Poettering 81d2fe53fc nspawn: some minor modernizations 2020-04-23 07:59:26 +02:00
Niklas Hambüchen 69123c218c man: Fix typo "multiplied with" -> "multiplied by" 2020-04-23 07:56:28 +02:00
Lennart Poettering e309b929ba man: document the new --resolv-conf= options 2020-04-22 19:38:04 +02:00
Lennart Poettering 86775e3524 nspawn: beef up --resolve-conf= modes
Let's add flavours for copying stub/uplink resolv.conf versions.

Let's add a more brutal "replace" mode, where we'll replace any existing
destination file.

Let's also change what "auto" means: instead of copying the static file,
let's use the stub file, so that DNS search info is copied over.

Fixes: #15340
2020-04-22 19:38:04 +02:00
Lennart Poettering 082814743f resolve: move resolv.conf path definitions to shared header
That way we can use it from nspawn.
2020-04-22 19:38:04 +02:00
8 changed files with 112 additions and 56 deletions

View File

@ -140,7 +140,7 @@
<literal>us</literal>. To turn off any kind of rate limiting,
set either value to 0.</para>
<para>Note that the effective rate limit is multiplied with a
<para>Note that the effective rate limit is multiplied by a
factor derived from the available free disk space for the journal.
Currently, this factor is calculated using the base 2 logarithm.</para>

View File

@ -1099,29 +1099,60 @@
<varlistentry>
<term><option>--resolv-conf=</option></term>
<listitem><para>Configures how <filename>/etc/resolv.conf</filename> inside of the container (i.e. DNS
configuration synchronization from host to container) shall be handled. Takes one of <literal>off</literal>,
<literal>copy-host</literal>, <literal>copy-static</literal>, <literal>bind-host</literal>,
<literal>bind-static</literal>, <literal>delete</literal> or <literal>auto</literal>. If set to
<literal>off</literal> the <filename>/etc/resolv.conf</filename> file in the container is left as it is
included in the image, and neither modified nor bind mounted over. If set to <literal>copy-host</literal>, the
<filename>/etc/resolv.conf</filename> file from the host is copied into the container. Similar, if
<literal>bind-host</literal> is used, the file is bind mounted from the host into the container. If set to
<literal>copy-static</literal> the static <filename>resolv.conf</filename> file supplied with
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
copied into the container, and correspondingly <literal>bind-static</literal> bind mounts it there. If set to
<literal>delete</literal> the <filename>/etc/resolv.conf</filename> file in the container is deleted if it
exists. Finally, if set to <literal>auto</literal> the file is left as it is if private networking is turned on
(see <option>--private-network</option>). Otherwise, if <filename>systemd-resolved.service</filename> is
connectible its static <filename>resolv.conf</filename> file is used, and if not the host's
<filename>/etc/resolv.conf</filename> 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 <literal>copy</literal> if the container shall be
able to make changes to the DNS configuration on its own, deviating from the host's settings. Otherwise
<literal>bind</literal> is preferable, as it means direct changes to <filename>/etc/resolv.conf</filename> 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
<listitem><para>Configures how <filename>/etc/resolv.conf</filename> inside of the container shall be
handled (i.e. DNS configuration synchronization from host to container). Takes one of
<literal>off</literal>, <literal>copy-host</literal>, <literal>copy-static</literal>,
<literal>copy-uplink</literal>, <literal>copy-stub</literal>, <literal>replace-host</literal>,
<literal>replace-static</literal>, <literal>replace-uplink</literal>,
<literal>replace-stub</literal>, <literal>bind-host</literal>, <literal>bind-static</literal>,
<literal>bind-uplink</literal>, <literal>bind-stub</literal>, <literal>delete</literal> or
<literal>auto</literal>.</para>
<para>If set to <literal>off</literal> the <filename>/etc/resolv.conf</filename> file in the
container is left as it is included in the image, and neither modified nor bind mounted over.</para>
<para>If set to <literal>copy-host</literal>, the <filename>/etc/resolv.conf</filename> 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 <literal>replace-host</literal> is used the file is copied, replacing any
existing inode, including symlinks. Similar, if <literal>bind-host</literal> is used, the file is
bind mounted from the host into the container.</para>
<para>If set to <literal>copy-static</literal>, <literal>replace-static</literal> or
<literal>bind-static</literal> the static <filename>resolv.conf</filename> file supplied with
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
(specifically: <filename>/usr/lib/systemd/resolv.conf</filename>) is copied or bind mounted into the
container.</para>
<para>If set to <literal>copy-uplink</literal>, <literal>replace-uplink</literal> or
<literal>bind-uplink</literal> the uplink <filename>resolv.conf</filename> file managed by
<filename>systemd-resolved.service</filename> (specifically:
<filename>/run/systemd/resolve/resolv.conf</filename>) is copied or bind mounted into the
container.</para>
<para>If set to <literal>copy-stub</literal>, <literal>replace-stub</literal> or
<literal>bind-stub</literal> the stub <filename>resolv.conf</filename> file managed by
<filename>systemd-resolved.service</filename> (specifically:
<filename>/run/systemd/resolve/stub-resolv.conf</filename>) is copied or bind mounted into the
container.</para>
<para>If set to <literal>delete</literal> the <filename>/etc/resolv.conf</filename> file in the
container is deleted if it exists.</para>
<para>Finally, if set to <literal>auto</literal> the file is left as it is if private networking is
turned on (see <option>--private-network</option>). Otherwise, if
<filename>systemd-resolved.service</filename> is connectible its stub
<filename>resolv.conf</filename> file is used, and if not the host's
<filename>/etc/resolv.conf</filename> file is used. In the latter cases the file is copied if the
image is writable, and bind mounted otherwise.</para>
<para>It's recommended to use <literal>copy-…</literal> or <literal>replace-…</literal> if the
container shall be able to make changes to the DNS configuration on its own, deviating from the
host's settings. Otherwise <literal>bind</literal> is preferable, as it means direct changes to
<filename>/etc/resolv.conf</filename> 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
<literal>auto</literal>.</para></listitem>
</varlistentry>

View File

@ -52,7 +52,7 @@ int expose_port_parse(ExposePort **l, const char *s) {
}
if (r < 0)
return -EINVAL;
return r;
LIST_FOREACH(ports, p, *l)
if (p->protocol == protocol && p->host_port == host_port)
@ -62,9 +62,11 @@ int expose_port_parse(ExposePort **l, const char *s) {
if (!p)
return -ENOMEM;
p->protocol = protocol;
p->host_port = host_port;
p->container_port = container_port;
*p = (ExposePort) {
.protocol = protocol,
.host_port = host_port,
.container_port = container_port,
};
LIST_PREPEND(ports, *l, p);
@ -115,7 +117,6 @@ int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
_cleanup_free_ struct local_address *addresses = NULL;
_cleanup_free_ char *pretty = NULL;
union in_addr_union new_exposed;
ExposePort *p;
bool add;
@ -144,8 +145,11 @@ int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *ex
if (in_addr_equal(af, exposed, &new_exposed))
return 0;
if (DEBUG_LOGGING) {
_cleanup_free_ char *pretty = NULL;
in_addr_to_string(af, &new_exposed, &pretty);
log_debug("New container IP is %s.", strna(pretty));
}
LIST_FOREACH(ports, p, l) {

View File

@ -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",
};

View File

@ -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,

View File

@ -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,9 +1898,13 @@ 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 (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
@ -1908,7 +1912,8 @@ static int setup_resolv_conf(const char *dest) {
*
* 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;
}

View File

@ -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;

View File

@ -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"