refactor: migrate Secrets Manager secrets to SSM Parameter Store (free tier)
This commit is contained in:
@@ -8,6 +8,7 @@ import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
|
||||
import * as apigatewayv2integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
||||
import * as iam from 'aws-cdk-lib/aws-iam';
|
||||
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
||||
import * as ssm from 'aws-cdk-lib/aws-ssm';
|
||||
import * as events from 'aws-cdk-lib/aws-events';
|
||||
import * as eventsTargets from 'aws-cdk-lib/aws-events-targets';
|
||||
import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources';
|
||||
@@ -20,25 +21,33 @@ export class AgentClawStack extends cdk.Stack {
|
||||
super(scope, id, props);
|
||||
|
||||
// ── Context parameters ─────────────────────────────────────────────────
|
||||
const telegramBotTokenSecretArn = this.node.tryGetContext('telegramBotTokenSecretArn') as string | undefined;
|
||||
const braveApiKeySecretArn = this.node.tryGetContext('braveApiKeySecretArn') as string | undefined;
|
||||
const telegramBotTokenParamName = this.node.tryGetContext('telegramBotTokenParamName') as string | undefined;
|
||||
const braveApiKeyParamName = this.node.tryGetContext('braveApiKeyParamName') as string | undefined;
|
||||
const googleOAuthClientParamName = this.node.tryGetContext('googleOAuthClientParamName') as string | undefined;
|
||||
const existingWorkspaceBucketName = this.node.tryGetContext('workspaceBucketName') as string | undefined;
|
||||
const runtime1Arn = this.node.tryGetContext('runtime1Arn') as string | undefined;
|
||||
|
||||
if (!telegramBotTokenSecretArn) {
|
||||
throw new Error('Context param required: telegramBotTokenSecretArn');
|
||||
if (!telegramBotTokenParamName) {
|
||||
throw new Error('Context param required: telegramBotTokenParamName');
|
||||
}
|
||||
if (!braveApiKeySecretArn) {
|
||||
throw new Error('Context param required: braveApiKeySecretArn');
|
||||
if (!braveApiKeyParamName) {
|
||||
throw new Error('Context param required: braveApiKeyParamName');
|
||||
}
|
||||
if (!googleOAuthClientParamName) {
|
||||
throw new Error('Context param required: googleOAuthClientParamName');
|
||||
}
|
||||
|
||||
// ── Secrets (reference existing) ───────────────────────────────────────
|
||||
const botTokenSecret = secretsmanager.Secret.fromSecretCompleteArn(
|
||||
this, 'TelegramBotToken', telegramBotTokenSecretArn
|
||||
);
|
||||
const braveApiKeySecret = secretsmanager.Secret.fromSecretCompleteArn(
|
||||
this, 'BraveApiKey', braveApiKeySecretArn
|
||||
);
|
||||
// ── SSM Parameters (reference existing SecureString params) ────────────
|
||||
const ssmParamArns = [
|
||||
`arn:aws:ssm:${this.region}:${this.account}:parameter${telegramBotTokenParamName}`,
|
||||
`arn:aws:ssm:${this.region}:${this.account}:parameter${braveApiKeyParamName}`,
|
||||
`arn:aws:ssm:${this.region}:${this.account}:parameter${googleOAuthClientParamName}`,
|
||||
];
|
||||
|
||||
const ssmReadPolicy = new iam.PolicyStatement({
|
||||
actions: ['ssm:GetParameter'],
|
||||
resources: ssmParamArns,
|
||||
});
|
||||
|
||||
// ── S3 workspace bucket ────────────────────────────────────────────────
|
||||
const workspaceBucket = existingWorkspaceBucketName
|
||||
@@ -94,13 +103,13 @@ export class AgentClawStack extends cdk.Stack {
|
||||
memorySize: 128,
|
||||
environment: {
|
||||
MESSAGE_QUEUE_URL: messageQueue.queueUrl,
|
||||
TELEGRAM_BOT_TOKEN_SECRET_ARN: telegramBotTokenSecretArn,
|
||||
TELEGRAM_BOT_TOKEN_SSM_PARAM: telegramBotTokenParamName,
|
||||
TELEGRAM_WEBHOOK_SECRET: '', // set via SSM or direct env after deploy
|
||||
ATTACHMENTS_BUCKET_NAME: workspaceBucket.bucketName,
|
||||
},
|
||||
});
|
||||
messageQueue.grantSendMessages(tgIngestFn);
|
||||
botTokenSecret.grantRead(tgIngestFn);
|
||||
tgIngestFn.addToRolePolicy(ssmReadPolicy);
|
||||
workspaceBucket.grantWrite(tgIngestFn);
|
||||
|
||||
// ── Lambda: agent-runner ───────────────────────────────────────────────
|
||||
@@ -114,8 +123,8 @@ export class AgentClawStack extends cdk.Stack {
|
||||
environment: {
|
||||
SESSION_TABLE_NAME: sessionTable.tableName,
|
||||
WORKSPACE_BUCKET_NAME: workspaceBucket.bucketName,
|
||||
TELEGRAM_BOT_TOKEN_SECRET_ARN: telegramBotTokenSecretArn,
|
||||
BRAVE_API_KEY_SECRET_ARN: braveApiKeySecretArn,
|
||||
TELEGRAM_BOT_TOKEN_SSM_PARAM: telegramBotTokenParamName,
|
||||
BRAVE_API_KEY_SSM_PARAM: braveApiKeyParamName,
|
||||
RUNTIME_1_ARN: runtime1Arn ?? 'PLACEHOLDER_SET_AFTER_RUNTIME_DEPLOY',
|
||||
AWS_REGION_NAME: 'us-east-1',
|
||||
},
|
||||
@@ -125,8 +134,7 @@ export class AgentClawStack extends cdk.Stack {
|
||||
usersTable.grantReadWriteData(agentRunnerFn);
|
||||
agentRunnerFn.addEnvironment('USERS_TABLE_NAME', usersTable.tableName);
|
||||
workspaceBucket.grantRead(agentRunnerFn);
|
||||
botTokenSecret.grantRead(agentRunnerFn);
|
||||
braveApiKeySecret.grantRead(agentRunnerFn);
|
||||
agentRunnerFn.addToRolePolicy(ssmReadPolicy);
|
||||
messageQueue.grantConsumeMessages(agentRunnerFn);
|
||||
|
||||
// AgentCore invoke permission
|
||||
@@ -172,8 +180,7 @@ export class AgentClawStack extends cdk.Stack {
|
||||
resources: ['*'],
|
||||
}));
|
||||
workspaceBucket.grantRead(runtime1Role);
|
||||
botTokenSecret.grantRead(runtime1Role);
|
||||
braveApiKeySecret.grantRead(runtime1Role);
|
||||
runtime1Role.addToPolicy(ssmReadPolicy);
|
||||
usersTable.grantReadWriteData(runtime1Role);
|
||||
// Google secret grants added after workspace_mcp section below
|
||||
runtime1Role.addToPolicy(new iam.PolicyStatement({
|
||||
@@ -192,15 +199,12 @@ export class AgentClawStack extends cdk.Stack {
|
||||
// and fed back as context param runtime1Arn.
|
||||
|
||||
// ── Google Workspace MCP ──────────────────────────────────────────────
|
||||
const googleOAuthClientSecret = secretsmanager.Secret.fromSecretCompleteArn(
|
||||
this, 'GoogleOAuthClient', 'arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl'
|
||||
);
|
||||
|
||||
// workspace-mcp Lambda execution role (import existing — created during initial setup)
|
||||
const _workspaceMcpRole = iam.Role.fromRoleName(
|
||||
this, 'WorkspaceMcpRole', 'agent-claw-workspace-mcp-role'
|
||||
);
|
||||
googleOAuthClientSecret.grantRead(_workspaceMcpRole);
|
||||
_workspaceMcpRole.addToPrincipalPolicy?.(ssmReadPolicy);
|
||||
// Grant workspace-mcp role read access to all per-user Google credential secrets
|
||||
(_workspaceMcpRole as iam.Role).addToPrincipalPolicy?.(new iam.PolicyStatement({
|
||||
sid: 'PerUserGoogleCredentialsRead',
|
||||
@@ -225,8 +229,7 @@ export class AgentClawStack extends cdk.Stack {
|
||||
conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': 'AWS_IAM' } },
|
||||
}));
|
||||
|
||||
// Grant AgentCore execution role read access to Google OAuth client + per-user credentials
|
||||
googleOAuthClientSecret.grantRead(runtime1Role);
|
||||
// Grant AgentCore execution role read access to per-user Google credentials (stays in Secrets Manager)
|
||||
runtime1Role.addToPolicy(new iam.PolicyStatement({
|
||||
sid: 'PerUserGoogleCredentialsReadRuntime',
|
||||
actions: ['secretsmanager:GetSecretValue'],
|
||||
@@ -250,22 +253,15 @@ export class AgentClawStack extends cdk.Stack {
|
||||
timeout: cdk.Duration.seconds(30),
|
||||
memorySize: 128,
|
||||
environment: {
|
||||
GOOGLE_OAUTH_CLIENT_SECRET_ARN: googleOAuthClientSecret.secretArn,
|
||||
GOOGLE_OAUTH_CLIENT_SSM_PARAM: googleOAuthClientParamName,
|
||||
USERS_TABLE_NAME: usersTable.tableName,
|
||||
TELEGRAM_BOT_TOKEN_SECRET_ARN: telegramBotTokenSecretArn,
|
||||
TELEGRAM_BOT_TOKEN_SSM_PARAM: telegramBotTokenParamName,
|
||||
// OAUTH_REDIRECT_URI set after API GW URL is known — injected via addEnvironment below
|
||||
OAUTH_REDIRECT_URI: 'PLACEHOLDER',
|
||||
},
|
||||
});
|
||||
googleOAuthClientSecret.grantRead(oauthHandlerFn);
|
||||
botTokenSecret.grantRead(oauthHandlerFn);
|
||||
oauthHandlerFn.addToRolePolicy(ssmReadPolicy);
|
||||
usersTable.grantReadWriteData(oauthHandlerFn);
|
||||
// Explicit access to the OAuth client secret (fromSecretNameV2 wildcard may not resolve)
|
||||
oauthHandlerFn.addToRolePolicy(new iam.PolicyStatement({
|
||||
sid: 'GoogleOAuthClientSecretExact',
|
||||
actions: ['secretsmanager:GetSecretValue'],
|
||||
resources: ['arn:aws:secretsmanager:us-east-1:495395224548:secret:agent-claw/google-oauth-client-subXHl'],
|
||||
}));
|
||||
// Grant OAuth handler write access to per-user credential secrets
|
||||
oauthHandlerFn.addToRolePolicy(new iam.PolicyStatement({
|
||||
sid: 'PerUserGoogleCredentialsWrite',
|
||||
@@ -344,10 +340,10 @@ export class AgentClawStack extends cdk.Stack {
|
||||
timeout: cdk.Duration.seconds(30),
|
||||
memorySize: 128,
|
||||
environment: {
|
||||
TELEGRAM_BOT_TOKEN_SECRET_ARN: telegramBotTokenSecretArn,
|
||||
TELEGRAM_BOT_TOKEN_SSM_PARAM: telegramBotTokenParamName,
|
||||
},
|
||||
});
|
||||
botTokenSecret.grantRead(schedulerFn);
|
||||
schedulerFn.addToRolePolicy(ssmReadPolicy);
|
||||
// Allow EventBridge to invoke the scheduler Lambda
|
||||
schedulerFn.addPermission('EventBridgeInvoke', {
|
||||
principal: new iam.ServicePrincipal('events.amazonaws.com'),
|
||||
|
||||
Reference in New Issue
Block a user