From 0527cb8cbe1dd12f2f117395dbece0883934b7c0 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Fri, 17 Jun 2022 13:20:57 -0700 Subject: [PATCH] refactor: add IRSA as first precedent for aws client creds (#202) * refactor: add IRSA as first precedent for aws client creds * chore: changelogs * refactor: cleanup irsa instantiation * refactor: cleaner kms instantiation * fix: add debug logging for IRSA failures * refactor: move kms_client inside try_from_signer_conf --- Cargo.lock | 40 ++++++++--------- agents/processor/CHANGELOG.md | 1 + agents/processor/src/processor.rs | 3 +- agents/processor/src/push.rs | 5 ++- agents/watcher/src/watcher.rs | 7 ++- chains/nomad-ethereum/src/macros.rs | 4 +- nomad-core/CHANGELOG.md | 1 + nomad-core/Cargo.toml | 3 +- nomad-core/src/aws.rs | 70 +++++++++++++++++++++++++++++ nomad-core/src/lib.rs | 3 ++ nomad-core/src/signer.rs | 7 +-- 11 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 nomad-core/src/aws.rs diff --git a/Cargo.lock b/Cargo.lock index e13c3c61..05caf458 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2556,8 +2556,9 @@ dependencies = [ "prometheus", "rocksdb", "rusoto_core", - "rusoto_credential 0.47.0", + "rusoto_credential", "rusoto_kms", + "rusoto_sts", "serde 1.0.136", "serde_json", "sha3 0.9.1", @@ -3606,7 +3607,7 @@ dependencies = [ "hyper-tls", "lazy_static", "log", - "rusoto_credential 0.48.0", + "rusoto_credential", "rusoto_signature", "rustc_version", "serde 1.0.136", @@ -3615,24 +3616,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "rusoto_credential" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a46b67db7bb66f5541e44db22b0a02fed59c9603e146db3a9e633272d3bac2f" -dependencies = [ - "async-trait", - "chrono", - "dirs-next", - "futures", - "hyper", - "serde 1.0.136", - "serde_json", - "shlex", - "tokio", - "zeroize", -] - [[package]] name = "rusoto_credential" version = "0.48.0" @@ -3697,13 +3680,28 @@ dependencies = [ "md-5 0.9.1", "percent-encoding", "pin-project-lite", - "rusoto_credential 0.48.0", + "rusoto_credential", "rustc_version", "serde 1.0.136", "sha2 0.9.9", "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", + "serde_urlencoded", + "xml-rs", +] + [[package]] name = "rust-ini" version = "0.13.0" diff --git a/agents/processor/CHANGELOG.md b/agents/processor/CHANGELOG.md index 403e13ee..9c7c546a 100644 --- a/agents/processor/CHANGELOG.md +++ b/agents/processor/CHANGELOG.md @@ -2,6 +2,7 @@ ### Unreleased +- refactor: processor now uses global AWS client when proof pushing is enabled - prevent processor from retrying messages it has previously attempted to process - improve prove/process tracing diff --git a/agents/processor/src/processor.rs b/agents/processor/src/processor.rs index 4e6639be..00fd191b 100644 --- a/agents/processor/src/processor.rs +++ b/agents/processor/src/processor.rs @@ -423,7 +423,8 @@ impl NomadAgent for Processor { // if we have a bucket, add a task to push to it if let Some(config) = &self.config { info!(bucket = %config.bucket, "Starting S3 push tasks"); - tasks.push(Pusher::new(self.core.home.name(), &config.bucket, db.clone()).spawn()) + let pusher = Pusher::new(self.core.home.name(), &config.bucket, db.clone()).await; + tasks.push(pusher.spawn()) } // find the first task to shut down. Then cancel all others diff --git a/agents/processor/src/push.rs b/agents/processor/src/push.rs index dc85e715..fdede351 100644 --- a/agents/processor/src/push.rs +++ b/agents/processor/src/push.rs @@ -39,9 +39,10 @@ impl std::fmt::Debug for Pusher { impl Pusher { /// Instantiate a new pusher with a region - pub fn new(name: &str, bucket: &str, db: NomadDB) -> Self { + pub async fn new(name: &str, bucket: &str, db: NomadDB) -> Self { let region: Region = Default::default(); // loads from aws env - let client = S3Client::new(region.clone()); + let client = + S3Client::new_with_client(nomad_core::aws::get_client().await.clone(), region.clone()); Self { name: name.to_owned(), bucket: bucket.to_owned(), diff --git a/agents/watcher/src/watcher.rs b/agents/watcher/src/watcher.rs index 90dba069..375c8e51 100644 --- a/agents/watcher/src/watcher.rs +++ b/agents/watcher/src/watcher.rs @@ -559,9 +559,12 @@ impl NomadAgent for Watcher { let core = settings.as_ref().try_into_core("watcher").await?; - Ok(Self::new( + let signer = Signers::try_from_signer_conf(&settings.base.attestation_signer.expect("signer")) - .await?, + .await?; + + Ok(Self::new( + signer, settings.agent.interval, connection_managers, core, diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index f3bcbfba..b4103bf1 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -156,7 +156,7 @@ macro_rules! wrap_with_signer { #[macro_export] macro_rules! tx_submitter_local { ($base_provider:expr, $signer_conf:ident) => {{ - let signer = Signers::try_from_signer_conf(&$signer_conf).await?; + let signer = nomad_core::Signers::try_from_signer_conf(&$signer_conf).await?; let signing_provider: Arc<_> = wrap_with_signer!($base_provider.clone(), signer); TxSubmitter::new(signing_provider.into()) }}; @@ -166,7 +166,7 @@ macro_rules! tx_submitter_local { #[macro_export] macro_rules! tx_submitter_gelato { ($base_provider:expr, $gelato_conf:ident) => {{ - let signer = Signers::try_from_signer_conf(&$gelato_conf.sponsor).await?; + let signer = nomad_core::Signers::try_from_signer_conf(&$gelato_conf.sponsor).await?; let sponsor = signer.clone(); let chain_id = $base_provider.get_chainid().await?.as_usize(); let signing_provider: Arc<_> = wrap_with_signer!($base_provider.clone(), signer); // kludge: only using signing provider for type consistency with TxSubmitter::Local diff --git a/nomad-core/CHANGELOG.md b/nomad-core/CHANGELOG.md index 761684ad..1501937f 100644 --- a/nomad-core/CHANGELOG.md +++ b/nomad-core/CHANGELOG.md @@ -2,5 +2,6 @@ ### Unreleased +- refactor: Add IRSA credentials to client instantiation - implement `Encode` and `Decode` for `bool` - adds a changelog diff --git a/nomad-core/Cargo.toml b/nomad-core/Cargo.toml index 49393e8d..05c5554d 100644 --- a/nomad-core/Cargo.toml +++ b/nomad-core/Cargo.toml @@ -25,13 +25,14 @@ prometheus = "0.12.0" bytes = { version = "1", features = ["serde"]} rusoto_core = "0.48.0" rusoto_kms = "0.48.0" -rusoto_credential = "0.47.0" +rusoto_credential = "0.48.0" once_cell = "1.8.0" num = { version="0", features=["serde"] } accumulator = { path = "../accumulator" } nomad-types = { path = "../nomad-types" } nomad-xyz-configuration = { path = "../configuration" } +rusoto_sts = "0.48.0" [dev-dependencies] tokio = { version = "1.0.1", features = ["rt", "time", "macros"] } diff --git a/nomad-core/src/aws.rs b/nomad-core/src/aws.rs new file mode 100644 index 00000000..27cb86e6 --- /dev/null +++ b/nomad-core/src/aws.rs @@ -0,0 +1,70 @@ +use once_cell::sync::OnceCell; +use rusoto_core::{ + credential::{AutoRefreshingProvider, ProvideAwsCredentials}, + Client, HttpClient, +}; +use rusoto_kms::KmsClient; +use rusoto_sts::WebIdentityProvider; + +static CLIENT: OnceCell = OnceCell::new(); +static KMS_CLIENT: OnceCell = OnceCell::new(); + +// Try to get an irsa provider +#[tracing::instrument] +async fn try_irsa_provider() -> Option> { + let irsa_provider = WebIdentityProvider::from_k8s_env(); + + // if there are no IRSA credentials this will error + let result = irsa_provider.credentials().await; + + if result.is_err() { + tracing::debug!(error = %result.as_ref().unwrap_err(), "Error in irsa provider instantiation"); + } + + result + .ok() + .and_then(|_| AutoRefreshingProvider::new(irsa_provider).ok()) +} + +/// Get a shared AWS client with credentials +/// +/// Credential precedence is as follows +/// 1. IRSA +/// 2. IAM +/// 3. environment +/// 4. Conf file +pub async fn get_client() -> &'static Client { + // init exactly once + if CLIENT.get().is_none() { + // try IRSA first + let client = match try_irsa_provider().await { + Some(credentials_provider) => { + let dispatcher = HttpClient::new().unwrap(); + Client::new_with(credentials_provider, dispatcher) + } + // if the IRSA provider returned no creds, use the default + // credentials chain + None => Client::shared(), + }; + + if CLIENT.set(client).is_err() { + panic!("unable to set Client") + }; + } + + CLIENT.get().expect("just init") +} + +/// Get a shared KMS client +pub async fn get_kms_client() -> &'static KmsClient { + if KMS_CLIENT.get().is_none() { + let client = get_client().await.clone(); + + let kms = KmsClient::new_with_client(client, Default::default()); + + if KMS_CLIENT.set(kms).is_err() { + panic!("unable to set KmsClient") + }; + } + KMS_CLIENT.get().expect("just init") +} diff --git a/nomad-core/src/lib.rs b/nomad-core/src/lib.rs index 8713a774..f9b54802 100644 --- a/nomad-core/src/lib.rs +++ b/nomad-core/src/lib.rs @@ -10,6 +10,9 @@ pub use accumulator; +/// AWS global state and init +pub mod aws; + /// DB related utilities pub mod db; diff --git a/nomad-core/src/signer.rs b/nomad-core/src/signer.rs index 28ff6f8b..caf919a5 100644 --- a/nomad-core/src/signer.rs +++ b/nomad-core/src/signer.rs @@ -13,10 +13,6 @@ use ethers::{ signers::{AwsSignerError, LocalWallet, Signer}, }; use nomad_xyz_configuration::agent::SignerConf; -use once_cell::sync::Lazy; -use rusoto_kms::KmsClient; - -static KMS_CLIENT: Lazy = Lazy::new(|| KmsClient::new(Default::default())); /// Error types for Signers #[derive(Debug, thiserror::Error)] @@ -62,7 +58,8 @@ impl Signers { match conf { SignerConf::HexKey(key) => Ok(Self::Local(key.as_ref().parse()?)), SignerConf::Aws { id } => { - let signer = AwsSigner::new(&KMS_CLIENT, id, 0).await?; + let kms_client = crate::aws::get_kms_client().await; + let signer = AwsSigner::new(kms_client, id, 0).await?; Ok(Self::Aws(signer)) } SignerConf::Node => bail!("Node signer"),