initrd-generator: support rd.net.dns-backend and rd.net.dns-resolve-mode

Add support for two new command line arguments:

 - `rd.net.dns-backend` used to control the "dns" option in
   NetworkManager configuration;
 - `rd.net.dns-resolve-mode` used to control the "resolve-mode" in the
   global DNS configuration.

The use case for them is the installation of a new system where the
administrator wants to configure aspects of the DNS resolution
starting from the early boot, for example to enable DNS over TLS and
avoid that any query goes out unencrypted.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2123
This commit is contained in:
Beniamino Galvani 2025-01-27 13:59:05 +01:00
parent 43fe39fa71
commit b4776d6ced
5 changed files with 141 additions and 40 deletions

View file

@ -158,6 +158,8 @@
<member><option>rd.neednet</option></member>
<member><option>rd.ethtool</option></member>
<member><option>rd.net.dns</option></member>
<member><option>rd.net.dns-backend</option></member>
<member><option>rd.net.dns-resolve-mode</option></member>
<member><option>rd.net.timeout.dhcp</option></member>
<member><option>rd.net.dhcp.retry</option></member>
<member><option>rd.net.dhcp.vendor-class</option></member>
@ -228,9 +230,13 @@
<listitem>
<para>NetworkManager supports the
<option>rd.net.dhcp.dscp</option>={<replaceable>CS0</replaceable>|<replaceable>CS4</replaceable>|<replaceable>CS6</replaceable>}
kernel command line option to set a specific DSCP (TOS) value
in the IP header of DHCP messages.
<option>rd.net.dns-backend</option>=<replaceable>VALUE</replaceable>
kernel command line option to configure the DNS processing
mode. See the description of the <literal>"dns"</literal> key in
the <literal>"main section"</literal> paragraph of <link
linkend='NetworkManager.conf'><citerefentry><refentrytitle>NetworkManager.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>. For
example: <literal>rd.net.dns-backend=systemd-resolved</literal>,
<literal>rd.net.dns-backend=dnsconfd</literal>
</para>
</listitem>
@ -247,6 +253,17 @@
example: <literal>rd.net.dns=2001:db8::1</literal>,
<literal>rd.net.dns=dns+tls://192.0.2.0</literal>,
<literal>rd.net.dns=dns+tls://[2001:db8::2]:5353#example.org</literal>.
In addition, it supports configuring the <literal>"resolve-mode"</literal>
key in the global DNS configuration via the
<option>rd.net.dns-resolve-mode</option> command line option.
</para>
</listitem>
<listitem>
<para>NetworkManager supports the
<option>rd.net.dhcp.dscp</option>={<replaceable>CS0</replaceable>|<replaceable>CS4</replaceable>|<replaceable>CS6</replaceable>}
kernel command line option to set a specific DSCP (TOS) value
in the IP header of DHCP messages.
</para>
</listitem>

View file

