diff --git a/.gitignore b/.gitignore index 883a290..c6044b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules *.env +mantle.yml *.DS_Store diff --git a/mantle/Cargo.lock b/mantle/Cargo.lock index 3c19214..dc1f6b5 100644 --- a/mantle/Cargo.lock +++ b/mantle/Cargo.lock @@ -389,6 +389,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "const-random" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "tiny-keccak", +] + [[package]] name = "colorchoice" version = "1.0.1" @@ -738,6 +758,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -1972,6 +2001,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f" +dependencies = [ + "dlv-list", + "hashbrown 0.14.3", +] + [[package]] name = "output_vt100" version = "0.1.3" @@ -2519,6 +2558,7 @@ dependencies = [ "chrono", "clap 2.34.0", "difference", + "dirs-next", "glob", "log", "logger", @@ -2527,6 +2567,8 @@ dependencies = [ "rbxcloud", "rusoto_core", "rusoto_s3", + "rusoto_sts", + "rust-ini", "schemars", "serde", "serde_yaml", @@ -2806,8 +2848,33 @@ dependencies = [ "hyper-tls", "lazy_static", "log", - "rusoto_credential", - "rusoto_signature", + "rusoto_credential 0.47.0", + "rusoto_signature 0.47.0", + "rustc_version 0.4.0", + "serde", + "serde_json", + "tokio", + "xml-rs", +] + +[[package]] +name = "rusoto_core" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bytes", + "crc32fast", + "futures", + "http", + "hyper", + "hyper-tls", + "lazy_static", + "log", + "rusoto_credential 0.48.0", + "rusoto_signature 0.48.0", "rustc_version 0.4.0", "serde", "serde_json", @@ -2833,6 +2900,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rusoto_credential" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05" +dependencies = [ + "async-trait", + "chrono", + "dirs-next", + "futures", + "hyper", + "serde", + "serde_json", + "shlex", + "tokio", + "zeroize", +] + [[package]] name = "rusoto_s3" version = "0.47.0" @@ -2842,7 +2927,7 @@ dependencies = [ "async-trait", "bytes", "futures", - "rusoto_core", + "rusoto_core 0.47.0", "xml-rs", ] @@ -2865,13 +2950,54 @@ dependencies = [ "md-5 0.9.1", "percent-encoding", "pin-project-lite", - "rusoto_credential", + "rusoto_credential 0.47.0", "rustc_version 0.4.0", "serde", "sha2", "tokio", ] +[[package]] +name = "rusoto_signature" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" +dependencies = [ + "base64 0.13.1", + "bytes", + "chrono", + "digest 0.9.0", + "futures", + "hex", + "hmac", + "http", + "hyper", + "log", + "md-5", + "percent-encoding", + "pin-project-lite", + "rusoto_credential 0.48.0", + "rustc_version 0.4.0", + "serde", + "sha2", + "tokio", +] + +[[package]] +name = "rusoto_sts" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1643f49aa67cb7cb895ebac5a2ff3f991c6dbdc58ad98b28158cd5706aecd1d" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "rusoto_core 0.48.0", + "serde_urlencoded", + "xml-rs", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -2884,6 +3010,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3646,6 +3782,15 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/mantle/rbx_mantle/Cargo.toml b/mantle/rbx_mantle/Cargo.toml index d03e878..2436218 100644 --- a/mantle/rbx_mantle/Cargo.toml +++ b/mantle/rbx_mantle/Cargo.toml @@ -20,7 +20,10 @@ clap = "2.33.0" glob = "0.3.0" sha2 = "0.9.8" difference = "2.0.0" +dirs-next = "2.0.0" +rust-ini = "0.20.0" rusoto_core = "0.47.0" +rusoto_sts = "0.48.0" rusoto_s3 = "0.47.0" tokio = { version = "1", features = ["full"] } async-trait = "0.1.51" diff --git a/mantle/rbx_mantle/src/state/aws_credentials_provider.rs b/mantle/rbx_mantle/src/state/aws_credentials_provider.rs index dc95878..63819d8 100644 --- a/mantle/rbx_mantle/src/state/aws_credentials_provider.rs +++ b/mantle/rbx_mantle/src/state/aws_credentials_provider.rs @@ -1,9 +1,12 @@ use async_trait::async_trait; +use dirs_next::home_dir; +use ini::Ini; use rusoto_core::credential::{ AwsCredentials, ContainerProvider, CredentialsError, EnvironmentProvider, InstanceMetadataProvider, ProfileProvider, ProvideAwsCredentials, }; use std::env; +use std::path::PathBuf; use std::time::Duration; #[derive(Clone, Debug)] @@ -17,6 +20,17 @@ pub struct AwsCredentialsProvider { impl AwsCredentialsProvider { pub fn new() -> AwsCredentialsProvider { + // Set up profile provider using optionally supplied profile name // + let profile_provider: Option; + if let Ok(profile_name) = env::var("MANTLE_AWS_PROFILE") { + let mut provider = ProfileProvider::new().unwrap(); + provider.set_profile(profile_name); + profile_provider = Some(provider); + } else { + profile_provider = ProfileProvider::new().ok(); + } + + // Inherit IAM role from instance metadata service or ECS agent role // let mut inherit_iam_role = false; if let Ok(value) = env::var("MANTLE_AWS_INHERIT_IAM_ROLE") { if value == "true" { @@ -27,7 +41,7 @@ impl AwsCredentialsProvider { AwsCredentialsProvider { prefixed_environment_provider: EnvironmentProvider::with_prefix("MANTLE_AWS"), environment_provider: EnvironmentProvider::default(), - profile_provider: ProfileProvider::new().ok(), + profile_provider, container_provider: if inherit_iam_role { let mut provider = ContainerProvider::new(); provider.set_timeout(Duration::from_secs(15)); @@ -46,6 +60,13 @@ impl AwsCredentialsProvider { } } +fn get_config_path() -> PathBuf { + home_dir() + .expect("Expected a HOME directory") + .join(".aws") + .join("config") +} + async fn chain_provider_credentials( provider: AwsCredentialsProvider, ) -> Result { @@ -56,9 +77,29 @@ async fn chain_provider_credentials( return Ok(creds); } if let Some(ref profile_provider) = provider.profile_provider { + // Check standard profile credentials first // if let Ok(creds) = profile_provider.credentials().await { return Ok(creds); } + + // Check SSO profile credentials as fallback // + println!("Checking profile provider (sso)"); + let aws_config = Ini::load_from_file(get_config_path()) + .expect(format!("Failed to load AWS config ({:?})", get_config_path()).as_str()); + let profile_name = profile_provider.profile(); + println!("profile name: {}", profile_name); + + let target_section = aws_config.iter().find(|(section, _)| { + section.is_some() && section.unwrap() == format!("profile {}", profile_name) + }); + + if let Some((section, properties)) = target_section { + let section_name = section.unwrap(); + println!("Section name: {}", section_name); + for (key, value) in properties.iter() { + println!("{}: {:?}", key, value); + } + } } if let Some(ref container_provider) = provider.container_provider { if let Ok(creds) = container_provider.credentials().await {