From 64277b1d9fd30b2524630137458c39bc6aa012bf Mon Sep 17 00:00:00 2001 From: Orne Brocaar Date: Thu, 9 Nov 2023 13:22:10 +0000 Subject: [PATCH] Simplify AWS SNS integration / reduce dependencies. This removes the aws-sdk-sns crate (+ dependencies) and refactors the AWS SNS integration to use reqwest for the API call + aws-sign-v4 for creating the AWS request signature. --- Cargo.lock | 390 ++------------------------ chirpstack/Cargo.toml | 6 +- chirpstack/src/integration/aws_sns.rs | 136 +++++---- 3 files changed, 87 insertions(+), 445 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99a53d03..f128803d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,320 +400,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "aws-config" -version = "0.56.1" +name = "aws-sign-v4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6b3804dca60326e07205179847f17a4fce45af3a1106939177ad41ac08a6de" +checksum = "93df874cdba37261e8d371d427d8ba77c753febe271b8f484349af6dceea0e48" dependencies = [ - "aws-credential-types", - "aws-http", - "aws-sdk-sts", - "aws-smithy-async", - "aws-smithy-client", - "aws-smithy-http", - "aws-smithy-http-tower", - "aws-smithy-json", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand 2.0.0", - "http", - "hyper", - "time", - "tokio", - "tower", - "tracing", -] - -[[package]] -name = "aws-credential-types" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a66ac8ef5fa9cf01c2d999f39d16812e90ec1467bd382cbbb74ba23ea86201" -dependencies = [ - "aws-smithy-async", - "aws-smithy-types", - "fastrand 2.0.0", - "tokio", - "tracing", - "zeroize", -] - -[[package]] -name = "aws-http" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e626370f9ba806ae4c439e49675fd871f5767b093075cdf4fef16cac42ba900" -dependencies = [ - "aws-credential-types", - "aws-smithy-http", - "aws-smithy-types", - "aws-types", - "bytes", - "http", - "http-body", - "lazy_static", - "percent-encoding", - "pin-project-lite", - "tracing", -] - -[[package]] -name = "aws-runtime" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ac5cf0ff19c1bca0cea7932e11b239d1025a45696a4f44f72ea86e2b8bdd07" -dependencies = [ - "aws-credential-types", - "aws-http", - "aws-sigv4", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "fastrand 2.0.0", - "http", - "percent-encoding", - "tracing", - "uuid", -] - -[[package]] -name = "aws-sdk-sns" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0279f51da1a84996515caf4882040ff77982929c688b09e4cf4d943e39e652f1" -dependencies = [ - "aws-credential-types", - "aws-http", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-client", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-query", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-smithy-xml", - "aws-types", - "http", - "regex", - "tokio-stream", - "tracing", -] - -[[package]] -name = "aws-sdk-sts" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47ad6bf01afc00423d781d464220bf69fb6a674ad6629cbbcb06d88cdc2be82" -dependencies = [ - "aws-credential-types", - "aws-http", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-client", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-query", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-smithy-xml", - "aws-types", - "http", - "regex", - "tracing", -] - -[[package]] -name = "aws-sigv4" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b28f4910bb956b7ab320b62e98096402354eca976c587d1eeccd523d9bac03" -dependencies = [ - "aws-smithy-http", - "form_urlencoded", + "chrono", "hex", - "hmac", "http", - "once_cell", - "percent-encoding", - "regex", - "sha2", - "time", - "tracing", -] - -[[package]] -name = "aws-smithy-async" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cdb73f85528b9d19c23a496034ac53703955a59323d581c06aa27b4e4e247af" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", - "tokio-stream", -] - -[[package]] -name = "aws-smithy-client" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27b2756264c82f830a91cb4d2d485b2d19ad5bea476d9a966e03d27f27ba59a" -dependencies = [ - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-http-tower", - "aws-smithy-types", - "bytes", - "fastrand 2.0.0", - "http", - "http-body", - "hyper", - "hyper-rustls", - "lazy_static", - "pin-project-lite", - "rustls 0.21.7", - "tokio", - "tower", - "tracing", -] - -[[package]] -name = "aws-smithy-http" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cdcf365d8eee60686885f750a34c190e513677db58bbc466c44c588abf4199" -dependencies = [ - "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", - "http", - "http-body", - "hyper", - "once_cell", - "percent-encoding", - "pin-project-lite", - "pin-utils", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "aws-smithy-http-tower" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822de399d0ce62829a69dfa8c5cd08efdbe61a7426b953e2268f8b8b52a607bd" -dependencies = [ - "aws-smithy-http", - "aws-smithy-types", - "bytes", - "http", - "http-body", - "pin-project-lite", - "tower", - "tracing", -] - -[[package]] -name = "aws-smithy-json" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1e7ab8fa7ad10c193af7ae56d2420989e9f4758bf03601a342573333ea34f" -dependencies = [ - "aws-smithy-types", -] - -[[package]] -name = "aws-smithy-query" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28556a3902091c1f768a34f6c998028921bdab8d47d92586f363f14a4a32d047" -dependencies = [ - "aws-smithy-types", - "urlencoding", -] - -[[package]] -name = "aws-smithy-runtime" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745e096b3553e7e0f40622aa04971ce52765af82bebdeeac53aa6fc82fe801e6" -dependencies = [ - "aws-smithy-async", - "aws-smithy-client", - "aws-smithy-http", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "fastrand 2.0.0", - "http", - "http-body", - "once_cell", - "pin-project-lite", - "pin-utils", - "tokio", - "tracing", -] - -[[package]] -name = "aws-smithy-runtime-api" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d0ae0c9cfd57944e9711ea610b48a963fb174a53aabacc08c5794a594b1d02" -dependencies = [ - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-types", - "bytes", - "http", - "tokio", - "tracing", -] - -[[package]] -name = "aws-smithy-types" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90dbc8da2f6be461fa3c1906b20af8f79d14968fe47f2b7d29d086f62a51728" -dependencies = [ - "base64-simd", - "itoa", - "num-integer", - "ryu", - "serde", - "time", -] - -[[package]] -name = "aws-smithy-xml" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01d2dedcdd8023043716cfeeb3c6c59f2d447fce365d8e194838891794b23b6" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "aws-types" -version = "0.56.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85aa0451bf8af1bf22a4f028d5d28054507a14be43cb8ac0597a8471fba9edfe" -dependencies = [ - "aws-credential-types", - "aws-smithy-async", - "aws-smithy-client", - "aws-smithy-http", - "aws-smithy-types", - "http", - "rustc_version", - "tracing", + "ring", + "sha256", + "url", ] [[package]] @@ -813,16 +510,6 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" -[[package]] -name = "base64-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" -dependencies = [ - "outref", - "vsimd", -] - [[package]] name = "base64ct" version = "1.6.0" @@ -954,16 +641,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -[[package]] -name = "bytes-utils" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" -dependencies = [ - "bytes", - "either", -] - [[package]] name = "castaway" version = "0.1.2" @@ -1011,10 +688,7 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", - "aws-config", - "aws-credential-types", - "aws-sdk-sns", - "aws-types", + "aws-sign-v4", "backend", "base64 0.21.4", "bigdecimal", @@ -1062,6 +736,7 @@ dependencies = [ "rust-embed", "serde", "serde_json", + "serde_urlencoded", "serde_yaml", "sha2", "thiserror", @@ -2172,7 +1847,6 @@ dependencies = [ "futures-util", "http", "hyper", - "log", "rustls 0.21.7", "rustls-native-certs", "tokio", @@ -2871,12 +2545,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "outref" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" - [[package]] name = "overload" version = "0.1.1" @@ -3718,15 +3386,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.37.23" @@ -3919,12 +3578,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - [[package]] name = "serde" version = "1.0.188" @@ -4086,6 +3739,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -4887,12 +4553,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - [[package]] name = "waker-fn" version = "1.1.0" @@ -5183,12 +4843,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "xmlparser" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" - [[package]] name = "yasna" version = "0.5.2" diff --git a/chirpstack/Cargo.toml b/chirpstack/Cargo.toml index 66e74137..1ffa0035 100644 --- a/chirpstack/Cargo.toml +++ b/chirpstack/Cargo.toml @@ -17,6 +17,7 @@ clap = { version = "4.4", features = ["derive"] } serde = { version = "1.0", features = ["derive", "rc"] } serde_yaml = "0.9" serde_json = "1.0" +serde_urlencoded = "0.7" humantime-serde = "1.1" toml = "0.7" handlebars = "4.4" @@ -57,10 +58,7 @@ reqwest = { version = "0.11", features = [ ], default-features = false } # Integrations -aws-types = "0.56" -aws-credential-types = "0.56" -aws-config = { version = "0.56", default-features = false } -aws-sdk-sns = "0.30" +aws-sign-v4 = "0.2" hmac = "0.12" sha2 = "0.10" urlencoding = "2.1" diff --git a/chirpstack/src/integration/aws_sns.rs b/chirpstack/src/integration/aws_sns.rs index 31ad7af8..0e261b48 100644 --- a/chirpstack/src/integration/aws_sns.rs +++ b/chirpstack/src/integration/aws_sns.rs @@ -2,10 +2,6 @@ use std::collections::HashMap; use anyhow::Result; use async_trait::async_trait; -use aws_credential_types::provider::{future, ProvideCredentials, Result as CredentialsResult}; -use aws_credential_types::Credentials; -use aws_sdk_sns::types::MessageAttributeValue; -use aws_types::region::Region; use base64::{engine::general_purpose, Engine as _}; use prost::Message; use tracing::{info, trace}; @@ -15,58 +11,19 @@ use crate::storage::application::AwsSnsConfiguration; use chirpstack_api::api::Encoding; use chirpstack_api::integration; -#[derive(Debug)] -struct StaticCredentials { - aws_access_key_id: String, - aws_secret_access_key: String, -} - -impl StaticCredentials { - fn new(key: &str, secret: &str) -> Self { - StaticCredentials { - aws_access_key_id: key.to_string(), - aws_secret_access_key: secret.to_string(), - } - } - - fn credentials(&self) -> CredentialsResult { - Ok(Credentials::new( - self.aws_access_key_id.clone(), - self.aws_secret_access_key.clone(), - None, - None, - "StaticProvider", - )) - } -} - -impl ProvideCredentials for StaticCredentials { - fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> - where - Self: 'a, - { - future::ProvideCredentials::ready(self.credentials()) - } -} - pub struct Integration { json: bool, - client: aws_sdk_sns::Client, + access_key_id: String, + secret_access_key: String, + region: String, topic_arn: String, + client: reqwest::Client, } impl Integration { pub async fn new(conf: &AwsSnsConfiguration) -> Result { trace!("Initializing AWS SNS integration"); - let credentials = StaticCredentials::new(&conf.access_key_id, &conf.secret_access_key); - let config = aws_config::ConfigLoader::default() - .credentials_provider(credentials) - .region(Region::new(conf.region.clone())) - .load() - .await; - let client = aws_sdk_sns::Client::new(&config); - Ok(Integration { json: match Encoding::try_from(conf.encoding) .map_err(|_| anyhow!("Invalid encoding"))? @@ -75,7 +32,10 @@ impl Integration { Encoding::Protobuf => false, }, topic_arn: conf.topic_arn.clone(), - client, + access_key_id: conf.access_key_id.clone(), + secret_access_key: conf.secret_access_key.clone(), + region: conf.region.clone(), + client: reqwest::Client::new(), }) } @@ -87,33 +47,63 @@ impl Integration { pl: &str, ) -> Result<()> { info!(event = %event, dev_eui = %dev_eui, "Publishing event"); + + let hostname = format!("sns.{}.amazonaws.com", self.region); + let url = format!("https://{}/", hostname); + let ts = chrono::Utc::now(); + + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("host", hostname.parse()?); + headers.insert( + "X-Amz-Date", + ts.format("%Y%m%dT%H%M%SZ").to_string().parse()?, + ); + headers.insert( + reqwest::header::CONTENT_TYPE, + "application/x-www-form-urlencoded".parse()?, + ); + + let body = [ + ("Action", "Publish"), + ("TopicArn", &self.topic_arn), + ("MessageAttributes.entry.1.Name", "event"), + ("MessageAttributes.entry.1.Value.DataType", "String"), + ("MessageAttributes.entry.1.Value.StringValue", event), + ("MessageAttributes.entry.2.Name", "dev_eui"), + ("MessageAttributes.entry.2.Value.DataType", "String"), + ("MessageAttributes.entry.2.Value.StringValue", dev_eui), + ("MessageAttributes.entry.3.Name", "application_id"), + ("MessageAttributes.entry.3.Value.DataType", "String"), + ( + "MessageAttributes.entry.3.Value.StringValue", + application_id, + ), + ("Message", pl), + ]; + let body = serde_urlencoded::to_string(body)?; + + let s = aws_sign_v4::AwsSign::new( + "POST", + &url, + &ts, + &headers, + &self.region, + &self.access_key_id, + &self.secret_access_key, + "sns", + &body, + ) + .sign(); + + headers.insert(reqwest::header::AUTHORIZATION, s.parse()?); + self.client - .publish() - .topic_arn(self.topic_arn.clone()) - .message_attributes( - "event", - MessageAttributeValue::builder() - .data_type("String") - .string_value(event) - .build(), - ) - .message_attributes( - "dev_eui", - MessageAttributeValue::builder() - .data_type("String") - .string_value(dev_eui) - .build(), - ) - .message_attributes( - "application_id", - MessageAttributeValue::builder() - .data_type("String") - .string_value(application_id) - .build(), - ) - .message(pl) + .post(url) + .headers(headers) + .body(body) .send() - .await?; + .await? + .error_for_status()?; Ok(()) }