From b4776d6ced90557d32514a7be867a678affa3b80 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 27 Jan 2025 13:59:05 +0100 Subject: [PATCH] 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 --- man/nm-initrd-generator.xml | 23 +++++++- src/nm-initrd-generator/nm-initrd-generator.c | 58 ++++++++++++++----- src/nm-initrd-generator/nm-initrd-generator.h | 4 +- src/nm-initrd-generator/nmi-cmdline-reader.c | 47 ++++++++++++--- .../tests/test-cmdline-reader.c | 49 +++++++++++----- 5 files changed, 141 insertions(+), 40 deletions(-) diff --git a/man/nm-initrd-generator.xml b/man/nm-initrd-generator.xml index 170cb883e3..2d3d875532 100644 --- a/man/nm-initrd-generator.xml +++ b/man/nm-initrd-generator.xml @@ -158,6 +158,8 @@ + + @@ -228,9 +230,13 @@ NetworkManager supports the - ={CS0|CS4|CS6} - kernel command line option to set a specific DSCP (TOS) value - in the IP header of DHCP messages. + =VALUE + kernel command line option to configure the DNS processing + mode. See the description of the "dns" key in + the "main section" paragraph of NetworkManager.conf5. For + example: rd.net.dns-backend=systemd-resolved, + rd.net.dns-backend=dnsconfd @@ -247,6 +253,17 @@ example: rd.net.dns=2001:db8::1, rd.net.dns=dns+tls://192.0.2.0, rd.net.dns=dns+tls://[2001:db8::2]:5353#example.org. + In addition, it supports configuring the "resolve-mode" + key in the global DNS configuration via the + command line option. + + + + + NetworkManager supports the + ={CS0|CS4|CS6} + kernel command line option to set a specific DSCP (TOS) value + in the IP header of DHCP messages. diff --git a/src/nm-initrd-generator/nm-initrd-generator.c b/src/nm-initrd-generator/nm-initrd-generator.c index 14a168341e..b89b4e413f 100644 --- a/src/nm-initrd-generator/nm-initrd-generator.c +++ b/src/nm-initrd-generator/nm-initrd-generator.c @@ -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); diff --git a/src/nm-initrd-generator/nm-initrd-generator.h b/src/nm-initrd-generator/nm-initrd-generator.h index 23dd484889..c2baad4e55 100644 --- a/src/nm-initrd-generator/nm-initrd-generator.h +++ b/src/nm-initrd-generator/nm-initrd-generator.h @@ -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__ */ diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 91fe5d1d51..d6dc1fcb7c 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -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) { diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c index 07cf57bdaa..a0100764ca 100644 --- a/src/nm-initrd-generator/tests/test-cmdline-reader.c +++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c @@ -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) \