-
Notifications
You must be signed in to change notification settings - Fork 510
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
dogtag: implement imds and reverse dns hostname tools #3898
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,6 +89,8 @@ members = [ | |
|
||
"imdsclient", | ||
|
||
"dogtag", | ||
|
||
"driverdog", | ||
|
||
"early-boot-config/early-boot-config", | ||
|
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 = ["macros"]} # LTS | ||
walkdir = "2" | ||
|
||
[build-dependencies] | ||
generate-readme = { version = "0.1", path = "../generate-readme" } |
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`. |
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`. |
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 | ||
Comment on lines
+7
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think these comments apply; |
||
#[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"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: "IMDS" |
||
NoHostname, | ||
} | ||
} |
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>, | ||
}, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
generate_readme::from_lib().unwrap() | ||
} |
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"; | ||
jmt-lab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// 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 | ||
jmt-lab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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); | ||
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see where we are passing the |
||
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 {}) | ||
Comment on lines
+51
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original logic in ab3b3e6 had a fallback where the host's IP address was used in a slightly cleaned up way (dots replaced with dashes) in these two cases:
That logic was later removed in 0de2d11 since in some cases (EKS-A clusters) we want the hostname to exactly match the IP. 0b9658f fixed a different bug related to direct use of an IPv6 address, where colons needed to be replaced with dashes. Is that handled in |
||
} | ||
|
||
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, | ||
} | ||
} |
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 should not ship the IMDS resolver on non-aws variants.