From 6bf2eaf8d7c677b1f3b9747a9a917f9eb3d61062 Mon Sep 17 00:00:00 2001 From: KenshiHashiguchin Date: Sat, 4 Oct 2025 16:25:59 +0900 Subject: [PATCH] feat: Add IAM role authentication support for AWS KMS wallets - Make credentials optional during wallet import/creation - Use AWS SDK default credential chain (IAM roles, etc.) when credentials are not specified --- src/server/routes/backend-wallet/import.ts | 21 +++++++-------- .../utils/wallets/create-aws-kms-wallet.ts | 8 +++--- .../utils/wallets/create-smart-wallet.ts | 4 +-- .../wallets/fetch-aws-kms-wallet-params.ts | 27 ++++--------------- .../utils/wallets/import-aws-kms-wallet.ts | 10 +++---- .../db/wallets/create-wallet-details.ts | 16 ++++++----- src/shared/db/wallets/get-wallet-details.ts | 4 +-- src/shared/utils/account.ts | 8 +++--- src/shared/utils/cache/get-wallet.ts | 8 +++--- 9 files changed, 45 insertions(+), 61 deletions(-) diff --git a/src/server/routes/backend-wallet/import.ts b/src/server/routes/backend-wallet/import.ts index 4ffb2c469..b2c8ebfd8 100644 --- a/src/server/routes/backend-wallet/import.ts +++ b/src/server/routes/backend-wallet/import.ts @@ -158,20 +158,17 @@ export const importBackendWallet = async (fastify: FastifyInstance) => { credentials?.awsAccessKeyId ?? config.walletConfiguration.aws?.awsAccessKeyId; - if (!(accessKeyId && secretAccessKey)) { - throw createCustomError( - `Please provide 'awsAccessKeyId' and 'awsSecretAccessKey' to import a wallet. Can be provided as configuration or as credential with the request.`, - StatusCodes.BAD_REQUEST, - "MISSING_PARAMETERS", - ); - } - + // Credentials are optional - if not provided, AWS SDK will use IAM roles or other credential providers + const walletCredentials = + accessKeyId && secretAccessKey + ? { + accessKeyId, + secretAccessKey, + } + : undefined; walletAddress = await importAwsKmsWallet({ awsKmsArn, - crendentials: { - accessKeyId, - secretAccessKey, - }, + crendentials: walletCredentials, label, }); } diff --git a/src/server/utils/wallets/create-aws-kms-wallet.ts b/src/server/utils/wallets/create-aws-kms-wallet.ts index 1398db8c0..803d9e91c 100644 --- a/src/server/utils/wallets/create-aws-kms-wallet.ts +++ b/src/server/utils/wallets/create-aws-kms-wallet.ts @@ -27,10 +27,10 @@ export const createAwsKmsWalletDetails = async ({ return importAwsKmsWallet({ awsKmsArn, label, - crendentials: { + crendentials: params.awsAccessKeyId && params.awsSecretAccessKey ? { accessKeyId: params.awsAccessKeyId, secretAccessKey: params.awsSecretAccessKey, - }, + } : undefined, }); }; @@ -54,10 +54,10 @@ export const createAwsKmsKey = async ( const client = new KMSClient({ region: params.awsRegion, - credentials: { + credentials: params.awsAccessKeyId && params.awsSecretAccessKey ? { accessKeyId: params.awsAccessKeyId, secretAccessKey: params.awsSecretAccessKey, - }, + } : undefined, }); const res = await client.send( diff --git a/src/server/utils/wallets/create-smart-wallet.ts b/src/server/utils/wallets/create-smart-wallet.ts index 555f6e70f..e6a27bfb1 100644 --- a/src/server/utils/wallets/create-smart-wallet.ts +++ b/src/server/utils/wallets/create-smart-wallet.ts @@ -67,10 +67,10 @@ export const createSmartAwsWalletDetails = async ({ keyId, config: { region: awsKmsWallet.params.awsRegion, - credentials: { + credentials: awsKmsWallet.params.awsAccessKeyId && awsKmsWallet.params.awsSecretAccessKey ? { accessKeyId: awsKmsWallet.params.awsAccessKeyId, secretAccessKey: awsKmsWallet.params.awsSecretAccessKey, - }, + } : undefined, }, }); diff --git a/src/server/utils/wallets/fetch-aws-kms-wallet-params.ts b/src/server/utils/wallets/fetch-aws-kms-wallet-params.ts index 0b0f46ef5..c6f7affe5 100644 --- a/src/server/utils/wallets/fetch-aws-kms-wallet-params.ts +++ b/src/server/utils/wallets/fetch-aws-kms-wallet-params.ts @@ -1,17 +1,18 @@ import { getConfig } from "../../../shared/utils/cache/get-config"; export type AwsKmsWalletParams = { - awsAccessKeyId: string; - awsSecretAccessKey: string; + awsAccessKeyId?: string; + awsSecretAccessKey?: string; - awsRegion: string; + awsRegion?: string; }; export class FetchAwsKmsWalletParamsError extends Error {} /** * Fetches the AWS KMS wallet creation parameters from the configuration or overrides. - * If any required parameter cannot be resolved from either the configuration or the overrides, an error is thrown. + * Credentials are optional - if not provided, AWS SDK will use IAM roles or other credential providers. + * Only AWS region is required. */ export async function fetchAwsKmsWalletParams( overrides: Partial, @@ -21,31 +22,13 @@ export async function fetchAwsKmsWalletParams( const awsAccessKeyId = overrides.awsAccessKeyId ?? config.walletConfiguration.aws?.awsAccessKeyId; - if (!awsAccessKeyId) { - throw new FetchAwsKmsWalletParamsError( - "AWS access key ID is required for this wallet type. Could not find in configuration or params.", - ); - } - const awsSecretAccessKey = overrides.awsSecretAccessKey ?? config.walletConfiguration.aws?.awsSecretAccessKey; - if (!awsSecretAccessKey) { - throw new FetchAwsKmsWalletParamsError( - "AWS secretAccessKey is required for this wallet type. Could not find in configuration or params.", - ); - } - const awsRegion = overrides.awsRegion ?? config.walletConfiguration.aws?.defaultAwsRegion; - if (!awsRegion) { - throw new FetchAwsKmsWalletParamsError( - "AWS region is required for this wallet type. Could not find in configuration or params.", - ); - } - return { awsAccessKeyId, awsSecretAccessKey, diff --git a/src/server/utils/wallets/import-aws-kms-wallet.ts b/src/server/utils/wallets/import-aws-kms-wallet.ts index 364a2c0af..a2aab3c3c 100644 --- a/src/server/utils/wallets/import-aws-kms-wallet.ts +++ b/src/server/utils/wallets/import-aws-kms-wallet.ts @@ -6,7 +6,7 @@ import { getAwsKmsAccount } from "./get-aws-kms-account"; interface ImportAwsKmsWalletParams { awsKmsArn: string; - crendentials: { + crendentials?: { accessKeyId: string; secretAccessKey: string; }; @@ -27,10 +27,10 @@ export const importAwsKmsWallet = async ({ keyId, config: { region, - credentials: { + credentials: crendentials ? { accessKeyId: crendentials.accessKeyId, secretAccessKey: crendentials.secretAccessKey, - }, + } : undefined, }, }); @@ -42,8 +42,8 @@ export const importAwsKmsWallet = async ({ awsKmsArn, label, - awsKmsAccessKeyId: crendentials.accessKeyId, - awsKmsSecretAccessKey: crendentials.secretAccessKey, + awsKmsAccessKeyId: crendentials?.accessKeyId, + awsKmsSecretAccessKey: crendentials?.secretAccessKey, }); return walletAddress; diff --git a/src/shared/db/wallets/create-wallet-details.ts b/src/shared/db/wallets/create-wallet-details.ts index 6655e086d..8a4aab48f 100644 --- a/src/shared/db/wallets/create-wallet-details.ts +++ b/src/shared/db/wallets/create-wallet-details.ts @@ -18,8 +18,8 @@ type CreateWalletDetailsParams = { awsKmsKeyId?: string; // deprecated and unused, todo: remove with next breaking change awsKmsArn: string; - awsKmsSecretAccessKey: string; // will be encrypted and stored, pass plaintext to this function - awsKmsAccessKeyId: string; + awsKmsSecretAccessKey?: string; // will be encrypted and stored, pass plaintext to this function + awsKmsAccessKeyId?: string; } | { type: "gcp-kms"; @@ -35,8 +35,8 @@ type CreateWalletDetailsParams = { | { type: "smart:aws-kms"; awsKmsArn: string; - awsKmsSecretAccessKey: string; // will be encrypted and stored, pass plaintext to this function - awsKmsAccessKeyId: string; + awsKmsSecretAccessKey?: string; // will be encrypted and stored, pass plaintext to this function + awsKmsAccessKeyId?: string; accountSignerAddress: Address; accountFactoryAddress: Address | undefined; @@ -99,7 +99,9 @@ export const createWalletDetails = async ({ ...walletDetails, address: walletDetails.address.toLowerCase(), - awsKmsSecretAccessKey: encrypt(walletDetails.awsKmsSecretAccessKey), + awsKmsSecretAccessKey: walletDetails.awsKmsSecretAccessKey + ? encrypt(walletDetails.awsKmsSecretAccessKey) + : null, }, }); } @@ -123,7 +125,9 @@ export const createWalletDetails = async ({ ...walletDetails, address: walletDetails.address.toLowerCase(), - awsKmsSecretAccessKey: encrypt(walletDetails.awsKmsSecretAccessKey), + awsKmsSecretAccessKey: walletDetails.awsKmsSecretAccessKey + ? encrypt(walletDetails.awsKmsSecretAccessKey) + : null, accountSignerAddress: walletDetails.accountSignerAddress.toLowerCase(), accountFactoryAddress: diff --git a/src/shared/db/wallets/get-wallet-details.ts b/src/shared/db/wallets/get-wallet-details.ts index 5e28a2219..22427b4dc 100644 --- a/src/shared/db/wallets/get-wallet-details.ts +++ b/src/shared/db/wallets/get-wallet-details.ts @@ -63,8 +63,8 @@ const awsKmsWalletSchema = z .object({ type: z.literal("aws-kms"), awsKmsArn: z.string(), - awsKmsSecretAccessKey: z.string(), - awsKmsAccessKeyId: z.string(), + awsKmsSecretAccessKey: z.string().nullable(), + awsKmsAccessKeyId: z.string().nullable(), }) .merge(baseWalletPartialSchema); diff --git a/src/shared/utils/account.ts b/src/shared/utils/account.ts index 074ce55d3..ca5beae9f 100644 --- a/src/shared/utils/account.ts +++ b/src/shared/utils/account.ts @@ -67,10 +67,10 @@ export const walletDetailsToAccount = async ({ keyId, config: { region, - credentials: { + credentials: walletDetails.awsKmsAccessKeyId && walletDetails.awsKmsSecretAccessKey ? { accessKeyId: walletDetails.awsKmsAccessKeyId, secretAccessKey: walletDetails.awsKmsSecretAccessKey, - }, + } : undefined, }, }); @@ -103,10 +103,10 @@ export const walletDetailsToAccount = async ({ keyId, config: { region, - credentials: { + credentials: walletDetails.awsKmsAccessKeyId && walletDetails.awsKmsSecretAccessKey ? { accessKeyId: walletDetails.awsKmsAccessKeyId, secretAccessKey: walletDetails.awsKmsSecretAccessKey, - }, + } : undefined, }, }); diff --git a/src/shared/utils/cache/get-wallet.ts b/src/shared/utils/cache/get-wallet.ts index 0e0e110dd..73d51a890 100644 --- a/src/shared/utils/cache/get-wallet.ts +++ b/src/shared/utils/cache/get-wallet.ts @@ -62,8 +62,8 @@ export const getWallet = async ({ wallet = new AwsKmsWallet({ keyId: splitArn.keyId, region: splitArn.region, - accessKeyId: walletDetails.awsKmsAccessKeyId, - secretAccessKey: walletDetails.awsKmsSecretAccessKey, + accessKeyId: walletDetails.awsKmsAccessKeyId ?? undefined, + secretAccessKey: walletDetails.awsKmsSecretAccessKey ?? undefined, }); break; @@ -103,8 +103,8 @@ export const getWallet = async ({ const adminWallet = new AwsKmsWallet({ keyId: splitArn.keyId, region: splitArn.region, - accessKeyId: walletDetails.awsKmsAccessKeyId, - secretAccessKey: walletDetails.awsKmsSecretAccessKey, + accessKeyId: walletDetails.awsKmsAccessKeyId ?? undefined, + secretAccessKey: walletDetails.awsKmsSecretAccessKey ?? undefined, }); const smartWallet: EVMWallet = await getSmartWallet({