-
Notifications
You must be signed in to change notification settings - Fork 521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add configurable DNS settings #2353
Conversation
^ Rebases on develop and fix the merge conflict in |
^ Fixed a clippy lint I originally missed |
sources/api/migration/migrations/v1.10.0/dns-settings/src/main.rs
Outdated
Show resolved
Hide resolved
^ Addresses @arnaldo2792 's comments |
^ Rebases on |
^ Changes the type of |
^ Addresses @etungsten 's coments |
sources/api/migration/migrations/v1.10.0/dns-settings/src/main.rs
Outdated
Show resolved
Hide resolved
^ Fixes the migration filename (thanks @etungsten!) |
sources/api/netdog/src/dns.rs
Outdated
if config_exists { | ||
let config_str = | ||
fs::read_to_string(path).context(error::DnsConfReadFailedSnafu { path })?; | ||
let dns_config = | ||
toml::from_str(&config_str).context(error::DnsConfParseSnafu { path })?; | ||
|
||
Ok(Some(dns_config)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the from_lease_info()
path, if we're given a lease info file with no relevant fields, we return a DnsSettings
struct with both optional fields set to None
.
Would it make sense to do that here as well, and always return a Result<Self>
instead?
// Use DNS API settings if they exist, otherwise use DHCP lease | ||
let dns_settings = if let Some(settings) = | ||
DnsSettings::from_config().context(error::DnsFromConfSnafu)? | ||
{ | ||
settings | ||
} else { | ||
DnsSettings::from_lease_info(&info) | ||
}; | ||
dns_settings | ||
.write_resolv_conf() | ||
.context(error::ResolvConfWriteFailedSnafu)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This relates to from_config()
returning a valid DnsSettings
struct - potentially we want to divide this up rather than making it all API settings or all lease settings:
- if we have name-servers in the config, use those, otherwise use the lease
- if we have a search-list in the config, use it, otherwise use the lease
pub(crate) fn run() -> Result<()> { | ||
// Use DNS API settings if they exist, otherwise use the primary interface's lease | ||
let dns_settings = | ||
if let Some(settings) = DnsSettings::from_config().context(error::DnsFromConfSnafu)? { | ||
settings | ||
} else { | ||
let lease_path = primary_interface_lease()?; | ||
DnsSettings::from_lease_file(lease_path).context(error::DnsFromLeaseSnafu)? | ||
}; | ||
dns_settings | ||
.write_resolv_conf() | ||
.context(error::ResolvConfWriteFailedSnafu)?; | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the merging of the DNS config settings and the lease settings be a helper function? Especially if becomes slightly more complicated than using one source or the other.
We're doing almost but not quite exactly the same thing as in the DHCP helper entry point, which seems like it could lead to bugs.
// If both ipv4 and ipv6 leases exist, use the ipv4 lease for DNS settings | ||
let ipv4_exists = Path::exists(&ipv4); | ||
let ipv6_exists = Path::exists(&ipv6); | ||
match (ipv4_exists, ipv6_exists) { | ||
(true, true) => Ok(ipv4), | ||
(true, false) => Ok(ipv4), | ||
(false, true) => Ok(ipv6), | ||
(false, false) => error::MissingLeaseSnafu { path: LEASE_FOLDER }.fail(), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We entirely ignore the DHCP6 lease coming from the "install" path - it seems like we'd want the behavior to be consistent, otherwise we'll lose the IPv6 version of the settings the next time the lease is renewed and resolv.conf
is rewritten.
^ Addresses @bcressey 's comments. The highlights:
|
^ Above push avoids returning an error if the DHCP lease doesn't exist. The lease won't exist in the case a user is not using DHCP (static addressing). It also makes the DHCP lease optional when constructing a |
^ Rebases on develop and fixes the merge conflict with |
(InterfaceType::Dhcp, InterfaceFamily::Ipv4 | InterfaceFamily::Ipv6) => { | ||
// A lease should exist when using DHCP | ||
let primary_lease_path = | ||
lease_path(&primary_interface).context(error::MissingLeaseSnafu { | ||
interface: primary_interface, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still like to use args.data_file
in some way, if only to check that it matches primary_lease_path
with an early return if it doesn't.
Otherwise we potentially end up rewriting resolv.conf
whenever any DHCP lease is renewed, instead of just the one for the primary interface.
Also, depending on the order in which leases are obtained, we might not have a lease for the primary interface yet early in the boot, if other interfaces are also using DHCP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point about the timing!
Also, there's a check just before the match
statement that returns if the install interface isn't the primary interface
sources/api/netdog/src/dns.rs
Outdated
if self.nameservers.is_none() { | ||
// Randomize name server order, for libc implementations like musl that send | ||
// queries to the first N servers. | ||
let mut dns_servers: Vec<IpAddr> = lease.dns_servers.clone().into_iter().collect(); | ||
dns_servers.shuffle(&mut thread_rng()); | ||
|
||
self.nameservers = Some(BTreeSet::from_iter(dns_servers.into_iter())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to shuffle the nameservers after the merge, just before writing resolv.conf
- to guarantee that they're always shuffled. Here we don't shuffle if the nameservers are set via API.
#[test] | ||
fn empty_config() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a test for missing_config
too, where the file doesn't exist.
sources/api/netdog/src/main.rs
Outdated
@@ -47,6 +52,7 @@ static PRIMARY_INTERFACE: &str = "/var/lib/netdog/primary_interface"; | |||
static DEFAULT_NET_CONFIG_FILE: &str = "/var/lib/bottlerocket/net.toml"; | |||
static PRIMARY_SYSCTL_CONF: &str = "/etc/sysctl.d/90-primary_interface.conf"; | |||
static SYSTEMD_SYSCTL: &str = "/usr/lib/systemd/systemd-sysctl"; | |||
static LEASE_FOLDER: &str = "/var/run/wicked"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: 🪟 -> 🐧
static LEASE_FOLDER: &str = "/var/run/wicked"; | |
static LEASE_DIR: &str = "/var/run/wicked"; |
This commit adds a new `dns` module to centralize the logic of gathering DNS information from an API-generated configuration file, and merging any missing settings from a given DHCP lease. This module also handles writing the `/etc/resolv.conf` using the DNS information.
This change updates the `install` subcommand to make use of the newly-added `dns` module. The most important change here is that the code will use DNS settings from config if they exist, supplementing any missing settings with settings from DHCP lease. The code also takes into account DHCP6 lease renewals when rewriting the `resolv.conf`. When multiple lease files exist, the code uses the DHCP4 lease to gather DHCP lease data.
This commit adds a new `write-resolv-conf` subcommand, which is meant to be used as a restart command for DNS API settings. It will write the `resolv.conf` using DNS settings if they exist, supplementing any missing settings with settings from the DHCP lease of the primary interface if it exists.
This commit adds the ability to configure DNS name servers and search suffixes via a new settings prefix `settings.dns`. These DNS settings populate a configuration file which gets used by `netdog` to write the `resolv.conf`. `netdog write-resolv-conf` is triggered via restart command. If DNS settings exist, they are used to write the `resolv.conf`, otherwise the DHCP lease for the primary interface is used.
This adds the migrations needed for `settings.dns`, one for the setting, its service and configuration file, and one for the setting metadata.
^ Addresses @bcressey 's comments. I also simplified a few tests - I was calling |
Is there plans to merge this into |
assert_ne!(resolv_conf == format1, resolv_conf == format2) | ||
} | ||
|
||
#[test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice tests!
Issue number:
Related to #2204
Description of changes:
It's probably easiest to review this commit by commit, rather than as a whole. All the pieces are pretty small.
Testing done:
aws-k8s-1.22
instance and confirmresolv.conf
is still written correctly with no DNS settingsmetal-dev
image with and without user data and ensureresolv.conf
is written properly with the correct settingsapiclient
and observeresolv.conf
written with desired settings, using DHCP if only one of the settings is set.Terms of contribution:
By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.