mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: enhance AWS IAM resource handling with new gateway access schema and improved project ID management
- Introduced GatewayAccessResponseSchema for consistent response structures across Postgres, MySQL, and SSH resources. - Updated PAM account router to utilize the new schema, streamlining response validation. - Refactored AWS IAM service to improve project ID handling during role assumption and credential management. - Enhanced AWS IAM resource schemas to support gateway-specific configurations, improving flexibility and type safety.
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -616,7 +616,6 @@ export const pamAccountServiceFactory = ({
|
||||
return {
|
||||
sessionId: session.id,
|
||||
resourceType,
|
||||
projectId: account.projectId,
|
||||
account,
|
||||
consoleUrl,
|
||||
metadata: {
|
||||
|
||||
@@ -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<Credentials | null> => {
|
||||
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<Credentials | null> => {
|
||||
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<boolean> => {
|
||||
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<boolean> => {
|
||||
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/`;
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
@@ -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 ----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -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) => {
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="px-4 pb-2.5">
|
||||
<p className="mb-3 text-sm text-mineshaft-300">
|
||||
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{" "}
|
||||
<code className="rounded bg-mineshaft-700 px-1 text-xs">infisical-pam-*</code>{" "}
|
||||
naming convention, no additional changes are needed to the PAM role.
|
||||
</p>
|
||||
|
||||
@@ -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::<INFISICAL_AWS_ACCOUNT_ID>:root"
|
||||
},
|
||||
"Action": "sts:AssumeRole",
|
||||
"Condition": {
|
||||
@@ -186,7 +186,11 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
|
||||
<code className="rounded bg-mineshaft-700 px-1 font-bold">
|
||||
{INFISICAL_AWS_ACCOUNT_EU}
|
||||
</code>{" "}
|
||||
for EU region. The External ID{" "}
|
||||
for EU region. Replace{" "}
|
||||
<code className="rounded bg-mineshaft-700 px-1 font-bold">
|
||||
<INFISICAL_AWS_ACCOUNT_ID>
|
||||
</code>{" "}
|
||||
with the appropriate Infisical AWS account ID for your region. The External ID{" "}
|
||||
<code className="rounded bg-mineshaft-700 px-1 font-bold">{projectId}</code> is your
|
||||
current project ID.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user