@ -11,6 +11,7 @@
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-core-intern/nm-keyfile-internal.h"
#include "libnm-glib-aux/nm-io-utils.h"
#include "libnm-glib-aux/nm-keyfile-aux.h"
#include "libnm-log-core/nm-logging.h"
/*****************************************************************************/
@ -155,6 +156,8 @@ main(int argc, char *argv[])
gs_unref_array GArray *confs = NULL;
guint i;
gs_strfreev char **global_dns_servers = NULL;
gs_free char *dns_backend = NULL;
gs_free char *dns_resolve_mode = NULL;
option_context = g_option_context_new(
"-- [ip=...] [rd.route=...] [bridge=...] [bond=...] [team=...] [vlan=...] "
@ -195,7 +198,9 @@ main(int argc, char *argv[])
(const char *const *) remaining,
&hostname,
&carrier_timeout_sec,
&global_dns_servers);
&global_dns_servers,
&dns_backend,
&dns_resolve_mode);
confs = g_array_new(FALSE, FALSE, sizeof(NMUtilsNamedValue));
g_array_set_clear_func(confs, (GDestroyNotify) nm_utils_named_value_clear_with_g_free);
@ -235,27 +240,32 @@ main(int argc, char *argv[])
g_array_append_val(confs, v);
}
if (global_dns_servers) {
if (global_dns_servers || dns_resolve_mode) {
nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
NMUtilsNamedValue v;
gs_free char *value = NULL;
value = g_strjoinv(",", global_dns_servers);
gs_free char *dns_list = NULL;
keyfile = g_key_file_new();
g_key_file_set_list_separator(keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR);
nm_key_file_add_group(keyfile, NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS);
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS,
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS,
"");
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN "*",
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS,
value);
if (dns_resolve_mode) {
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS,
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_RESOLVE_MODE,
dns_resolve_mode);
}
if (global_dns_servers) {
dns_list = g_strjoinv(",", global_dns_servers);
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN "*",
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS,
dns_list);
}
if (!dump_to_stdout) {
add_keyfile_comment(keyfile, "from \"rd.net.dns\"");
add_keyfile_comment(keyfile, "from \"rd.net.dns\" and \"rd.net.dns-resolv-mode\"");
}
v = (NMUtilsNamedValue) {
@ -265,6 +275,26 @@ main(int argc, char *argv[])
g_array_append_val(confs, v);
}
if (dns_backend) {
nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
NMUtilsNamedValue v;
keyfile = g_key_file_new();
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUP_MAIN,
NM_CONFIG_KEYFILE_KEY_MAIN_DNS,
dns_backend);
if (!dump_to_stdout) {
add_keyfile_comment(keyfile, "from \"rd.net.dns-backend\"");
}
v = (NMUtilsNamedValue) {
.name = g_strdup_printf("%s/16-dns-backend.conf", run_config_dir),
.value_str = g_key_file_to_data(keyfile, NULL, NULL),
};
g_array_append_val(confs, v);
}
if (dump_to_stdout) {
nm_clear_g_free(&connections_dir);
nm_clear_g_free(&initrd_dir);

View file

@ -46,6 +46,8 @@ GHashTable *nmi_cmdline_reader_parse(const char *etc_connections_dir,
const char *const *argv,
char **hostname,
gint64 *carrier_timeout_sec,
char ***global_dns_servers);
char ***global_dns_servers,
char **dns_backend,
char **dns_resolve_mode);
#endif /* __NM_INITRD_GENERATOR_H__ */

View file

