Skip to content

Commit

Permalink
dogtag: implement imds and reverse dns hostname tools
Browse files Browse the repository at this point in the history
  • Loading branch information
jmt-lab committed Apr 16, 2024
1 parent f196bda commit 8948a57
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 0 deletions.
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.

2 changes: 2 additions & 0 deletions sources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ members = [

"imdsclient",

"dogtag",

"driverdog",

"early-boot-config/early-boot-config",
Expand Down
30 changes: 30 additions & 0 deletions sources/dogtag/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "dogtag"
version = "0.1.0"
authors = ["Jarrett Tierney <[email protected]>"]
license = "Apache-2.0 OR MIT"
edition = "2021"
publish = false
exclude = ["README.md"]

[[bin]]
name = "20-imds"
path = "bin/imds.rs"

[[bin]]
name = "10-reverse-dns"
path = "bin/reverse.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
argh = "0.1"
dns-lookup = "2"
imdsclient = { version = "0.1", path = "../imdsclient" }
log = "0.4"
snafu = "0.8"
tokio = { version = "1.32", features = ["full"]}
walkdir = "2.4"

[build-dependencies]
generate-readme = { version = "0.1", path = "../generate-readme" }
16 changes: 16 additions & 0 deletions sources/dogtag/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# dogtag

Current version: 0.1.0

dogtag resolves the hostname of a bottlerocket server/instance. It's used to generate settings.network.hostname. To accomplish this, it uses a set of standalone binaries in /var/bottlerocket/dogtag that resolve the hostname via different methods.

Currently, bottlerocket ships with two hostname resolver binaries:

20-imds - Fetches hostname from EC2 Instance Metadata Service
10-reverse-dns - Uses reverse DNS lookup to resolve the hostname

dogtag runs the resolvers in /var/bottlerocket/dogtag in reverse alphanumerical order until one of them returns a hostname, at which point it will exit early and print the returned hostname to stdout.

## Colophon

This text was generated from `README.tpl` using [cargo-readme](https://crates.io/crates/cargo-readme), and includes the rustdoc from `src/main.rs`.
9 changes: 9 additions & 0 deletions sources/dogtag/README.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# {{crate}}

Current version: {{version}}

{{readme}}

## Colophon

This text was generated from `README.tpl` using [cargo-readme](https://crates.io/crates/cargo-readme), and includes the rustdoc from `src/main.rs`.
39 changes: 39 additions & 0 deletions sources/dogtag/bin/imds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use dogtag::Cli;
use imdsclient::ImdsClient;
use snafu::{OptionExt, ResultExt};

type Result<T> = std::result::Result<T, error::Error>;

/// Implements a hostname lookup tool by fetching the public hostname
/// from the instance metadata via IMDS. It will interface with IMDS
/// via:
///
/// * Check for IPv6, default to IPv4 if not available
/// * Check for IMDSv2, fallback to IMDSv1 if not enabled
#[tokio::main]
async fn main() -> Result<()> {
// Even though for this helper we do not need any arguments
// still validate to ensure the helper follows standards.
let _: Cli = argh::from_env();
let mut imds = ImdsClient::new();
let hostname = imds
.fetch_hostname()
.await
.context(error::ImdsSnafu)?
.context(error::NoHostnameSnafu)?;
println!("{}", hostname);
Ok(())
}

mod error {
use snafu::Snafu;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(super)))]
pub(super) enum Error {
#[snafu(display("failed to fetch hostname from IMDS: {}", source))]
Imds { source: imdsclient::Error },
#[snafu(display("no hostname returned by imds"))]
NoHostname,
}
}
34 changes: 34 additions & 0 deletions sources/dogtag/bin/reverse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use dns_lookup::lookup_addr;
use dogtag::Cli;
use snafu::ResultExt;

type Result<T> = std::result::Result<T, error::Error>;

