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.
This commit is contained in:
Orne Brocaar 2023-11-09 13:22:10 +00:00
parent d24c830cd5
commit 64277b1d9f
3 changed files with 87 additions and 445 deletions

390
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<Integration> {
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(())
}