diff --git a/backend/src/ee/routes/v1/pam-account-routers/pam-account-router.ts b/backend/src/ee/routes/v1/pam-account-routers/pam-account-router.ts index 10032788f..0c78b6449 100644 --- a/backend/src/ee/routes/v1/pam-account-routers/pam-account-router.ts +++ b/backend/src/ee/routes/v1/pam-account-routers/pam-account-router.ts @@ -6,6 +6,7 @@ import { PamAccountOrderBy, PamAccountView } from "@app/ee/services/pam-account/ import { SanitizedAwsIamAccountWithResourceSchema } from "@app/ee/services/pam-resource/aws-iam/aws-iam-resource-schemas"; import { SanitizedMySQLAccountWithResourceSchema } from "@app/ee/services/pam-resource/mysql/mysql-resource-schemas"; import { PamResource } from "@app/ee/services/pam-resource/pam-resource-enums"; +import { GatewayAccessResponseSchema } from "@app/ee/services/pam-resource/pam-resource-schemas"; import { SanitizedPostgresAccountWithResourceSchema } from "@app/ee/services/pam-resource/postgres/postgres-resource-schemas"; import { SanitizedSSHAccountWithResourceSchema } from "@app/ee/services/pam-resource/ssh/ssh-resource-schemas"; import { BadRequestError } from "@app/lib/errors"; @@ -130,51 +131,15 @@ export const registerPamAccountRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.discriminatedUnion("resourceType", [ - // Gateway-based resources (Postgres) - z.object({ - sessionId: z.string(), - resourceType: z.literal(PamResource.Postgres), - relayClientCertificate: z.string(), - relayClientPrivateKey: z.string(), - relayServerCertificateChain: z.string(), - gatewayClientCertificate: z.string(), - gatewayClientPrivateKey: z.string(), - gatewayServerCertificateChain: z.string(), - relayHost: z.string(), - metadata: z.record(z.string(), z.string().optional()).optional() - }), - // Gateway-based resources (MySQL) - z.object({ - sessionId: z.string(), - resourceType: z.literal(PamResource.MySQL), - relayClientCertificate: z.string(), - relayClientPrivateKey: z.string(), - relayServerCertificateChain: z.string(), - gatewayClientCertificate: z.string(), - gatewayClientPrivateKey: z.string(), - gatewayServerCertificateChain: z.string(), - relayHost: z.string(), - metadata: z.record(z.string(), z.string().optional()).optional() - }), - // Gateway-based resources (SSH) - z.object({ - sessionId: z.string(), - resourceType: z.literal(PamResource.SSH), - relayClientCertificate: z.string(), - relayClientPrivateKey: z.string(), - relayServerCertificateChain: z.string(), - gatewayClientCertificate: z.string(), - gatewayClientPrivateKey: z.string(), - gatewayServerCertificateChain: z.string(), - relayHost: z.string(), - metadata: z.record(z.string(), z.string().optional()).optional() - }), + // Gateway-based resources (Postgres, MySQL, SSH) + GatewayAccessResponseSchema.extend({ resourceType: z.literal(PamResource.Postgres) }), + GatewayAccessResponseSchema.extend({ resourceType: z.literal(PamResource.MySQL) }), + GatewayAccessResponseSchema.extend({ resourceType: z.literal(PamResource.SSH) }), // AWS IAM (no gateway, returns console URL) z.object({ sessionId: z.string(), resourceType: z.literal(PamResource.AwsIam), consoleUrl: z.string().url(), - projectId: z.string().uuid(), metadata: z.record(z.string(), z.string().optional()).optional() }) ]) @@ -203,7 +168,7 @@ export const registerPamAccountRouter = async (server: FastifyZodProvider) => { await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, orgId: req.permission.orgId, - projectId: response.projectId, + projectId: req.body.projectId, event: { type: EventType.PAM_ACCOUNT_ACCESS, metadata: { diff --git a/backend/src/ee/services/pam-account/pam-account-service.ts b/backend/src/ee/services/pam-account/pam-account-service.ts index bb00f2eaf..945db3624 100644 --- a/backend/src/ee/services/pam-account/pam-account-service.ts +++ b/backend/src/ee/services/pam-account/pam-account-service.ts @@ -616,7 +616,6 @@ export const pamAccountServiceFactory = ({ return { sessionId: session.id, resourceType, - projectId: account.projectId, account, consoleUrl, metadata: { diff --git a/backend/src/ee/services/pam-resource/aws-iam/aws-iam-federation.ts b/backend/src/ee/services/pam-resource/aws-iam/aws-iam-federation.ts index f9be88b2f..e387beaa1 100644 --- a/backend/src/ee/services/pam-resource/aws-iam/aws-iam-federation.ts +++ b/backend/src/ee/services/pam-resource/aws-iam/aws-iam-federation.ts @@ -1,8 +1,10 @@ -import { AssumeRoleCommand, STSClient, STSClientConfig } from "@aws-sdk/client-sts"; +import { AssumeRoleCommand, Credentials, STSClient, STSClientConfig } from "@aws-sdk/client-sts"; import { CustomAWSHasher } from "@app/lib/aws/hashing"; import { getConfig } from "@app/lib/config/env"; +import { request } from "@app/lib/config/request"; import { crypto } from "@app/lib/crypto/cryptography"; +import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { TAwsIamResourceConnectionDetails } from "./aws-iam-resource-types"; @@ -14,42 +16,123 @@ const AWS_STS_MIN_DURATION_SECONDS = 900; // 3. The target account's resources can be in any region - it doesn't affect STS calls const AWS_STS_DEFAULT_REGION = "us-east-1"; -const createStsClient = (): STSClient => { +const createStsClient = (credentials?: Credentials): STSClient => { const appCfg = getConfig(); const config: STSClientConfig = { region: AWS_STS_DEFAULT_REGION, useFipsEndpoint: crypto.isFipsModeEnabled(), - sha256: CustomAWSHasher, - credentials: - appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID && appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY - ? { - accessKeyId: appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID, - secretAccessKey: appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY - } - : undefined // if hosting on AWS + sha256: CustomAWSHasher }; + if (credentials) { + // Use provided credentials (for role chaining) + config.credentials = { + accessKeyId: credentials.AccessKeyId!, + secretAccessKey: credentials.SecretAccessKey!, + sessionToken: credentials.SessionToken + }; + } else if (appCfg.PAM_AWS_ACCESS_KEY_ID && appCfg.PAM_AWS_SECRET_ACCESS_KEY) { + // Use configured static credentials + config.credentials = { + accessKeyId: appCfg.PAM_AWS_ACCESS_KEY_ID, + secretAccessKey: appCfg.PAM_AWS_SECRET_ACCESS_KEY + }; + } + // Otherwise uses instance profile if hosting on AWS + return new STSClient(config); }; +/** + * Assumes the PAM role and returns the credentials. + * Returns null if assumption fails (for validation) or throws if throwOnError is true. + */ +const assumePamRole = async ({ + connectionDetails, + projectId, + sessionDuration = AWS_STS_MIN_DURATION_SECONDS, + sessionNameSuffix = "validation", + throwOnError = false +}: { + connectionDetails: TAwsIamResourceConnectionDetails; + projectId: string; + sessionDuration?: number; + sessionNameSuffix?: string; + throwOnError?: boolean; +}): Promise => { + const stsClient = createStsClient(); + + const result = await stsClient.send( + new AssumeRoleCommand({ + RoleArn: connectionDetails.roleArn, + RoleSessionName: `infisical-pam-${sessionNameSuffix}-${Date.now()}`, + DurationSeconds: sessionDuration, + ExternalId: projectId + }) + ); + + if (!result.Credentials) { + if (throwOnError) { + throw new InternalServerError({ + message: "Failed to assume PAM role - AWS STS did not return credentials" + }); + } + return null; + } + + return result.Credentials; +}; + +/** + * Assumes a target role using PAM role credentials (role chaining). + * Returns null if assumption fails (for validation) or throws if throwOnError is true. + */ +const assumeTargetRole = async ({ + pamCredentials, + targetRoleArn, + projectId, + roleSessionName, + sessionDuration = AWS_STS_MIN_DURATION_SECONDS, + throwOnError = false +}: { + pamCredentials: Credentials; + targetRoleArn: string; + projectId: string; + roleSessionName: string; + sessionDuration?: number; + throwOnError?: boolean; +}): Promise => { + const chainedStsClient = createStsClient(pamCredentials); + + const result = await chainedStsClient.send( + new AssumeRoleCommand({ + RoleArn: targetRoleArn, + RoleSessionName: roleSessionName, + DurationSeconds: sessionDuration, + ExternalId: projectId + }) + ); + + if (!result.Credentials) { + if (throwOnError) { + throw new BadRequestError({ + message: "Failed to assume target role - verify the target role trust policy allows the PAM role to assume it" + }); + } + return null; + } + + return result.Credentials; +}; + export const validatePamRoleConnection = async ( connectionDetails: TAwsIamResourceConnectionDetails, projectId: string ): Promise => { - const stsClient = createStsClient(); - try { - await stsClient.send( - new AssumeRoleCommand({ - RoleArn: connectionDetails.roleArn, - RoleSessionName: `infisical-pam-validation-${Date.now()}`, - DurationSeconds: AWS_STS_MIN_DURATION_SECONDS, - ExternalId: projectId - }) - ); - - return true; + const credentials = await assumePamRole({ connectionDetails, projectId }); + return credentials !== null; } catch { return false; } @@ -64,45 +147,17 @@ export const validateTargetRoleAssumption = async ({ targetRoleArn: string; projectId: string; }): Promise => { - const stsClient = createStsClient(); - try { - // First assume the PAM role - const pamRoleCredentials = await stsClient.send( - new AssumeRoleCommand({ - RoleArn: connectionDetails.roleArn, - RoleSessionName: `infisical-pam-validation-${Date.now()}`, - DurationSeconds: AWS_STS_MIN_DURATION_SECONDS, - ExternalId: projectId - }) - ); + const pamCredentials = await assumePamRole({ connectionDetails, projectId }); + if (!pamCredentials) return false; - if (!pamRoleCredentials.Credentials) { - return false; - } - - // Then use the PAM role credentials to assume the target role - const pamStsClient = new STSClient({ - region: AWS_STS_DEFAULT_REGION, - useFipsEndpoint: crypto.isFipsModeEnabled(), - sha256: CustomAWSHasher, - credentials: { - accessKeyId: pamRoleCredentials.Credentials.AccessKeyId!, - secretAccessKey: pamRoleCredentials.Credentials.SecretAccessKey!, - sessionToken: pamRoleCredentials.Credentials.SessionToken - } + const targetCredentials = await assumeTargetRole({ + pamCredentials, + targetRoleArn, + projectId, + roleSessionName: `infisical-pam-target-validation-${Date.now()}` }); - - await pamStsClient.send( - new AssumeRoleCommand({ - RoleArn: targetRoleArn, - RoleSessionName: `infisical-pam-target-validation-${Date.now()}`, - DurationSeconds: AWS_STS_MIN_DURATION_SECONDS, - ExternalId: projectId - }) - ); - - return true; + return targetCredentials !== null; } catch { return false; } @@ -124,48 +179,24 @@ export const generateConsoleFederationUrl = async ({ projectId: string; sessionDuration: number; }): Promise<{ consoleUrl: string; expiresAt: Date }> => { - const stsClient = createStsClient(); - - // First assume the PAM role - const pamRoleCredentials = await stsClient.send( - new AssumeRoleCommand({ - RoleArn: connectionDetails.roleArn, - RoleSessionName: `infisical-pam-${Date.now()}`, - DurationSeconds: sessionDuration, - ExternalId: projectId - }) - ); - - if (!pamRoleCredentials.Credentials) { - throw new Error("Failed to assume PAM role"); - } - - // Role chaining: use PAM role credentials to assume the target role - const pamStsClient = new STSClient({ - region: AWS_STS_DEFAULT_REGION, - useFipsEndpoint: crypto.isFipsModeEnabled(), - sha256: CustomAWSHasher, - credentials: { - accessKeyId: pamRoleCredentials.Credentials.AccessKeyId!, - secretAccessKey: pamRoleCredentials.Credentials.SecretAccessKey!, - sessionToken: pamRoleCredentials.Credentials.SessionToken - } + const pamCredentials = await assumePamRole({ + connectionDetails, + projectId, + sessionDuration, + sessionNameSuffix: "session", + throwOnError: true }); - const targetRoleCredentials = await pamStsClient.send( - new AssumeRoleCommand({ - RoleArn: targetRoleArn, - RoleSessionName: roleSessionName, - DurationSeconds: sessionDuration, - ExternalId: projectId - }) - ); + const targetCredentials = await assumeTargetRole({ + pamCredentials: pamCredentials!, + targetRoleArn, + projectId, + roleSessionName, + sessionDuration, + throwOnError: true + }); - if (!targetRoleCredentials.Credentials) { - throw new Error("Failed to assume target role"); - } - - const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = targetRoleCredentials.Credentials; + const { AccessKeyId, SecretAccessKey, SessionToken, Expiration } = targetCredentials!; // Generate federation URL const sessionJson = JSON.stringify({ @@ -178,25 +209,20 @@ export const generateConsoleFederationUrl = async ({ const signinTokenUrl = `${federationEndpoint}?Action=getSigninToken&Session=${encodeURIComponent(sessionJson)}`; - const tokenResponse = await fetch(signinTokenUrl); + const tokenResponse = await request.get<{ SigninToken?: string }>(signinTokenUrl); - if (!tokenResponse.ok) { - const errorText = await tokenResponse.text(); - // eslint-disable-next-line no-console - throw new Error(`AWS federation endpoint returned error (${tokenResponse.status}): ${errorText.substring(0, 200)}`); + if (!tokenResponse.data.SigninToken) { + throw new InternalServerError({ + message: `AWS federation endpoint did not return a SigninToken: ${JSON.stringify(tokenResponse.data).substring(0, 200)}` + }); } - const responseText = await tokenResponse.text(); - let tokenData: { SigninToken: string }; - - try { - tokenData = JSON.parse(responseText) as { SigninToken: string }; - } catch { - throw new Error(`AWS federation endpoint returned invalid response: ${responseText.substring(0, 200)}`); - } + const tokenData = tokenResponse.data; if (!tokenData.SigninToken) { - throw new Error(`AWS federation endpoint did not return a SigninToken: ${responseText.substring(0, 200)}`); + throw new InternalServerError({ + message: `AWS federation endpoint did not return a SigninToken: ${JSON.stringify(tokenResponse.data).substring(0, 200)}` + }); } const consoleDestination = `https://console.aws.amazon.com/`; diff --git a/backend/src/ee/services/pam-resource/aws-iam/aws-iam-resource-schemas.ts b/backend/src/ee/services/pam-resource/aws-iam/aws-iam-resource-schemas.ts index 238d00b4c..2762977eb 100644 --- a/backend/src/ee/services/pam-resource/aws-iam/aws-iam-resource-schemas.ts +++ b/backend/src/ee/services/pam-resource/aws-iam/aws-iam-resource-schemas.ts @@ -51,13 +51,11 @@ export const AwsIamResourceListItemSchema = z.object({ export const CreateAwsIamResourceSchema = BaseCreatePamResourceSchema.extend({ connectionDetails: AwsIamResourceConnectionDetailsSchema, - gatewayId: z.string().uuid().nullable().optional(), rotationAccountCredentials: AwsIamAccountCredentialsSchema.nullable().optional() }); export const UpdateAwsIamResourceSchema = BaseUpdatePamResourceSchema.extend({ connectionDetails: AwsIamResourceConnectionDetailsSchema.optional(), - gatewayId: z.string().uuid().nullable().optional(), rotationAccountCredentials: AwsIamAccountCredentialsSchema.nullable().optional() }); diff --git a/backend/src/ee/services/pam-resource/mysql/mysql-resource-schemas.ts b/backend/src/ee/services/pam-resource/mysql/mysql-resource-schemas.ts index 8d3589a8a..cb12a4c8c 100644 --- a/backend/src/ee/services/pam-resource/mysql/mysql-resource-schemas.ts +++ b/backend/src/ee/services/pam-resource/mysql/mysql-resource-schemas.ts @@ -2,13 +2,13 @@ import { z } from "zod"; import { PamResource } from "../pam-resource-enums"; import { + BaseCreateGatewayPamResourceSchema, BaseCreatePamAccountSchema, - BaseCreatePamResourceSchema, BasePamAccountSchema, BasePamAccountSchemaWithResource, BasePamResourceSchema, - BaseUpdatePamAccountSchema, - BaseUpdatePamResourceSchema + BaseUpdateGatewayPamResourceSchema, + BaseUpdatePamAccountSchema } from "../pam-resource-schemas"; import { BaseSqlAccountCredentialsSchema, @@ -43,12 +43,12 @@ export const MySQLResourceListItemSchema = z.object({ resource: z.literal(PamResource.MySQL) }); -export const CreateMySQLResourceSchema = BaseCreatePamResourceSchema.extend({ +export const CreateMySQLResourceSchema = BaseCreateGatewayPamResourceSchema.extend({ connectionDetails: MySQLResourceConnectionDetailsSchema, rotationAccountCredentials: MySQLAccountCredentialsSchema.nullable().optional() }); -export const UpdateMySQLResourceSchema = BaseUpdatePamResourceSchema.extend({ +export const UpdateMySQLResourceSchema = BaseUpdateGatewayPamResourceSchema.extend({ connectionDetails: MySQLResourceConnectionDetailsSchema.optional(), rotationAccountCredentials: MySQLAccountCredentialsSchema.nullable().optional() }); diff --git a/backend/src/ee/services/pam-resource/pam-resource-schemas.ts b/backend/src/ee/services/pam-resource/pam-resource-schemas.ts index 17ed1ccd1..a3db6b446 100644 --- a/backend/src/ee/services/pam-resource/pam-resource-schemas.ts +++ b/backend/src/ee/services/pam-resource/pam-resource-schemas.ts @@ -3,6 +3,18 @@ import { z } from "zod"; import { PamAccountsSchema, PamResourcesSchema } from "@app/db/schemas"; import { slugSchema } from "@app/server/lib/schemas"; +export const GatewayAccessResponseSchema = z.object({ + sessionId: z.string(), + relayClientCertificate: z.string(), + relayClientPrivateKey: z.string(), + relayServerCertificateChain: z.string(), + gatewayClientCertificate: z.string(), + gatewayClientPrivateKey: z.string(), + gatewayServerCertificateChain: z.string(), + relayHost: z.string(), + metadata: z.record(z.string(), z.string().optional()).optional() +}); + // Resources export const BasePamResourceSchema = PamResourcesSchema.omit({ encryptedConnectionDetails: true, @@ -10,17 +22,27 @@ export const BasePamResourceSchema = PamResourcesSchema.omit({ resourceType: true }); -export const BaseCreatePamResourceSchema = z.object({ +const CoreCreatePamResourceSchema = z.object({ projectId: z.string().uuid(), - gatewayId: z.string().uuid(), name: slugSchema({ field: "name" }) }); -export const BaseUpdatePamResourceSchema = z.object({ - gatewayId: z.string().uuid().optional(), +export const BaseCreateGatewayPamResourceSchema = CoreCreatePamResourceSchema.extend({ + gatewayId: z.string().uuid() +}); + +export const BaseCreatePamResourceSchema = CoreCreatePamResourceSchema; + +const CoreUpdatePamResourceSchema = z.object({ name: slugSchema({ field: "name" }).optional() }); +export const BaseUpdateGatewayPamResourceSchema = CoreUpdatePamResourceSchema.extend({ + gatewayId: z.string().uuid().optional() +}); + +export const BaseUpdatePamResourceSchema = CoreUpdatePamResourceSchema; + // Accounts export const BasePamAccountSchema = PamAccountsSchema.omit({ encryptedCredentials: true diff --git a/backend/src/ee/services/pam-resource/postgres/postgres-resource-schemas.ts b/backend/src/ee/services/pam-resource/postgres/postgres-resource-schemas.ts index bbe83a3a4..fd58484f7 100644 --- a/backend/src/ee/services/pam-resource/postgres/postgres-resource-schemas.ts +++ b/backend/src/ee/services/pam-resource/postgres/postgres-resource-schemas.ts @@ -2,13 +2,13 @@ import { z } from "zod"; import { PamResource } from "../pam-resource-enums"; import { + BaseCreateGatewayPamResourceSchema, BaseCreatePamAccountSchema, - BaseCreatePamResourceSchema, BasePamAccountSchema, BasePamAccountSchemaWithResource, BasePamResourceSchema, - BaseUpdatePamAccountSchema, - BaseUpdatePamResourceSchema + BaseUpdateGatewayPamResourceSchema, + BaseUpdatePamAccountSchema } from "../pam-resource-schemas"; import { BaseSqlAccountCredentialsSchema, @@ -40,12 +40,12 @@ export const PostgresResourceListItemSchema = z.object({ resource: z.literal(PamResource.Postgres) }); -export const CreatePostgresResourceSchema = BaseCreatePamResourceSchema.extend({ +export const CreatePostgresResourceSchema = BaseCreateGatewayPamResourceSchema.extend({ connectionDetails: PostgresResourceConnectionDetailsSchema, rotationAccountCredentials: PostgresAccountCredentialsSchema.nullable().optional() }); -export const UpdatePostgresResourceSchema = BaseUpdatePamResourceSchema.extend({ +export const UpdatePostgresResourceSchema = BaseUpdateGatewayPamResourceSchema.extend({ connectionDetails: PostgresResourceConnectionDetailsSchema.optional(), rotationAccountCredentials: PostgresAccountCredentialsSchema.nullable().optional() }); diff --git a/backend/src/ee/services/pam-resource/ssh/ssh-resource-schemas.ts b/backend/src/ee/services/pam-resource/ssh/ssh-resource-schemas.ts index 97d462369..01b8ef2c0 100644 --- a/backend/src/ee/services/pam-resource/ssh/ssh-resource-schemas.ts +++ b/backend/src/ee/services/pam-resource/ssh/ssh-resource-schemas.ts @@ -2,13 +2,13 @@ import { z } from "zod"; import { PamResource } from "../pam-resource-enums"; import { + BaseCreateGatewayPamResourceSchema, BaseCreatePamAccountSchema, - BaseCreatePamResourceSchema, BasePamAccountSchema, BasePamAccountSchemaWithResource, BasePamResourceSchema, - BaseUpdatePamAccountSchema, - BaseUpdatePamResourceSchema + BaseUpdateGatewayPamResourceSchema, + BaseUpdatePamAccountSchema } from "../pam-resource-schemas"; import { SSHAuthMethod } from "./ssh-resource-enums"; @@ -73,12 +73,12 @@ export const SanitizedSSHResourceSchema = BaseSSHResourceSchema.extend({ .optional() }); -export const CreateSSHResourceSchema = BaseCreatePamResourceSchema.extend({ +export const CreateSSHResourceSchema = BaseCreateGatewayPamResourceSchema.extend({ connectionDetails: SSHResourceConnectionDetailsSchema, rotationAccountCredentials: SSHAccountCredentialsSchema.nullable().optional() }); -export const UpdateSSHResourceSchema = BaseUpdatePamResourceSchema.extend({ +export const UpdateSSHResourceSchema = BaseUpdateGatewayPamResourceSchema.extend({ connectionDetails: SSHResourceConnectionDetailsSchema.optional(), rotationAccountCredentials: SSHAccountCredentialsSchema.nullable().optional() }); diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 21e83c2b7..14eb60192 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -286,6 +286,10 @@ const envSchema = z DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY: zpStr(z.string().optional()).default( process.env.INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY ), + + // PAM AWS credentials (for AWS IAM PAM resource type) + PAM_AWS_ACCESS_KEY_ID: zpStr(z.string().optional()), + PAM_AWS_SECRET_ACCESS_KEY: zpStr(z.string().optional()), /* ----------------------------------------------------------------------------- */ /* App Connections ----------------------------------------------------------------------------- */ diff --git a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountForm/AwsIamAccountForm.tsx b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountForm/AwsIamAccountForm.tsx index 0e9f2db4b..904bee1cf 100644 --- a/frontend/src/pages/pam/PamAccountsPage/components/PamAccountForm/AwsIamAccountForm.tsx +++ b/frontend/src/pages/pam/PamAccountsPage/components/PamAccountForm/AwsIamAccountForm.tsx @@ -18,7 +18,7 @@ import { CopyButton } from "@app/components/v2/CopyButton"; import { useProject } from "@app/context"; import { PamResourceType, TAwsIamAccount } from "@app/hooks/api/pam"; -import { GenericAccountFields } from "./GenericAccountFields"; +import { GenericAccountFields, genericAccountFieldsSchema } from "./GenericAccountFields"; type Props = { account?: TAwsIamAccount; @@ -45,12 +45,7 @@ const AwsIamCredentialsSchema = z.object({ .default(3600) }); -const genericAwsIamAccountFieldsSchema = z.object({ - name: z.string().min(1, "Name is required").max(64, "Name must be at most 64 characters"), - description: z.string().max(512).optional().nullable() -}); - -const formSchema = genericAwsIamAccountFieldsSchema.extend({ +const formSchema = genericAccountFieldsSchema.extend({ credentials: AwsIamCredentialsSchema }); @@ -153,8 +148,8 @@ export const AwsIamAccountForm = ({ account, onSubmit }: Props) => {

