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

Add imdsclient library for querying IMDS #1372

Merged
merged 4 commits into from
May 21, 2021

Conversation

jpculp
Copy link
Member

@jpculp jpculp commented Mar 9, 2021

Issue number:

N/A

Description of changes:

This adds an imdsclient library for tools and setting generators to leverage for fetching user-data, meta-data, and dynamic data from the AWS Instance Metadata Service.

shibaken was also updated by removing the stand-alone IMDS logic in favor of the shared imdsclient library.
Some of the log messages in shibaken were also tweaked for more clarity.

pluto and early-boot-config have also been updated by removing the stand-alone IMDS logic in favor of the shared imdsclient library.

Packages that need to be updated:

  • shibaken
  • pluto
  • early-boot-config

Issues:

Testing done:

  • Built aws-k8s-1.19 ami and launched instance.
  • Instance connected to eks cluster.
  • Connected to control container via ssm session.
  • Verified that host-containers.admin.user-data contained a base64-encoded block.
  • Connected to admin container via ssh.
  • Verified that /.bottlerocket/host-containers/admin/user-data contained JSON.
  • Ran sudo sheltie to verify root shell was still available.
  • Checked for failed systemd units.
  • Ran pluto with it's sub-commands to verify functionality.

Also built and launched vmware-k8s-1.20 (x86_64) and aws-ecs-1 (aarch64).
Verified that they booted correctly and connected to their respective clusters.

Terms of contribution:

By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.

@jpculp
Copy link
Member Author

jpculp commented Mar 9, 2021

Seems like the examples in the Doc strings aren't tight enough to pass Doc-tests. Will post another revision.

sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
@@ -191,7 +158,7 @@ fn parse_args(args: env::Args) -> Result<Args> {
}