@ -34,13 +34,15 @@ typedef struct {
NMConnection *default_connection; /* connection not bound to any ifname */
char *hostname;
GHashTable *znet_ifnames;
GPtrArray *global_dns;
char *dns_backend;
char *dns_resolve_mode;
/* Parameters to be set for all connections */
gboolean ignore_auto_dns;
int dhcp_timeout;
char *dhcp4_vci;
char *dhcp_dscp;
GPtrArray *global_dns;
gboolean ignore_auto_dns;
int dhcp_timeout;
char *dhcp4_vci;
char *dhcp_dscp;
gint64 carrier_timeout_sec;
} Reader;
@ -77,6 +79,8 @@ reader_destroy(Reader *reader, gboolean free_hash)
g_hash_table_unref(reader->znet_ifnames);
nm_clear_g_free(&reader->dhcp4_vci);
nm_clear_g_free(&reader->dhcp_dscp);
nm_clear_g_free(&reader->dns_backend);
nm_clear_g_free(&reader->dns_resolve_mode);
nm_g_slice_free(reader);
if (!free_hash)
return g_steal_pointer(&hash);
@ -1236,6 +1240,28 @@ reader_parse_global_dns(Reader *reader, char *argument)
g_ptr_array_add(reader->global_dns, g_strdup(argument));
}
static void
reader_parse_dns_backend(Reader *reader, const char *argument)
{
if (!NM_IN_STRSET(argument, "none", "default", "systemd-resolved", "dnsmasq", "dnsconfd")) {
_LOGW(LOGD_CORE, "rd.net.dns-backend: invalid value '%s'", argument);
return;
}
reader->dns_backend = g_strdup(argument);
}
static void
reader_parse_dns_resolve_mode(Reader *reader, const char *argument)
{
if (!NM_IN_STRSET(argument, "backup", "prefer", "exclusive")) {
_LOGW(LOGD_CORE, "rd.net.dns-resolve-mode: invalid value '%s'", argument);
return;
}
reader->dns_resolve_mode = g_strdup(argument);
}
static void
reader_parse_ethtool(Reader *reader, char *argument)
{
@ -1410,7 +1436,9 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
const char *const *argv,
char **hostname,
gint64 *carrier_timeout_sec,
char ***global_dns_servers)
char ***global_dns_servers,
char **dns_backend,
char **dns_resolve_mode)
{
Reader *reader;
const char *tag;
@ -1529,6 +1557,10 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
reader_parse_ethtool(reader, argument);
} else if (nm_streq(tag, "rd.net.dns")) {
reader_parse_global_dns(reader, argument);
} else if (nm_streq(tag, "rd.net.dns-backend")) {
reader_parse_dns_backend(reader, argument);
} else if (nm_streq(tag, "rd.net.dns-resolve-mode")) {
reader_parse_dns_resolve_mode(reader, argument);
}
}
@ -1643,8 +1675,9 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
g_hash_table_foreach(reader->hash, _normalize_conn, NULL);
NM_SET_OUT(hostname, g_steal_pointer(&reader->hostname));
NM_SET_OUT(carrier_timeout_sec, reader->carrier_timeout_sec);
NM_SET_OUT(dns_backend, g_steal_pointer(&reader->dns_backend));
NM_SET_OUT(dns_resolve_mode, g_steal_pointer(&reader->dns_resolve_mode));
if (reader->global_dns) {
if (global_dns_servers) {

View file

@ -23,7 +23,12 @@
/*****************************************************************************/
#define _parse(ARGV, out_hostname, out_carrier_timeout_sec, _out_global_dns_servers) \
#define _parse(ARGV, \
out_hostname, \
out_carrier_timeout_sec, \
_out_global_dns_servers, \
_out_dns_backend, \
_out_dns_resolve_mode) \
({ \
const char *const *const _ARGV = (ARGV); \
char **const _out_hostname = (out_hostname); \
@ -35,8 +40,9 @@
_ARGV, \
_out_hostname, \
_out_carrier_timeout_sec, \
_out_global_dns_servers); \
\
_out_global_dns_servers, \
_out_dns_backend, \
_out_dns_resolve_mode); \
g_assert(_connections); \
\
_connections; \
@ -51,6 +57,8 @@
_con_connections = _parse((ARGV), \
nmtst_get_rand_bool() ? &_con_hostname : NULL, \
nmtst_get_rand_bool() ? &_con_carrier_timeout_sec : NULL, \
NULL, \
NULL, \
NULL); \
g_assert_cmpstr(_con_hostname, ==, NULL); \
g_assert_cmpint(_con_carrier_timeout_sec, ==, 0); \
@ -156,7 +164,7 @@ test_dhcp_with_hostname(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "host1");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -426,7 +434,7 @@ test_if_ip4_manual(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "hostname1.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -507,7 +515,7 @@ test_if_ip4_auto(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "myhostname");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -598,7 +606,7 @@ test_if_ip6_manual(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "hostname0.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -686,7 +694,7 @@ test_if_mac_ifname(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "hostname0");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -1842,7 +1850,7 @@ test_rd_znet(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -1929,7 +1937,7 @@ test_rd_znet_legacy(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -2008,7 +2016,7 @@ test_rd_znet_ifnames(void)
gint64 carrier_timeout_sec = 0;
const char *const *v_subchannels;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2);
connection = g_hash_table_lookup(connections, "zeth0");
@ -2283,7 +2291,7 @@ test_nameserver(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 3);
g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0);
@ -2462,7 +2470,7 @@ test_carrier_timeout(void)
gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL, NULL, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 0);
g_assert_cmpstr(hostname, ==, NULL);
g_assert_cmpint(carrier_timeout_sec, ==, 20);
@ -2475,13 +2483,22 @@ test_global_dns(void)
const char *const *ARGV = NM_MAKE_STRV("rd.net.dns=dns+tls://8.8.8.8",
"rd.net.dns=1.1.1.1",
"rd.net.dns=foobar",
"rd.net.dns=dns+tls://[fd01::1]:35#name");
"rd.net.dns=dns+tls://[fd01::1]:35#name",
"rd.net.dns-backend=dnsconfd",
"rd.net.dns-resolve-mode=exclusive");
gs_free char *hostname = NULL;
gs_strfreev char **global_dns_servers = NULL;
gs_free char *dns_backend = NULL;
gs_free char *dns_resolve_mode = NULL;
gint64 carrier_timeout_sec = 0;
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dns: invalid server 'foobar'");
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, &global_dns_servers);
connections = _parse(ARGV,
&hostname,
&carrier_timeout_sec,
&global_dns_servers,
&dns_backend,
&dns_resolve_mode);
g_test_assert_expected_messages();
g_assert_cmpint(g_hash_table_size(connections), ==, 0);
@ -2492,6 +2509,8 @@ test_global_dns(void)
g_assert_cmpstr(global_dns_servers[1], ==, "1.1.1.1");
g_assert_cmpstr(global_dns_servers[2], ==, "dns+tls://[fd01::1]:35#name");
g_assert_cmpstr(global_dns_servers[3], ==, NULL);
g_assert_cmpstr(dns_backend, ==, "dnsconfd");
g_assert_cmpstr(dns_resolve_mode, ==, "exclusive");
}
#define _ethtool_check_inval(arg) \