diff --git a/Cargo.lock b/Cargo.lock index b0ac86b..3a905fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -799,6 +814,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "bytes" version = "1.5.0" @@ -830,6 +851,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.48.5", +] + [[package]] name = "clap" version = "4.4.10" @@ -1345,6 +1380,29 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -1417,6 +1475,7 @@ dependencies = [ "aws-smithy-runtime-api 0.56.1", "aws-types", "base64", + "chrono", "clap", "colored", "futures", @@ -1435,6 +1494,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2300,6 +2368,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + [[package]] name = "winapi" version = "0.3.9" @@ -2322,6 +2444,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 7e1f188..d270dff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ simple-home-dir = "0.2.0" tokio = { version = "1", features = ["full"] } unescape = "0.1.0" v = "0.1.0" +chrono = "0.4.31" # Config for 'cargo dist' [workspace.metadata.dist] diff --git a/README.md b/README.md index 64f48c7..09c1992 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ isotope analyze -a S3 - EC2 - Orphaned Elastic IP address - Public snapshot detection +- IAM + - Orphaned/unused key detection ### Community diff --git a/src/analyzer/iam_analyzer.rs b/src/analyzer/iam_analyzer.rs new file mode 100644 index 0000000..5dfad86 --- /dev/null +++ b/src/analyzer/iam_analyzer.rs @@ -0,0 +1,45 @@ +use async_trait::async_trait; +use aws_sdk_iam::Client; +use aws_types::sdk_config::SdkConfig; +use crate::analyzer::analyzer_trait::Analyzer; +use crate::analyzer::types::AnalysisResults; +use chrono::{TimeZone, Utc}; +use chrono::Duration; +pub struct IamAnalyzer { + pub config: SdkConfig +} + +#[async_trait] +impl Analyzer for IamAnalyzer { + + async fn run(&self) -> Option> { + let mut results = Vec::new(); + let iam = Client::new(&self.config); + + // Check for unused access keys + if let Ok(keys) = iam.list_access_keys().send().await { + for key in keys.access_key_metadata { + if let Some(create_date) = key.create_date { + if !key.access_key_id.as_ref().unwrap().is_empty(){ + if let Some(aws_create_date) = key.create_date { + let create_date = Utc.timestamp(aws_create_date.secs(), 0); + if create_date < Utc::now() - Duration::days(90) { + results.push(AnalysisResults { + message: format!("Unused access key: {}", key.access_key_id.as_ref().unwrap()), + advice: "Consider deleting unused access keys".to_string(), + analyzer_name: self.get_name(), + }); + } + } + } + } + } + } + + Some(results) + } + + fn get_name(&self) -> String { + "iam".to_string() + } +} \ No newline at end of file diff --git a/src/analyzer/mod.rs b/src/analyzer/mod.rs index 576fb0c..3ec938f 100644 --- a/src/analyzer/mod.rs +++ b/src/analyzer/mod.rs @@ -10,6 +10,7 @@ mod s3_analyzer; mod sg_analyzer; mod sts_analyzer; pub(crate) mod types; +mod iam_analyzer; pub fn generate_analyzers(config: &SdkConfig) -> Vec> { let analyzers: Vec> = vec![ @@ -34,6 +35,9 @@ pub fn generate_analyzers(config: &SdkConfig) -> Vec> { Box::new(ec2_snapshot_analyzer::EC2SnapshotAnalyzer { config: config.clone(), }), + Box::new(iam_analyzer::IamAnalyzer { + config: config.clone(), + }) ]; analyzers }