Skip to content

Commit

Permalink
Merge pull request #2353 from zmrow/dns-settings
Browse files Browse the repository at this point in the history
Add configurable DNS settings
  • Loading branch information
zmrow authored Sep 1, 2022
2 parents 63365f6 + 9659374 commit a3e92e9
Show file tree
Hide file tree
Showing 35 changed files with 587 additions and 77 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,17 @@ In addition to the container runtime daemons, these credential settings will als
10.1.1.1 test2.example.com
```

The following allows for custom DNS settings, which are used to generate the `/etc/resolv.conf`.
If either DNS setting is not populated, the system will use the DHCP lease of the primary interface to gather these setings.
See the `resolv.conf` [man page](https://man7.org/linux/man-pages/man5/resolv.conf.5.html) for more detail.
* `settings.dns.name-servers`: An array of IP address strings that represent the desired name server(s).
* `settings.dns.search-list`: An array of domain strings that represent the desired domain search path(s).
```
[settings.dns]
name-servers = ["1.2.3.4", "5.6.7.8"]
search-list = ["foo.bar", "baz.foo"]
```

##### Proxy settings

These settings will configure the proxying behavior of the following services:
Expand Down
4 changes: 4 additions & 0 deletions Release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,7 @@ version = "1.9.2"
]
"(1.9.0, 1.9.1)" = []
"(1.9.1, 1.9.2)" = []
"(1.9.2, 1.10.0)" = [
"migrate_v1.10.0_dns-settings.lz4",
"migrate_v1.10.0_dns-settings-metadata.lz4",
]
6 changes: 6 additions & 0 deletions packages/release/netdog.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{#if settings.dns.name-servers}}
name-servers = [{{join_array ", " settings.dns.name-servers }}]
{{/if}}
{{#if settings.dns.search-list}}
search-list = [{{join_array ", " settings.dns.search-list }}]
{{/if}}
3 changes: 3 additions & 0 deletions packages/release/release.spec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Source201: proxy-env
Source202: hostname-env
Source203: hosts.template
Source204: modprobe-conf.template
Source205: netdog.template

Source1001: multi-user.target
Source1002: configured.target
Expand Down Expand Up @@ -167,6 +168,7 @@ install -p -m 0644 %{S:201} %{buildroot}%{_cross_templatedir}/proxy-env
install -p -m 0644 %{S:202} %{buildroot}%{_cross_templatedir}/hostname-env
install -p -m 0644 %{S:203} %{buildroot}%{_cross_templatedir}/hosts
install -p -m 0644 %{S:204} %{buildroot}%{_cross_templatedir}/modprobe-conf
install -p -m 0644 %{S:205} %{buildroot}%{_cross_templatedir}/netdog-toml

install -d %{buildroot}%{_cross_udevrulesdir}
install -p -m 0644 %{S:1016} %{buildroot}%{_cross_udevrulesdir}/61-mount-cdrom.rules
Expand Down Expand Up @@ -216,6 +218,7 @@ ln -s preconfigured.target %{buildroot}%{_cross_unitdir}/default.target
%{_cross_unitdir}/systemd-tmpfiles-setup.service.d/00-debug.conf
%dir %{_cross_templatedir}
%{_cross_templatedir}/modprobe-conf
%{_cross_templatedir}/netdog-toml
%{_cross_templatedir}/motd
%{_cross_templatedir}/proxy-env
%{_cross_templatedir}/hostname-env
Expand Down
14 changes: 14 additions & 0 deletions sources/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion sources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ members = [
"api/prairiedog",

# "api/migration/migrations/vX.Y.Z/..."
# (all migrations currently archived; replace this line with new ones)
"api/migration/migrations/v1.10.0/dns-settings",
"api/migration/migrations/v1.10.0/dns-settings-metadata",

"bottlerocket-release",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "dns-settings-metadata"
version = "0.1.0"
license = "Apache-2.0 OR MIT"
edition = "2018"
publish = false

[dependencies]
migration-helpers = { path = "../../../migration-helpers", version = "0.1.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![deny(rust_2018_idioms)]

use migration_helpers::common_migrations::{AddMetadataMigration, SettingMetadata};
use migration_helpers::{migrate, Result};
use std::process;

/// We added a new setting and `affected-services` metadata for `settings.dns`
fn run() -> Result<()> {
migrate(AddMetadataMigration(&[SettingMetadata {
metadata: &["affected-services"],
setting: "settings.dns",
}]))
}

// Returning a Result from main makes it print a Debug representation of the error, but with Snafu
// we have nice Display representations of the error, so we wrap "main" (run) and print any error.
// https://github.com/shepmaster/snafu/issues/110
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
process::exit(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "dns-settings"
version = "0.1.0"
license = "Apache-2.0 OR MIT"
edition = "2018"
publish = false

[dependencies]
migration-helpers = { path = "../../../migration-helpers", version = "0.1.0"}
24 changes: 24 additions & 0 deletions sources/api/migration/migrations/v1.10.0/dns-settings/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![deny(rust_2018_idioms)]

use migration_helpers::common_migrations::AddPrefixesMigration;
use migration_helpers::{migrate, Result};
use std::process;

/// We added new settings under `settings.dns` for configuring /etc/resolv.conf
fn run() -> Result<()> {
migrate(AddPrefixesMigration(vec![
"settings.dns",
"services.dns",
"configuration-files.netdog-toml",
]))
}

// Returning a Result from main makes it print a Debug representation of the error, but with Snafu
// we have nice Display representations of the error, so we wrap "main" (run) and print any error.
// https://github.com/shepmaster/snafu/issues/110
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
process::exit(1);
}
}
4 changes: 4 additions & 0 deletions sources/api/netdog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ valid example: `netdog.default-interface=eno1:dhcp4,dhcp6?`.
The subcommand `prepare-primary-interface` writes the default sysctls for the primary interface to
file in `/etc/sysctl.d`, and then executes `systemd-sysctl` to apply them.

The subcommand `write-resolv-conf` writes the resolv.conf, favoring DNS API settings and
supplementing any missing settings with DNS settings from the primary interface's DHCP lease. It
is meant to be used as a restart command for DNS API settings.

## Colophon

This text was generated using [cargo-readme](https://crates.io/crates/cargo-readme), and includes the rustdoc from `src/main.rs`.
60 changes: 27 additions & 33 deletions sources/api/netdog/src/cli/install.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use super::{error, InterfaceFamily, InterfaceType, Result};
use crate::lease::LeaseInfo;
use crate::{CURRENT_IP, PRIMARY_INTERFACE, RESOLV_CONF};
use crate::dns::DnsSettings;
use crate::lease::{lease_path, LeaseInfo};
use crate::{CURRENT_IP, PRIMARY_INTERFACE};
use argh::FromArgs;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use snafu::ResultExt;
use std::fmt::Write;
use snafu::{OptionExt, ResultExt};
use std::fs;
use std::net::IpAddr;
use std::path::PathBuf;
Expand Down Expand Up @@ -52,37 +50,33 @@ pub(crate) fn run(args: InstallArgs) -> Result<()> {
}

match (&args.interface_type, &args.interface_family) {
(InterfaceType::Dhcp, InterfaceFamily::Ipv4) => {
let info =
LeaseInfo::from_lease(&args.data_file).context(error::LeaseParseFailedSnafu {
path: &args.data_file,
(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,
})?;
// Randomize name server order, for libc implementations like musl that send
// queries to the first N servers.
let mut dns_servers: Vec<_> = info.dns_servers.iter().collect();
dns_servers.shuffle(&mut thread_rng());
write_resolv_conf(&dns_servers, &info.dns_search)?;
write_current_ip(&info.ip_address.addr())?;
}
_ => eprintln!("Unhandled 'install' command: {:?}", &args),
}
Ok(())
}
if args.data_file != primary_lease_path {
return error::PrimaryLeaseConflictSnafu {
wicked_path: args.data_file,
generated_path: primary_lease_path,
}
.fail();
}

/// Write resolver configuration for libc.
fn write_resolv_conf(dns_servers: &[&IpAddr], dns_search: &Option<Vec<String>>) -> Result<()> {
let mut output = String::new();
// Use DNS API settings if they exist, supplementing any missing settings with settings
// derived from the primary interface's DHCP lease
let lease =
LeaseInfo::from_lease(primary_lease_path).context(error::LeaseParseFailedSnafu)?;
let dns_settings = DnsSettings::from_config_or_lease(Some(&lease))
.context(error::GetDnsSettingsSnafu)?;
dns_settings
.write_resolv_conf()
.context(error::ResolvConfWriteFailedSnafu)?;

if let Some(s) = dns_search {
writeln!(output, "search {}", s.join(" ")).context(error::ResolvConfBuildFailedSnafu)?;
}

for n in dns_servers {
writeln!(output, "nameserver {}", n).context(error::ResolvConfBuildFailedSnafu)?;
write_current_ip(&lease.ip_address.addr())?;
}
}

fs::write(RESOLV_CONF, output)
.context(error::ResolvConfWriteFailedSnafu { path: RESOLV_CONF })?;
Ok(())
}

Expand Down
28 changes: 21 additions & 7 deletions sources/api/netdog/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) mod node_ip;
pub(crate) mod prepare_primary_interface;
pub(crate) mod remove;
pub(crate) mod set_hostname;
pub(crate) mod write_resolv_conf;

pub(crate) use generate_hostname::GenerateHostnameArgs;
pub(crate) use generate_net_config::GenerateNetConfigArgs;
Expand All @@ -15,6 +16,7 @@ pub(crate) use remove::RemoveArgs;
use serde::{Deserialize, Serialize};
pub(crate) use set_hostname::SetHostnameArgs;
use snafu::ResultExt;
pub(crate) use write_resolv_conf::WriteResolvConfArgs;

#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "kebab-case")]
Expand Down Expand Up @@ -46,7 +48,7 @@ where

/// Potential errors during netdog execution
mod error {
use crate::{lease, net_config, wicked};
use crate::{dns, lease, net_config, wicked};
use snafu::Snafu;
use std::io;
use std::path::PathBuf;
Expand All @@ -61,6 +63,12 @@ mod error {
#[snafu(display("Failed to read current IP data in '{}': {}", path.display(), source))]
CurrentIpReadFailed { path: PathBuf, source: io::Error },

#[snafu(display("Unable to gather DNS settings: {}", source))]
GetDnsSettings { source: dns::Error },

#[snafu(display("Failed to read/parse DNS settings from DHCP lease: {}", source))]
DnsFromLease { source: dns::Error },

#[snafu(display("'systemd-sysctl' failed: {}", stderr))]
FailedSystemdSysctl { stderr: String },

Expand All @@ -85,8 +93,11 @@ mod error {
source: serde_json::error::Error,
},

#[snafu(display("Failed to read/parse lease data in '{}': {}", path.display(), source))]
LeaseParseFailed { path: PathBuf, source: lease::Error },
#[snafu(display("Failed to read/parse lease data: {}", source))]
LeaseParseFailed { source: lease::Error },

#[snafu(display("No DHCP lease found for interface '{}'", interface))]
MissingLease { interface: String },

#[snafu(display("Unable to read/parse network config from '{}': {}", path.display(), source))]
NetConfigParse {
Expand All @@ -100,11 +111,14 @@ mod error {
#[snafu(display("Failed to read primary interface from '{}': {}", path.display(), source))]
PrimaryInterfaceRead { path: PathBuf, source: io::Error },

#[snafu(display("Failed to build resolver configuration: {}", source))]
ResolvConfBuildFailed { source: std::fmt::Error },
#[snafu(display("Conflicting primary lease location; from wicked: '{}', generated by netdog: '{}'", wicked_path.display(), generated_path.display()))]
PrimaryLeaseConflict {
wicked_path: PathBuf,
generated_path: PathBuf,
},

#[snafu(display("Failed to write resolver configuration to '{}': {}", path.display(), source))]
ResolvConfWriteFailed { path: PathBuf, source: io::Error },
#[snafu(display("Failed to write resolver configuration: {}", source))]
ResolvConfWriteFailed { source: dns::Error },

#[snafu(display("Failed to build sysctl config: {}", source))]
SysctlConfBuild { source: std::fmt::Error },
Expand Down
37 changes: 37 additions & 0 deletions sources/api/netdog/src/cli/write_resolv_conf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use super::{error, Result};
use crate::dns::DnsSettings;
use crate::lease::{lease_path, LeaseInfo};
use crate::PRIMARY_INTERFACE;
use argh::FromArgs;
use snafu::ResultExt;
use std::fs;

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "write-resolv-conf")]
/// Writes /etc/resolv.conf, using DNS API settings if they exist
pub(crate) struct WriteResolvConfArgs {}

pub(crate) fn run() -> Result<()> {
// Use DNS API settings if they exist, supplementing any missing settings with settings
// derived from the primary interface's DHCP lease if it exists
let primary_interface = fs::read_to_string(PRIMARY_INTERFACE)
.context(error::PrimaryInterfaceReadSnafu {
path: PRIMARY_INTERFACE,
})?
.trim()
.to_lowercase();

let primary_lease_path = lease_path(&primary_interface);
let dns_settings = if let Some(primary_lease_path) = primary_lease_path {
let lease =
LeaseInfo::from_lease(&primary_lease_path).context(error::LeaseParseFailedSnafu)?;
DnsSettings::from_config_or_lease(Some(&lease)).context(error::GetDnsSettingsSnafu)?
} else {
DnsSettings::from_config_or_lease(None).context(error::GetDnsSettingsSnafu)?
};

dns_settings
.write_resolv_conf()
.context(error::ResolvConfWriteFailedSnafu)?;
Ok(())
}
Loading

0 comments on commit a3e92e9

Please sign in to comment.