Skip to content
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

update: add support for netavark update command #503

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
14 changes: 1 addition & 13 deletions src/commands/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::network::{core_utils, types};
use clap::Parser;
use log::{debug, error, info};
use std::collections::HashMap;
use std::env;
use std::fs::{self};
use std::path::Path;

Expand Down Expand Up @@ -51,18 +50,7 @@ impl Setup {

let mut response: HashMap<String, types::StatusBlock> = HashMap::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,
};
let dns_port = core_utils::get_netavark_dns_port()?;

let (mut hostns, mut netns) =
core_utils::open_netlink_sockets(&self.network_namespace_path)?;
Expand Down
16 changes: 1 addition & 15 deletions src/commands/teardown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::network::driver::{get_network_driver, DriverInfo};
use crate::{firewall, network};
use clap::Parser;
use log::debug;
use std::env;
use std::path::Path;

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -36,20 +35,7 @@ impl Teardown {

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) => {
error_list.push(NetavarkError::Message(format!(
"Invalid NETAVARK_DNS_PORT {}: {}",
port_string, e
)));
// default to 53 if there is a error here, we should not prevent cleanup because of it
53
}
},
Err(_) => 53,
};
let dns_port = core_utils::get_netavark_dns_port()?;

if Path::new(&aardvark_bin).exists() {
// stop dns server first before netavark clears the interface
Expand Down
59 changes: 59 additions & 0 deletions src/commands/update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::dns::aardvark::Aardvark;
use crate::error::{NetavarkError, NetavarkResult};
use crate::network::core_utils;

use clap::Parser;
use log::debug;
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(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 dns_port = core_utils::get_netavark_dns_port()?;

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 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)
{
return Err(NetavarkError::wrap(
"unable to modify network dns servers",
NetavarkError::Io(err),
));
}
} else {
return Err(NetavarkError::msg(
"Unable to parse aardvark config directory",
));
}
}

debug!("Network update complete");
Ok(())
}
}
56 changes: 56 additions & 0 deletions src/dns/aardvark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,62 @@ 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(
&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() {
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
let content = format!("{}{}", network_parts[0], network_dns_servers_collected);
file.write_all(content.as_bytes())?;
} else {
file.write_all(line.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
14 changes: 14 additions & 0 deletions src/network/core_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use netlink_packet_route::{
use nix::sched;
use sha2::{Digest, Sha512};
use std::collections::HashMap;
use std::env;
use std::fmt::Display;
use std::fs::File;
use std::io::{self, Error};
Expand All @@ -25,6 +26,19 @@ pub struct CoreUtils {
pub networkns: String,
}

pub fn get_netavark_dns_port() -> Result<u16, NetavarkError> {
match env::var("NETAVARK_DNS_PORT") {
Ok(port_string) => match port_string.parse() {
Ok(port) => Ok(port),
Err(e) => Err(NetavarkError::Message(format!(
"Invalid NETAVARK_DNS_PORT {}: {}",
port_string, e
))),
},
Err(_) => Ok(53),
}
}

pub fn parse_option<T>(
opts: &Option<HashMap<String, String>>,
name: &str,
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))
Comment on lines +115 to +116
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not relevant to the test, just remove it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not understand this bit, we are just getting a random port for the test since we are still starting the aardvark.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean don't pass anything and default to 53 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but now that I look at it again I think this is fine since we should make sure the changed dns port still works.


# 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