Skip to content

Commit

Permalink
update: add support for netavark update command
Browse files Browse the repository at this point in the history
Netavark update allows container managers to update network scoped DNS
servers of any configured network and notify running `aarvark-dns` about
it.

```console
netavark-update
Updates network dns servers for an already configured network

USAGE:
    netavark update --network-dns-servers <NETWORK_DNS_SERVERS> <NETWORK_NAME>

ARGS:
    <NETWORK_NAME>    Network name to update

OPTIONS:
    -h, --help                                         Print help information
    -n, --network-dns-servers <NETWORK_DNS_SERVERS>    DNS Servers to update for the network
```

Signed-off-by: Aditya R <[email protected]>
  • Loading branch information
flouthoc committed Nov 29, 2022
1 parent c8d3c08 commit af1168c
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod setup;
pub mod teardown;
pub mod update;
pub mod version;
80 changes: 80 additions & 0 deletions src/commands/update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::dns::aardvark::Aardvark;
use crate::error::{NetavarkError, NetavarkErrorList, NetavarkResult};

use clap::Parser;
use log::debug;
use std::env;
use std::path::Path;

#[derive(Parser, Debug)]
pub struct Update {
/// Network name to update
#[clap(forbid_empty_values = true, required = true)]
network_name: String,
/// DNS Servers to update for the network
#[clap(short, long, required = true, forbid_empty_values = true)]
network_dns_servers: Vec<String>,
}

impl Update {
/// Updates network dns servers for an already configured network
pub fn new(network_name: String, network_dns_servers: Vec<String>) -> Self {
Self {
network_name,
network_dns_servers,
}
}

pub fn exec(
&self,
config_dir: String,
aardvark_bin: String,
rootless: bool,
) -> NetavarkResult<()> {
let mut error_list = NetavarkErrorList::new();
let dns_port = match env::var("NETAVARK_DNS_PORT") {
Ok(port_string) => match port_string.parse() {
Ok(port) => port,
Err(e) => {
return Err(NetavarkError::Message(format!(
"Invalid NETAVARK_DNS_PORT {}: {}",
port_string, e
)))
}
},
Err(_) => 53,
};

println!(
"config {:?}, aardvarl_bind {:?}, rootless {:?}",
config_dir, aardvark_bin, rootless
);
println!(
"network_name {:?}, network dns server {:?}",
&self.network_name, &self.network_dns_servers
);
if Path::new(&aardvark_bin).exists() {
let path = Path::new(&config_dir).join("aardvark-dns");
if let Ok(path_string) = path.into_os_string().into_string() {
let mut aardvark_interface =
Aardvark::new(path_string, rootless, aardvark_bin, dns_port);
if let Err(err) = aardvark_interface
.modify_network_dns_servers(&self.network_name, &self.network_dns_servers)
{
error_list.push(err.into());
}
} else {
error_list.push(NetavarkError::msg(
"Unable to parse aardvark config directory",
));
}
}

if !error_list.is_empty() {
return Err(NetavarkError::List(error_list));
}

debug!("{:?}", "Update complete");
Ok(())
}
}
55 changes: 55 additions & 0 deletions src/dns/aardvark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,61 @@ impl Aardvark {
Ok(())
}

// Modifies network dns_servers for a specific network and notifies aardvark-dns server
// with the change.
pub fn modify_network_dns_servers(
&mut self,
network_name: &str,
network_dns_servers: &Vec<String>,
) -> Result<()> {
let mut dns_servers_modified = false;
let path = Path::new(&self.config).join(network_name);
let file_content = fs::read_to_string(&path)?;

let mut file = File::create(&path)?;

//for line in lines {
for (idx, line) in file_content.split_terminator('\n').enumerate() {
let mut content = line.to_string();
if idx == 0 {
// If this is first line, we have to modify this
// first line has a format of `<BINDIP>... <NETWORK_DNSSERVERS>..`
// We will read the first line and get the first coloumn and
// override the second coloumn with new network dns servers.
let network_parts = line.split(' ').collect::<Vec<&str>>();
if network_parts.is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("invalid network configuration file: {}", path.display()),
));
}
let network_dns_servers_collected = if !network_dns_servers.is_empty() {
dns_servers_modified = true;
let dns_server_collected = network_dns_servers
.iter()
.map(|g| g.to_string())
.collect::<Vec<String>>()
.join(",");
format!(" {}", dns_server_collected)
} else {
"".to_string()
};
// Modify line to support new format
content = format!("{}{}", network_parts[0], network_dns_servers_collected);
}
file.write_all(content.as_bytes())?;
file.write_all(b"\n")?;
}

// If dns servers were updated notify the aardvark-dns server
// if refresh is needed.
if dns_servers_modified {
self.notify(false)?;
}

Ok(())
}

pub fn delete_from_netavark_entries(
&self,
network_options: &types::NetworkOptions,
Expand Down
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand};

use netavark::commands::setup;
use netavark::commands::teardown;
use netavark::commands::update;
use netavark::commands::version;

#[derive(Parser, Debug)]
Expand All @@ -28,6 +29,8 @@ struct Opts {
enum SubCommand {
/// Configures the given network namespace with the given configuration.
Setup(setup::Setup),
/// Updates network dns servers for an already configured network.
Update(update::Update),
/// Undo any configuration applied via setup command.
Teardown(teardown::Teardown),
/// Display info about netavark.
Expand All @@ -47,6 +50,7 @@ fn main() {
let result = match opts.subcmd {
SubCommand::Setup(setup) => setup.exec(opts.file, config, aardvark_bin, rootless),
SubCommand::Teardown(teardown) => teardown.exec(opts.file, config, aardvark_bin, rootless),
SubCommand::Update(update) => update.exec(config, aardvark_bin, rootless),
SubCommand::Version(version) => version.exec(),
};

Expand Down
41 changes: 41 additions & 0 deletions test/100-bridge-iptables.bats
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,47 @@ fw_driver=iptables
expected_rc=1 run_in_host_netns ip addr show podman0
}

@test "$fw_driver - bridge driver must generate config for aardvark with multiple custom dns server with network dns servers and perform update" {
# get a random port directly to avoid low ports e.g. 53 would not create iptables
dns_port=$((RANDOM+10000))

# hack to make aardvark-dns run when really root or when running as user with
# podman unshare --rootless-netns; since netavark runs aardvark with systemd-run
# it needs to know if it should use systemd user instance or not.
# iptables are still setup identically.
rootless=false
if [[ ! -e "/run/dbus/system_bus_socket" ]]; then
rootless=true
fi

mkdir -p "$NETAVARK_TMPDIR/config"

NETAVARK_DNS_PORT="$dns_port" run_netavark --file ${TESTSDIR}/testfiles/dualstack-bridge-network-container-dns-server.json \
--rootless "$rootless" --config "$NETAVARK_TMPDIR/config" \
setup $(get_container_netns_path)

# check aardvark config and running
run_helper cat "$NETAVARK_TMPDIR/config/aardvark-dns/podman1"
assert "${lines[0]}" =~ "10.89.3.1,fd10:88:a::1 127.0.0.1,3.3.3.3" "aardvark set to listen to all IPs"
assert "${lines[1]}" =~ "^[0-9a-f]{64} 10.89.3.2 fd10:88:a::2 somename 8.8.8.8,1.1.1.1$" "aardvark config's container"
assert "${#lines[@]}" = 2 "too many lines in aardvark config"

aardvark_pid=$(cat "$NETAVARK_TMPDIR/config/aardvark-dns/aardvark.pid")
assert "$ardvark_pid" =~ "[0-9]*" "aardvark pid not found"
run_helper ps "$aardvark_pid"
assert "${lines[1]}" =~ ".*aardvark-dns --config $NETAVARK_TMPDIR/config/aardvark-dns -p $dns_port run" "aardvark not running or bad options"

NETAVARK_DNS_PORT="$dns_port" run_netavark --file ${TESTSDIR}/testfiles/dualstack-bridge-network-container-dns-server.json \
--rootless "$rootless" --config "$NETAVARK_TMPDIR/config" \
update podman1 --network-dns-servers 8.8.8.8

# check aardvark config and running
run_helper cat "$NETAVARK_TMPDIR/config/aardvark-dns/podman1"
assert "${lines[0]}" =~ "10.89.3.1,fd10:88:a::1 8.8.8.8" "aardvark set to listen to all IPs"
assert "${lines[1]}" =~ "^[0-9a-f]{64} 10.89.3.2 fd10:88:a::2 somename 8.8.8.8,1.1.1.1$" "aardvark config's container"
assert "${#lines[@]}" = 2 "too many lines in aardvark config"
}

@test "$fw_driver - ipv6 bridge" {
run_netavark --file ${TESTSDIR}/testfiles/ipv6-bridge.json setup $(get_container_netns_path)
result="$output"
Expand Down

0 comments on commit af1168c

Please sign in to comment.