/// Looks up the public hostname by using dns-lookup to
/// resolve it from the ip address provided
fn main() -> Result<()> {
let cli: Cli = argh::from_env();
let ip: std::net::IpAddr = cli.ip_address.parse().context(error::InvalidIpSnafu)?;
let hostname = lookup_addr(&ip).context(error::LookupSnafu)?;
println!("{}", hostname);
Ok(())
}

mod error {
use snafu::Snafu;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(super)))]
pub(super) enum Error {
#[snafu(display("Invalid ip address passed to tool {}", source))]
InvalidIp {
#[snafu(source(from(std::net::AddrParseError, Box::new)))]
source: Box<std::net::AddrParseError>,
},
#[snafu(display("Failed to lookup hostname via dns {}", source))]
Lookup {
#[snafu(source(from(std::io::Error, Box::new)))]
source: Box<std::io::Error>,
},
}
}
3 changes: 3 additions & 0 deletions sources/dogtag/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
generate_readme::from_lib().unwrap()
}
84 changes: 84 additions & 0 deletions sources/dogtag/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*!
dogtag resolves the hostname of a bottlerocket server/instance. It's used to generate settings.network.hostname. To accomplish this, it uses a set of standalone binaries in /var/bottlerocket/dogtag that resolve the hostname via different methods.
Currently, bottlerocket ships with two hostname resolver binaries:
20-imds - Fetches hostname from EC2 Instance Metadata Service
10-reverse-dns - Uses reverse DNS lookup to resolve the hostname
dogtag runs the resolvers in /var/bottlerocket/dogtag in reverse alphanumerical order until one of them returns a hostname, at which point it will exit early and print the returned hostname to stdout.
*/
use argh::FromArgs;
use log::debug;
use snafu::ResultExt;
use std::{path::PathBuf, process};
use walkdir::WalkDir;

const DOGTAG_BIN_PATH: &str = "/var/bottlerocket/dogtag";

/// Cli defines the standard cmdline interface for all hostname handlers
#[derive(FromArgs)]
#[argh(description = "hostname resolution tool")]
pub struct Cli {
#[argh(option)]
#[argh(description = "ip_address of the host")]
pub ip_address: String,
}

pub type Result<T> = std::result::Result<T, error::Error>;

/// find_hostname will utilize the helpers located in /var/bottlerocket/dogtag/ to try and discover the hostname
pub async fn find_hostname() -> Result<String> {
debug!(
"attempting to discover hostname helpers in {}",
DOGTAG_BIN_PATH
);
// We want to do reverse sort as we want to prioritize higher numbers first
// this is because it makes it easier to add more of these and not have to worry about
// bumping the binary name for existing ones
let mut hostname_helpers: Vec<PathBuf> = WalkDir::new(DOGTAG_BIN_PATH)
.max_depth(1)
.min_depth(1)
.sort_by_file_name()
.into_iter()
.collect::<std::result::Result<Vec<_>, _>>()
.context(error::WalkdirSnafu)?
.into_iter()
.map(|x| x.into_path())
.collect();
hostname_helpers.reverse();

for helper in hostname_helpers.iter() {
let output = process::Command::new(helper)
.output()
.map(Some)
.unwrap_or(None);
if let Some(output) = output.as_ref() {
// Read the std output
if output.status.success() {
let hostname = String::from_utf8_lossy(output.stdout.as_slice()).to_string();
return Ok(hostname.trim().to_string());
}
}
}
Err(error::Error::NoHelper {})
}

pub mod error {
use snafu::Snafu;

#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
pub enum Error {
#[snafu(display("Failed to detect hostname due to an io error: {}", source))]
Walkdir { source: walkdir::Error },
#[snafu(display(
"Failed to detect hostname, no helpers are installed in path or io error occurred"
))]
NoHelper,
#[snafu(display(
"Failed to detect hostname, no helper installed was able to resolve the hostname"
))]
FailHostname,
}
}

0 comments on commit 8948a57

Please sign in to comment.