Ok(Args {
log_level: log_level.unwrap_or_else(|| LevelFilter::Info),
log_level: log_level.unwrap_or(LevelFilter::Info),
Copy link
Member Author

Choose a reason for hiding this comment

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

Since sundog only prints stderr in the event of an error, would it make sense for me to change default log level to Debug? It would make it easier to troubleshoot, but might be somewhat ugly if someone ran shibaken in a shell.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we not have enough information in error/warn/info to get a sense of the problem when it fails? It seems like we should be adding information there, not just promoting all of the debug messages, if you think there's not enough to understand the problem.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's kind of an edge-case, but the current IMDS logic in Bottlerocket treats a 404 as an Ok(None) and that's what we want 99% of the time. However, in shibaken we decided to actually error out if it receives a 404 when it is collecting the data from the individual keys in IMDS. Since the imdsclient isn't returning an error on 404, some context is lost.

At an error level shibaken would return something like:

Error retrieving 'public-keys/1/openssh-key' from IMDS metadata

but the debug message would show the full uri like this:

(2) reqwest::async_impl::client: response '404 Not Found' for http://169.254.169.254/2020-10-27/meta-data/public-keys/1/openssh-key

I can update the error to say 404 for clarity, but I'm not quite sure if there is a simple way to pass the full uri. One option is to re-construct the uri via public constants, but I'm not a fan of that since it's not a guarantee that that was the uri that was reqwest'd

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the full URI is super helpful, since there's only one thing it could possibly be, given you know the version you're running. And this case could seemingly only crop up if EC2 listed erroneous keys, or if the user changed the registered keys after the list was retrieved. I don't think it's possible to change the keys associated in EC2 while an instance is running, so I don't expect that particular issue to come up. The error seems fine to me...

Copy link
Contributor

@webern webern Mar 15, 2021

Choose a reason for hiding this comment

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

I think the error message Error retrieving 'public-keys/1/openssh-key' from IMDS metadata is clear enough. If you really do have a reproducible case where you need to know whether it was a 404 or something else, then you are really in the weeds anyway and it seems reasonable to expect the user/developer to turn on debugging for something that deep.

@jpculp jpculp marked this pull request as draft March 9, 2021 01:53
Copy link
Contributor

@zmrow zmrow left a comment

Choose a reason for hiding this comment

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

Posting what I have for now, will be back with a few more!

sources/imdsclient/build.rs Outdated Show resolved Hide resolved
sources/imdsclient/README.md Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Show resolved Hide resolved
Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

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

Can tests be written with a mock server? You would need another constructor (which could be private) that takes the URLs. If this constructor were public, you could also use it in your doc example for a passing doc test. You could then implement Default for the real clients to use.

sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
@jpculp
Copy link
Member Author

jpculp commented Mar 12, 2021

  • Removed examples from docstrings.
  • Made the ImdsClient struct mutable.
  • Added refresh_token function. When receiving a 401 response from IMDS, the token will be refreshed, and the IMDS fetch be re-attempted 1 time.
  • Tweaked README wording.
  • Removed extraneous generate_constants from build.rs

sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
@jpculp
Copy link
Member Author

jpculp commented Mar 13, 2021

  • Add docstring to refresh_token
  • Tweaked ImdsData error in shibaken to be more visually consistent with ImdsClient error.

@jpculp jpculp marked this pull request as ready for review March 13, 2021 00:59
@jpculp jpculp requested review from zmrow and webern March 13, 2021 00:59
@@ -191,7 +158,7 @@ fn parse_args(args: env::Args) -> Result<Args> {
}

Ok(Args {
log_level: log_level.unwrap_or_else(|| LevelFilter::Info),
log_level: log_level.unwrap_or(LevelFilter::Info),
Copy link
Contributor

@webern webern Mar 15, 2021

Choose a reason for hiding this comment

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

I think the error message Error retrieving 'public-keys/1/openssh-key' from IMDS metadata is clear enough. If you really do have a reproducible case where you need to know whether it was a 404 or something else, then you are really in the weeds anyway and it seems reasonable to expect the user/developer to turn on debugging for something that deep.

sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@etungsten etungsten left a comment

Choose a reason for hiding this comment

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

LGTM assuming all the existing comments are addressed.

sources/api/shibaken/src/main.rs Outdated Show resolved Hide resolved
}

/// Helper to fetch `meta-data` targets from IMDS, preferring an override file if present.
pub fn fetch_metadata(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit worried about this API since we're not handling these strings as URLs and we're not doing any handling of slashes, etc. Ideally we'd at least try to parse the string as a URL so we at least know it's valid.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was going to rely on the str -> Url parse in Reqwest's get function, but it probably makes sense to parse the uri just before the loop.

sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
@jpculp jpculp mentioned this pull request Mar 16, 2021
@jpculp
Copy link
Member Author

jpculp commented Mar 16, 2021

Can tests be written with a mock server? You would need another constructor (which could be private) that takes the URLs. If this constructor were public, you could also use it in your doc example for a passing doc test. You could then implement Default for the real clients to use. -@webern

I've gone ahead and added an issue (#1398) for this so that we can revisit it at a later date.

@jpculp
Copy link
Member Author

jpculp commented Mar 16, 2021

  • Replaced &str with AsRef<str> for public functions
  • Changed attempt from u16 to u8
  • Created max_attempts variable
  • Changed session token expired message from warn to info
  • Tweaked a description string in shibaken for a clearer log message.

@jpculp jpculp requested review from zmrow and webern March 16, 2021 23:13
Copy link
Contributor

@zmrow zmrow left a comment

Choose a reason for hiding this comment

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

🔑

@jpculp
Copy link
Member Author

jpculp commented May 7, 2021

Fixed and tested vmware and aws-ecs builds.

@jpculp
Copy link
Member Author

jpculp commented May 12, 2021

Added tests to imdsclient.

@jpculp
Copy link
Member Author

jpculp commented May 17, 2021

  • In collaboration with @webern, moved IMDS functions from shibaken, pluto, and early-boot-config, and replaced them with more abstracted functions like fetch_mac_addresses.
  • Replaced fetch_metadata / fetch_dynamic with fetch_string / fetch_bytes.
  • Made refresh_token private.
  • Removed requirement to always add a description value.
  • @webern added some additional tests for printable strings and parsing public keys.

@jpculp jpculp marked this pull request as ready for review May 17, 2021 22:05
Copy link
Contributor

@webern webern left a comment

Choose a reason for hiding this comment

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

Nice refactor with new test code. 👍

sources/api/pluto/src/main.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
@jpculp
Copy link
Member Author

jpculp commented May 20, 2021

Tweaked the two error messages from @webern's feedback.

sources/Cargo.lock Show resolved Hide resolved
sources/imdsclient/src/lib.rs Outdated Show resolved Hide resolved
sources/imdsclient/src/lib.rs Show resolved Hide resolved
@jpculp
Copy link
Member Author

jpculp commented May 20, 2021

imdsclient

  • Changed number of retries.
  • Added 100ms delay between retries.
  • Added logic to retry on request timeouts (instead of just expired tokens).

This adds an imdsclient library for tools and setting generators to
leverage for fetching user-data, meta-data, and dynamic data from the
AWS Instance Metadata Service.
This removes the stand-alone IMDS logic in favor of the shared
imdsclient library.

Log messages were also tweaked for more clarity.
This removes the stand-alone IMDS logic in favor of the shared
imdsclient library.
This removes the stand-alone IMDS logic in favor of the shared
imdsclient library.
@jpculp
Copy link
Member Author

jpculp commented May 21, 2021

imdsclient

  • Removed extraneous semicolon.

@jpculp jpculp merged commit b25435d into bottlerocket-os:develop May 21, 2021
@jpculp jpculp deleted the imdsclient-lib branch May 21, 2021 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants