Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 17 additions & 24 deletions crates/core/src/wallet/aws_kms_wallet_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ impl AwsKmsWalletManager {
format!("alias/{}-wallet-{}-{}", self.alias, wallet_index, chain_id)
}

async fn build_aws_config(&self) -> aws_config::SdkConfig {
let mut config_loader = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()));

if let Some(endpoint_url) = &self.config.endpoint_url {
config_loader = config_loader.endpoint_url(endpoint_url);
}

config_loader.load().await
}

async fn get_or_create_key_id(
&self,
wallet_index: u32,
Expand All @@ -69,10 +80,7 @@ impl AwsKmsWalletManager {
}

async fn validate_aws_config(&self) -> Result<(), WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let sts = StsClient::new(&aws_config);
match sts.get_caller_identity().send().await {
Expand All @@ -94,10 +102,7 @@ impl AwsKmsWalletManager {
wallet_index: u32,
chain_id: &ChainId,
) -> Result<String, WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let kms = Client::new(&aws_config);
let expected_alias = self.build_alias(wallet_index, chain_id);
Expand Down Expand Up @@ -182,10 +187,7 @@ impl AwsKmsWalletManager {
key_id: &str,
chain_id: Option<u64>,
) -> Result<AwsSigner, WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let client = Client::new(&aws_config);

Expand Down Expand Up @@ -235,10 +237,7 @@ impl AwsKmsWalletManager {
}

pub async fn create_keys(&self, plans: Vec<KeyPlan>) -> Result<Vec<String>, WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let sts = StsClient::new(&aws_config);
let who = sts.get_caller_identity().send().await.map_err(|e| WalletError::ApiError {
Expand Down Expand Up @@ -304,10 +303,7 @@ impl AwsKmsWalletManager {
}

pub async fn list_keys(&self) -> Result<Vec<(String, String)>, WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let kms = Client::new(&aws_config);

Expand Down Expand Up @@ -335,10 +331,7 @@ impl AwsKmsWalletManager {
}

pub async fn list_aliases(&self) -> Result<Vec<(String, String)>, WalletError> {
let aws_config = aws_config::defaults(BehaviorVersion::latest())
.region(Region::new(self.config.region.clone()))
.load()
.await;
let aws_config = self.build_aws_config().await;

let kms = Client::new(&aws_config);

Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub struct AwsSecretManagerProviderConfig {
pub struct AwsKmsSigningProviderConfig {
pub region: String,
pub danger_override_alias: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub endpoint_url: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand Down
1 change: 1 addition & 0 deletions documentation/rrelayer/docs/pages/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### Features

- feat: allow custom `endpoint_url` for AWS KMS signers (for use with [LocalStack](https://localstack.cloud/))
- feat: allow overriding the host in the API config for deployments

---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ api_config:
signing_provider: // [!code focus]
aws_kms: // [!code focus]
region: "eu-west-1" // [!code focus]
endpoint_url: "http://localhost:4566" # Optional: for LocalStack // [!code focus]
```

You then need to add the AWS variables to the `.env`
Expand All @@ -80,3 +81,25 @@ AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
Your user must have the required roles assigned in AWS to this user which we state above else it will not work and
rrelayer will throw errors to tell you this.
:::

## LocalStack Support

The optional `endpoint_url` field allows you to point AWS KMS requests to a custom endpoint, which is particularly useful for local development and testing with [LocalStack](https://localstack.cloud/).

**Example LocalStack configuration:**

```yaml [rrelayer.yaml]
signing_provider:
aws_kms:
region: "us-east-1"
endpoint_url: "http://localhost:4566"
```

When using LocalStack:
- The region can be any valid AWS region name (LocalStack doesn't enforce region-specific behavior)
- Ensure LocalStack is running with both **KMS** and **STS** services enabled
- You still need to set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables

:::info
When `endpoint_url` is omitted, rrelayer will use the standard AWS KMS endpoints in production.
:::