- The target role must have a trust policy that allows the Infisical PAM role to - assume it. If you used the{" "} + The target role must have a trust policy that allows the Infisical PAM role you + created and used in the "Resources" tab to assume it. If you used the{" "} infisical-pam-*{" "} naming convention, no additional changes are needed to the PAM role.

diff --git a/frontend/src/pages/pam/PamResourcesPage/components/PamResourceForm/AwsIamResourceForm.tsx b/frontend/src/pages/pam/PamResourcesPage/components/PamResourceForm/AwsIamResourceForm.tsx index 194cecb48..515619449 100644 --- a/frontend/src/pages/pam/PamResourcesPage/components/PamResourceForm/AwsIamResourceForm.tsx +++ b/frontend/src/pages/pam/PamResourcesPage/components/PamResourceForm/AwsIamResourceForm.tsx @@ -66,7 +66,7 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => { "Statement": [{ "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::${INFISICAL_AWS_ACCOUNT_US}:root" + "AWS": "arn:aws:iam:::root" }, "Action": "sts:AssumeRole", "Condition": { @@ -186,7 +186,11 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => { {INFISICAL_AWS_ACCOUNT_EU} {" "} - for EU region. The External ID{" "} + for EU region. Replace{" "} + + <INFISICAL_AWS_ACCOUNT_ID> + {" "} + with the appropriate Infisical AWS account ID for your region. The External ID{" "} {projectId} is your current project ID.