diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/aws-iam-user-secret-rotation-router.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/aws-iam-user-secret-rotation-router.ts new file mode 100644 index 0000000000..489f3ea559 --- /dev/null +++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/aws-iam-user-secret-rotation-router.ts @@ -0,0 +1,19 @@ +import { + AwsIamUserSecretRotationGeneratedCredentialsSchema, + AwsIamUserSecretRotationSchema, + CreateAwsIamUserSecretRotationSchema, + UpdateAwsIamUserSecretRotationSchema +} from "@app/ee/services/secret-rotation-v2/aws-iam-user-secret"; +import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums"; + +import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints"; + +export const registerAwsIamUserSecretRotationRouter = async (server: FastifyZodProvider) => + registerSecretRotationEndpoints({ + type: SecretRotation.AwsIamUserSecret, + server, + responseSchema: AwsIamUserSecretRotationSchema, + createSchema: CreateAwsIamUserSecretRotationSchema, + updateSchema: UpdateAwsIamUserSecretRotationSchema, + generatedCredentialsSchema: AwsIamUserSecretRotationGeneratedCredentialsSchema + }); diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts index 1dacf1bd25..7fee081642 100644 --- a/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts +++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts @@ -1,6 +1,7 @@ import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums"; import { registerAuth0ClientSecretRotationRouter } from "./auth0-client-secret-rotation-router"; +import { registerAwsIamUserSecretRotationRouter } from "./aws-iam-user-secret-rotation-router"; import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router"; import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router"; @@ -12,5 +13,6 @@ export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record< > = { [SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter, [SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter, - [SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter + [SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter, + [SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter }; diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts index bfb8b38c06..fedcd4b8d1 100644 --- a/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts +++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { Auth0ClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/auth0-client-secret"; +import { AwsIamUserSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/aws-iam-user-secret"; import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials"; import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials"; import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema"; @@ -13,7 +14,8 @@ import { AuthMode } from "@app/services/auth/auth-type"; const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [ PostgresCredentialsRotationListItemSchema, MsSqlCredentialsRotationListItemSchema, - Auth0ClientSecretRotationListItemSchema + Auth0ClientSecretRotationListItemSchema, + AwsIamUserSecretRotationListItemSchema ]); export const registerSecretRotationV2Router = async (server: FastifyZodProvider) => { diff --git a/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-constants.ts b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-constants.ts new file mode 100644 index 0000000000..b4e0928fc3 --- /dev/null +++ b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-constants.ts @@ -0,0 +1,15 @@ +import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums"; +import { TSecretRotationV2ListItem } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; + +export const AWS_IAM_USER_SECRET_ROTATION_LIST_OPTION: TSecretRotationV2ListItem = { + name: "AWS IAM User Secret", + type: SecretRotation.AwsIamUserSecret, + connection: AppConnection.AWS, + template: { + secretsMapping: { + accessKeyId: "AWS_ACCESS_KEY", + secretAccessKey: "AWS_SECRET_ACCESS_KEY" + } + } +}; diff --git a/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-fns.ts b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-fns.ts new file mode 100644 index 0000000000..1c6e5d3bbd --- /dev/null +++ b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-fns.ts @@ -0,0 +1,125 @@ +import AWS from "aws-sdk"; + +import { + TAwsIamUserSecretRotationGeneratedCredentials, + TAwsIamUserSecretRotationWithConnection +} from "@app/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-types"; +import { + TRotationFactory, + TRotationFactoryGetSecretsPayload, + TRotationFactoryIssueCredentials, + TRotationFactoryRevokeCredentials, + TRotationFactoryRotateCredentials +} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types"; +import { getAwsConnectionConfig } from "@app/services/app-connection/aws"; + +export const awsIamUserSecretRotationFactory: TRotationFactory< + TAwsIamUserSecretRotationWithConnection, + TAwsIamUserSecretRotationGeneratedCredentials +> = (secretRotation) => { + const { + parameters: { region, clientName }, + connection, + secretsMapping + } = secretRotation; + + const $rotateClientSecret = async () => { + const { credentials } = await getAwsConnectionConfig(connection, region); + const iam = new AWS.IAM({ credentials }); + + const { AccessKeyMetadata } = await iam.listAccessKeys({ UserName: clientName }).promise(); + + if (AccessKeyMetadata && AccessKeyMetadata.length > 0) { + for (const key of AccessKeyMetadata) { + if (key.Status === "Inactive" && key.AccessKeyId) { + // eslint-disable-next-line no-await-in-loop + await iam + .deleteAccessKey({ + UserName: clientName, + AccessKeyId: key.AccessKeyId + }) + .promise(); + } + } + + const activeKey = AccessKeyMetadata.find((k) => k.Status === "Active"); + if (activeKey && activeKey.AccessKeyId) { + await iam + .updateAccessKey({ + UserName: clientName, + AccessKeyId: activeKey.AccessKeyId, + Status: "Inactive" + }) + .promise(); + } + } + + const { AccessKey } = await iam.createAccessKey({ UserName: clientName }).promise(); + + return { + accessKeyId: AccessKey.AccessKeyId, + secretAccessKey: AccessKey.SecretAccessKey + }; + }; + + const issueCredentials: TRotationFactoryIssueCredentials = async ( + callback + ) => { + const credentials = await $rotateClientSecret(); + + return callback(credentials); + }; + + const revokeCredentials: TRotationFactoryRevokeCredentials = async ( + generatedCredentials, + callback + ) => { + const { credentials } = await getAwsConnectionConfig(connection, region); + const iam = new AWS.IAM({ credentials }); + + for (const generatedCredential of generatedCredentials) { + // eslint-disable-next-line no-await-in-loop + await iam + .deleteAccessKey({ + UserName: clientName, + AccessKeyId: generatedCredential.accessKeyId + }) + .promise(); + } + + return callback(); + }; + + const rotateCredentials: TRotationFactoryRotateCredentials = async ( + _, + callback + ) => { + const credentials = await $rotateClientSecret(); + + return callback(credentials); + }; + + const getSecretsPayload: TRotationFactoryGetSecretsPayload = ( + generatedCredentials + ) => { + const secrets = [ + { + key: secretsMapping.accessKeyId, + value: generatedCredentials.accessKeyId + }, + { + key: secretsMapping.secretAccessKey, + value: generatedCredentials.secretAccessKey + } + ]; + + return secrets; + }; + + return { + issueCredentials, + revokeCredentials, + rotateCredentials, + getSecretsPayload + }; +}; diff --git a/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-schemas.ts b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-schemas.ts new file mode 100644 index 0000000000..70104d93a5 --- /dev/null +++ b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-schemas.ts @@ -0,0 +1,68 @@ +import { z } from "zod"; + +import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums"; +import { + BaseCreateSecretRotationSchema, + BaseSecretRotationSchema, + BaseUpdateSecretRotationSchema +} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-schemas"; +import { SecretRotations } from "@app/lib/api-docs"; +import { SecretNameSchema } from "@app/server/lib/schemas"; +import { AppConnection, AWSRegion } from "@app/services/app-connection/app-connection-enums"; + +export const AwsIamUserSecretRotationGeneratedCredentialsSchema = z + .object({ + accessKeyId: z.string(), + secretAccessKey: z.string() + }) + .array() + .min(1) + .max(2); + +const AwsIamUserSecretRotationParametersSchema = z.object({ + clientName: z + .string() + .trim() + .min(1, "Client Name Required") + .describe(SecretRotations.PARAMETERS.AWS_IAM_USER_SECRET.clientName), + region: z.nativeEnum(AWSRegion).describe(SecretRotations.PARAMETERS.AWS_IAM_USER_SECRET.region) +}); + +const AwsIamUserSecretRotationSecretsMappingSchema = z.object({ + accessKeyId: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.AWS_IAM_USER_SECRET.accessKeyId), + secretAccessKey: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.AWS_IAM_USER_SECRET.secretAccessKey) +}); + +export const AwsIamUserSecretRotationTemplateSchema = z.object({ + secretsMapping: z.object({ + accessKeyId: z.string(), + secretAccessKey: z.string() + }) +}); + +export const AwsIamUserSecretRotationSchema = BaseSecretRotationSchema(SecretRotation.AwsIamUserSecret).extend({ + type: z.literal(SecretRotation.AwsIamUserSecret), + parameters: AwsIamUserSecretRotationParametersSchema, + secretsMapping: AwsIamUserSecretRotationSecretsMappingSchema +}); + +export const CreateAwsIamUserSecretRotationSchema = BaseCreateSecretRotationSchema( + SecretRotation.AwsIamUserSecret +).extend({ + parameters: AwsIamUserSecretRotationParametersSchema, + secretsMapping: AwsIamUserSecretRotationSecretsMappingSchema +}); + +export const UpdateAwsIamUserSecretRotationSchema = BaseUpdateSecretRotationSchema( + SecretRotation.AwsIamUserSecret +).extend({ + parameters: AwsIamUserSecretRotationParametersSchema.optional(), + secretsMapping: AwsIamUserSecretRotationSecretsMappingSchema.optional() +}); + +export const AwsIamUserSecretRotationListItemSchema = z.object({ + name: z.literal("AWS IAM User Secret"), + connection: z.literal(AppConnection.AWS), + type: z.literal(SecretRotation.AwsIamUserSecret), + template: AwsIamUserSecretRotationTemplateSchema +}); diff --git a/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-types.ts b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-types.ts new file mode 100644 index 0000000000..5db7ef2b09 --- /dev/null +++ b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/aws-iam-user-secret-rotation-types.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; + +import { TAwsConnection } from "@app/services/app-connection/aws"; + +import { + AwsIamUserSecretRotationGeneratedCredentialsSchema, + AwsIamUserSecretRotationListItemSchema, + AwsIamUserSecretRotationSchema, + CreateAwsIamUserSecretRotationSchema +} from "./aws-iam-user-secret-rotation-schemas"; + +export type TAwsIamUserSecretRotation = z.infer; + +export type TAwsIamUserSecretRotationInput = z.infer; + +export type TAwsIamUserSecretRotationListItem = z.infer; + +export type TAwsIamUserSecretRotationWithConnection = TAwsIamUserSecretRotation & { + connection: TAwsConnection; +}; + +export type TAwsIamUserSecretRotationGeneratedCredentials = z.infer< + typeof AwsIamUserSecretRotationGeneratedCredentialsSchema +>; diff --git a/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/index.ts b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/index.ts new file mode 100644 index 0000000000..69635c68c6 --- /dev/null +++ b/backend/src/ee/services/secret-rotation-v2/aws-iam-user-secret/index.ts @@ -0,0 +1,3 @@ +export * from "./aws-iam-user-secret-rotation-constants"; +export * from "./aws-iam-user-secret-rotation-schemas"; +export * from "./aws-iam-user-secret-rotation-types"; diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts index d43cacb3a8..1e387a3b91 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts @@ -1,7 +1,8 @@ export enum SecretRotation { PostgresCredentials = "postgres-credentials", MsSqlCredentials = "mssql-credentials", - Auth0ClientSecret = "auth0-client-secret" + Auth0ClientSecret = "auth0-client-secret", + AwsIamUserSecret = "aws-iam-user-secret" } export enum SecretRotationStatus { diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts index 603b77cc1e..87115e7f4c 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts @@ -4,6 +4,7 @@ import { getConfig } from "@app/lib/config/env"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { AUTH0_CLIENT_SECRET_ROTATION_LIST_OPTION } from "./auth0-client-secret"; +import { AWS_IAM_USER_SECRET_ROTATION_LIST_OPTION } from "./aws-iam-user-secret"; import { MSSQL_CREDENTIALS_ROTATION_LIST_OPTION } from "./mssql-credentials"; import { POSTGRES_CREDENTIALS_ROTATION_LIST_OPTION } from "./postgres-credentials"; import { SecretRotation, SecretRotationStatus } from "./secret-rotation-v2-enums"; @@ -18,7 +19,8 @@ import { const SECRET_ROTATION_LIST_OPTIONS: Record = { [SecretRotation.PostgresCredentials]: POSTGRES_CREDENTIALS_ROTATION_LIST_OPTION, [SecretRotation.MsSqlCredentials]: MSSQL_CREDENTIALS_ROTATION_LIST_OPTION, - [SecretRotation.Auth0ClientSecret]: AUTH0_CLIENT_SECRET_ROTATION_LIST_OPTION + [SecretRotation.Auth0ClientSecret]: AUTH0_CLIENT_SECRET_ROTATION_LIST_OPTION, + [SecretRotation.AwsIamUserSecret]: AWS_IAM_USER_SECRET_ROTATION_LIST_OPTION }; export const listSecretRotationOptions = () => { diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts index 1050c3419a..74aba0017e 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts @@ -4,11 +4,13 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums export const SECRET_ROTATION_NAME_MAP: Record = { [SecretRotation.PostgresCredentials]: "PostgreSQL Credentials", [SecretRotation.MsSqlCredentials]: "Microsoft SQL Sever Credentials", - [SecretRotation.Auth0ClientSecret]: "Auth0 Client Secret" + [SecretRotation.Auth0ClientSecret]: "Auth0 Client Secret", + [SecretRotation.AwsIamUserSecret]: "AWS IAM User Secret" }; export const SECRET_ROTATION_CONNECTION_MAP: Record = { [SecretRotation.PostgresCredentials]: AppConnection.Postgres, [SecretRotation.MsSqlCredentials]: AppConnection.MsSql, - [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0 + [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0, + [SecretRotation.AwsIamUserSecret]: AppConnection.AWS }; diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts index a828acb32f..60094be97e 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts @@ -77,6 +77,7 @@ import { import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; +import { awsIamUserSecretRotationFactory } from "./aws-iam-user-secret/aws-iam-user-secret-rotation-fns"; import { TSecretRotationV2DALFactory } from "./secret-rotation-v2-dal"; export type TSecretRotationV2ServiceFactoryDep = { @@ -114,7 +115,8 @@ type TRotationFactoryImplementation = TRotationFactory< const SECRET_ROTATION_FACTORY_MAP: Record = { [SecretRotation.PostgresCredentials]: sqlCredentialsRotationFactory as TRotationFactoryImplementation, [SecretRotation.MsSqlCredentials]: sqlCredentialsRotationFactory as TRotationFactoryImplementation, - [SecretRotation.Auth0ClientSecret]: auth0ClientSecretRotationFactory as TRotationFactoryImplementation + [SecretRotation.Auth0ClientSecret]: auth0ClientSecretRotationFactory as TRotationFactoryImplementation, + [SecretRotation.AwsIamUserSecret]: awsIamUserSecretRotationFactory as TRotationFactoryImplementation }; export const secretRotationV2ServiceFactory = ({ diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-types.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-types.ts index c52fa54652..85fa68d4fe 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-types.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-types.ts @@ -12,6 +12,13 @@ import { TAuth0ClientSecretRotationListItem, TAuth0ClientSecretRotationWithConnection } from "./auth0-client-secret"; +import { + TAwsIamUserSecretRotation, + TAwsIamUserSecretRotationGeneratedCredentials, + TAwsIamUserSecretRotationInput, + TAwsIamUserSecretRotationListItem, + TAwsIamUserSecretRotationWithConnection +} from "./aws-iam-user-secret"; import { TMsSqlCredentialsRotation, TMsSqlCredentialsRotationInput, @@ -27,26 +34,34 @@ import { import { TSecretRotationV2DALFactory } from "./secret-rotation-v2-dal"; import { SecretRotation } from "./secret-rotation-v2-enums"; -export type TSecretRotationV2 = TPostgresCredentialsRotation | TMsSqlCredentialsRotation | TAuth0ClientSecretRotation; +export type TSecretRotationV2 = + | TPostgresCredentialsRotation + | TMsSqlCredentialsRotation + | TAuth0ClientSecretRotation + | TAwsIamUserSecretRotation; export type TSecretRotationV2WithConnection = | TPostgresCredentialsRotationWithConnection | TMsSqlCredentialsRotationWithConnection - | TAuth0ClientSecretRotationWithConnection; + | TAuth0ClientSecretRotationWithConnection + | TAwsIamUserSecretRotationWithConnection; export type TSecretRotationV2GeneratedCredentials = | TSqlCredentialsRotationGeneratedCredentials - | TAuth0ClientSecretRotationGeneratedCredentials; + | TAuth0ClientSecretRotationGeneratedCredentials + | TAwsIamUserSecretRotationGeneratedCredentials; export type TSecretRotationV2Input = | TPostgresCredentialsRotationInput | TMsSqlCredentialsRotationInput - | TAuth0ClientSecretRotationInput; + | TAuth0ClientSecretRotationInput + | TAwsIamUserSecretRotationInput; export type TSecretRotationV2ListItem = | TPostgresCredentialsRotationListItem | TMsSqlCredentialsRotationListItem - | TAuth0ClientSecretRotationListItem; + | TAuth0ClientSecretRotationListItem + | TAwsIamUserSecretRotationListItem; export type TSecretRotationV2Raw = NonNullable>>; diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema.ts index 2db9c02516..af46cb36c0 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema.ts @@ -4,8 +4,11 @@ import { Auth0ClientSecretRotationSchema } from "@app/ee/services/secret-rotatio import { MsSqlCredentialsRotationSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials"; import { PostgresCredentialsRotationSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials"; +import { AwsIamUserSecretRotationSchema } from "./aws-iam-user-secret"; + export const SecretRotationV2Schema = z.discriminatedUnion("type", [ PostgresCredentialsRotationSchema, MsSqlCredentialsRotationSchema, - Auth0ClientSecretRotationSchema + Auth0ClientSecretRotationSchema, + AwsIamUserSecretRotationSchema ]); diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index ff07940bc8..70c4b743e1 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -2015,6 +2015,10 @@ export const SecretRotations = { }, AUTH0_CLIENT_SECRET: { clientId: "The client ID of the Auth0 Application to rotate the client secret for." + }, + AWS_IAM_USER_SECRET: { + clientName: "The name of the client to rotate credentials for.", + region: "The AWS region to rotate credentials for." } }, SECRETS_MAPPING: { @@ -2025,6 +2029,10 @@ export const SecretRotations = { AUTH0_CLIENT_SECRET: { clientId: "The name of the secret that the client ID will be mapped to.", clientSecret: "The name of the secret that the rotated client secret will be mapped to." + }, + AWS_IAM_USER_SECRET: { + accessKeyId: "The name of the secret that the access key ID will be mapped to.", + secretAccessKey: "The name of the secret that the rotated secret access key will be mapped to." } } }; diff --git a/backend/src/server/routes/v1/app-connection-routers/aws-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/aws-connection-router.ts index 674e6e4172..ff4ef96f93 100644 --- a/backend/src/server/routes/v1/app-connection-routers/aws-connection-router.ts +++ b/backend/src/server/routes/v1/app-connection-routers/aws-connection-router.ts @@ -59,4 +59,30 @@ export const registerAwsConnectionRouter = async (server: FastifyZodProvider) => return { kmsKeys }; } }); + + server.route({ + method: "GET", + url: `/:connectionId/users`, + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + connectionId: z.string().uuid() + }) + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { connectionId } = req.params; + + const iamUsers = await server.services.appConnection.aws.listIamUsers( + { + connectionId + }, + req.permission + ); + + return { iamUsers }; + } + }); }; diff --git a/backend/src/services/app-connection/app-connection-types.ts b/backend/src/services/app-connection/app-connection-types.ts index 64d44ccc68..fcdf9a80dc 100644 --- a/backend/src/services/app-connection/app-connection-types.ts +++ b/backend/src/services/app-connection/app-connection-types.ts @@ -168,6 +168,10 @@ export type TListAwsConnectionKmsKeys = { destination: SecretSync.AWSParameterStore | SecretSync.AWSSecretsManager; }; +export type TListAwsConnectionIamUsers = { + connectionId: string; +}; + export type TAppConnectionCredentialsValidator = ( appConnection: TAppConnectionConfig ) => Promise; diff --git a/backend/src/services/app-connection/aws/aws-connection-service.ts b/backend/src/services/app-connection/aws/aws-connection-service.ts index 689608b811..891d12997d 100644 --- a/backend/src/services/app-connection/aws/aws-connection-service.ts +++ b/backend/src/services/app-connection/aws/aws-connection-service.ts @@ -2,7 +2,10 @@ import AWS from "aws-sdk"; import { OrgServiceActor } from "@app/lib/types"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; -import { TListAwsConnectionKmsKeys } from "@app/services/app-connection/app-connection-types"; +import { + TListAwsConnectionIamUsers, + TListAwsConnectionKmsKeys +} from "@app/services/app-connection/app-connection-types"; import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns"; import { TAwsConnection } from "@app/services/app-connection/aws/aws-connection-types"; import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; @@ -70,6 +73,16 @@ const listAwsKmsKeys = async ( return kmsKeys; }; +const listAwsIamUsers = async (appConnection: TAwsConnection) => { + const { credentials } = await getAwsConnectionConfig(appConnection); + + const iam = new AWS.IAM({ credentials }); + + const users = await iam.listUsers().promise(); + + return users.Users; +}; + export const awsConnectionService = (getAppConnection: TGetAppConnectionFunc) => { const listKmsKeys = async ( { connectionId, region, destination }: TListAwsConnectionKmsKeys, @@ -82,7 +95,16 @@ export const awsConnectionService = (getAppConnection: TGetAppConnectionFunc) => return kmsKeys; }; + const listIamUsers = async ({ connectionId }: TListAwsConnectionIamUsers, actor: OrgServiceActor) => { + const appConnection = await getAppConnection(AppConnection.AWS, connectionId, actor); + + const iamUsers = await listAwsIamUsers(appConnection); + + return iamUsers; + }; + return { - listKmsKeys + listKmsKeys, + listIamUsers }; }; diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/create.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/create.mdx new file mode 100644 index 0000000000..69557bb800 --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/create.mdx @@ -0,0 +1,9 @@ +--- +title: "Create" +openapi: "POST /api/v2/secret-rotations/aws-iam-user-secret" +--- + + + Check out the configuration docs for [AWS IAM User Secret Rotations](/documentation/platform/secret-rotation/aws-iam-user-secret) to learn how to obtain the + required parameters. + \ No newline at end of file diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/delete.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/delete.mdx new file mode 100644 index 0000000000..457e35b42b --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v2/secret-rotations/aws-iam-user-secret/{rotationId}" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-id.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-id.mdx new file mode 100644 index 0000000000..3e71aa4d7f --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by ID" +openapi: "GET /api/v2/secret-rotations/aws-iam-user-secret/{rotationId}" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-name.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-name.mdx new file mode 100644 index 0000000000..ccc4b9e281 --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-name.mdx @@ -0,0 +1,4 @@ +--- +title: "Get by Name" +openapi: "GET /api/v2/secret-rotations/aws-iam-user-secret/rotation-name/{rotationName}" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-generated-credentials-by-id.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-generated-credentials-by-id.mdx new file mode 100644 index 0000000000..0ade7a3d93 --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-generated-credentials-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Credentials by ID" +openapi: "GET /api/v2/secret-rotations/aws-iam-user-secret/{rotationId}/generated-credentials" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/list.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/list.mdx new file mode 100644 index 0000000000..6776b2d0fd --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/list.mdx @@ -0,0 +1,4 @@ +--- +title: "List" +openapi: "GET /api/v2/secret-rotations/aws-iam-user-secret" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/rotate-secrets.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/rotate-secrets.mdx new file mode 100644 index 0000000000..6eda840d42 --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/rotate-secrets.mdx @@ -0,0 +1,4 @@ +--- +title: "Rotate Secrets" +openapi: "POST /api/v2/secret-rotations/aws-iam-user-secret/{rotationId}/rotate-secrets" +--- diff --git a/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/update.mdx b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/update.mdx new file mode 100644 index 0000000000..e276af8d99 --- /dev/null +++ b/docs/api-reference/endpoints/secret-rotations/aws-iam-user-secret/update.mdx @@ -0,0 +1,9 @@ +--- +title: "Update" +openapi: "PATCH /api/v2/secret-rotations/aws-iam-user-secret/{rotationId}" +--- + + + Check out the configuration docs for [AWS IAM User Secret Rotations](/documentation/platform/secret-rotation/aws-iam-user-secret) to learn how to obtain the + required parameters. + \ No newline at end of file diff --git a/docs/documentation/platform/secret-rotation/aws-iam.mdx b/docs/documentation/platform/secret-rotation/aws-iam.mdx index c524abfbcf..01ae7e3f78 100644 --- a/docs/documentation/platform/secret-rotation/aws-iam.mdx +++ b/docs/documentation/platform/secret-rotation/aws-iam.mdx @@ -6,14 +6,41 @@ description: "Learn how to automatically rotate Access Key Id and Secret Key of Infisical's AWS IAM User secret rotation capability lets you update the **Access key** and **Secret access key** credentials of a target IAM user from within Infisical at a specified interval or on-demand. +## Prerequisites + +- Create an [AWS Connection](/integrations/app-connections/aws) with the required **Secret Rotation** audience and permissions +- Add the following permissions to your IAM Role/IAM User Permission policy set used by your AWS Connection: + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:ListAccessKeys", + "iam:CreateAccessKey", + "iam:UpdateAccessKey", + "iam:DeleteAccessKey" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "iam:ListUsers", + "Resource": "*" + } + ] + } + ``` + ## Workflow The typical workflow for using the AWS IAM User rotation strategy consists of four steps: 1. Creating the target IAM user whose credentials you wish to rotate. -2. Creating the managing IAM user used by Infisical to rotate the credentials of the target IAM user. -3. Configuring the rotation strategy in Infisical with the credentials of the managing IAM user. -4. Pressing the **Rotate** button in the Infisical dashboard to trigger the rotation of the target IAM user's credentials. The strategy can also be configured to rotate the credentials automatically at a specified interval. +2. Configuring the rotation strategy in Infisical with the credentials of the managing IAM user. +3. Pressing the **Rotate** button in the Infisical dashboard to trigger the rotation of the target IAM user's credentials. The strategy can also be configured to rotate the credentials automatically at a specified interval. In the following steps, we explore the end-to-end workflow for setting up this strategy in Infisical. @@ -22,122 +49,152 @@ In the following steps, we explore the end-to-end workflow for setting up this s To begin, create an IAM user whose credentials you wish to rotate. If you already have an IAM user, then you can skip this step. - - Next, create another IAM user to be used by Infisical to rotate the credentials of the IAM user in the previous step. - - 2.1. In your AWS console, head to IAM > Access management > Users and press **Create user**. - - ![iam user secret rotation create user](../../../images/platform/secret-rotation/aws-iam/rotation-manager-create-user.png) - - 2.2. Next, give the user a username like **infisical-rotation-manager** and press **Next**. - - ![iam user secret rotation username](../../../images/platform/secret-rotation/aws-iam/rotation-manager-username.png) - - 2.3. Next, in the **Set permissions** step, select **Attach policies directly** and then press **Create policy**. - - ![iam user secret rotation create policy](../../../images/platform/secret-rotation/aws-iam/rotation-manager-create-policy.png) - - 2.4. Next, in the **Policy editor**, paste the following JSON and press **Next**: - - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "VisualEditor0", - "Effect": "Allow", - "Action": [ - "iam:DeleteAccessKey", - "iam:GetAccessKeyLastUsed", - "iam:CreateAccessKey" - ], - "Resource": "*" - } - ] - } - ``` - - - The IAM policy above uses the wildcard option in Resource: "*". - - You may want to restrict the policy to a specific path, and make any adjustments as necessary, to control access for the managing user in production. - - Read more about this [here](https://aws.amazon.com/blogs/security/optimize-aws-administration-with-iam-paths/). - - - In the **Review and create** step, give the policy a name like **infisical-rotation-manager**, press **Create policy** to finish creating the policy. - - ![iam user secret rotation policy review](../../../images/platform/secret-rotation/aws-iam/rotation-manager-policy-review.png) - - 2.5. Back in the **Set permissions** step from step 2.3, refresh the policy list and search for the policy you just created from step 2.4. - - Select the policy and press **Next**. - - ![iam user secret rotation attach policy](../../../images/platform/secret-rotation/aws-iam/rotation-manager-attach-policy.png) - - In the **Review and create** step, press **Create user** to finish creating the IAM user. - - ![iam user secret rotation manager user review](../../../images/platform/secret-rotation/aws-iam/rotation-manager-user-review.png) - - 2.5. Having created the user, head to its Security credentials > Access keys and press **Create access key**. - - Follow the subsequent steps to create the **access key** and **secret access key** credential pair for the user. - - ![iam user secret rotation manager create access key](../../../images/platform/secret-rotation/aws-iam/rotation-manager-create-access-key.png) - - At the end of the flow, copy the **Access key** and **Secret access key** to use when configuring the AWS IAM User rotation strategy back in Infisical next. - - ![iam user secret rotation manager access keys](../../../images/platform/secret-rotation/aws-iam/rotation-manager-access-keys.png) - - 3.1. Back in Infisical, head to the Project > Secrets > Environment and path where you want the rotated AWS IAM credentials to appear and create two placeholder secrets. - - In this example, we'll create two secrets called `AWS_ACCESS_KEY` and `AWS_SECRET_ACCESS_KEY`. - - ![iam user secret rotation secrets](../../../images/platform/secret-rotation/aws-iam/rotation-config-secrets.png) - - 3.2. Next, in the **Secret Rotation** tab, press on the **AWS IAM** tile to configure the AWS IAM User rotation strategy. - - ![iam user secret rotation select aws iam user method](../../../images/platform/secret-rotation/aws-iam/rotations-select-aws-iam-user.png) - - 3.3. Input the configuration details for the AWS IAM User rotation strategy obtained from steps 1 and 2: - - ![iam user secret rotation config 1](../../../images/platform/secret-rotation/aws-iam/rotation-config-1.png) - - Here's some guidance on each field: - - - Manager User Access Key: The managing IAM user's access key from step 2.5. - - Manager User Secret Key: The managing IAM user's secret access key from step 2.5. - - Manager User AWS Region: The [AWS region](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) for Infisical to make requests to such as `us-east-1`. - - IAM Username: The IAM username of the user from step 1. - - Next, specify the output secret mappings configuration for the rotated AWS IAM credentials; this is the secrets whose values will be replaced with new credentials after each rotation. - Here, you can also specify a rotation interval for the credentials to be automatically rotated periodically. + + + 1. Navigate to your Secret Manager Project's Dashboard and select **Add Secret Rotation** from the actions dropdown. + ![Secret Manager Dashboard](/images/secret-rotations-v2/generic/add-secret-rotation.png) - In this example, we want to map the output of the rotated AWS IAM credentials to the secrets that we created in step 3.1 (i.e. `AWS_ACCESS_KEY` and `AWS_SECRET_ACCESS_KEY`). - - ![iam user secret rotation config 2](../../../images/platform/secret-rotation/aws-iam/rotation-config-2.png) - - Finally, press **Submit** to create the secret rotation strategy. - - - You should now see the AWS IAM User rotation strategy listed in the **Secret Rotation** tab. - - To manually trigger a rotation, you can press the **Rotate** button on the strategy. - Once triggered, the secrets in step 3.1 should be updated with new rotated credential values. - - ![iam user secret rotations aws iam user](../../../images/platform/secret-rotation/aws-iam/rotations-aws-iam-user.png) + 2. Select the **AWS IAM User Secret** option. + ![Select AWS IAM User Secret](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-option.png) + + 3. Select the **AWS Connection** to use and configure the rotation behavior. Then click **Next**. + ![Rotation Configuration](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-configuration.png) + + - **AWS Connection** - the connection that will perform the rotation of the specified application's Client Secret. + - **Rotation Interval** - the interval, in days, that once elapsed will trigger a rotation. + - **Rotate At** - the local time of day when rotation should occur once the interval has elapsed. + - **Auto-Rotation Enabled** - whether secrets should automatically be rotated once the rotation interval has elapsed. Disable this option to manually rotate secrets or pause secret rotation. + + 4. Select the AWS IAM user and the region of the user whose credentials you want to rotate. Then click **Next**. + ![Rotation Parameters](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-parameters.png) + + 5. Specify the secret names that the client credentials should be mapped to. Then click **Next**. + ![Rotation Secrets Mapping](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-secrets-mapping.png) + + - **Client ID** - the name of the secret that the application Client ID will be mapped to. + - **Client Secret** - the name of the secret that the rotated Client Secret will be mapped to. + + 6. Give your rotation a name and description (optional). Then click **Next**. + ![Rotation Details](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-details.png) + + - **Name** - the name of the secret rotation configuration. Must be slug-friendly. + - **Description** (optional) - a description of this rotation configuration. + + 7. Review your configuration, then click **Create Secret Rotation**. + ![Rotation Review](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-confirm.png) + + 8. Your **AWS IAM User** credentials are now available for use via the mapped secrets. + ![Rotation Created](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-created.png) + + + To create an AWS IAM User Rotation, make an API request to the [Create AWS IAM User Rotation](/api-reference/endpoints/secret-rotations/aws-iam-user-secret/create) API endpoint. + + You will first need the **User Name** of the AWS IAM user you want to rotate the secret for. This can be obtained from the IAM console, on Users tab. + ![Users](/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-user-names.png) + + + ### Sample request + + ```bash Request + curl --request POST \ + --url https://us.infisical.com/api/v2/secret-rotations/aws-iam-user-secret \ + --header 'Content-Type: application/json' \ + --data '{ + "name": "", + "projectId": "", + "description": "", + "connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "environment": "", + "secretPath": "", + "isAutoRotationEnabled": true, + "rotationInterval": 2, + "rotateAtUtc": { + "hours": 11.5, + "minutes": 29.5 + }, + "parameters": { + "clientName": "", + "region": "us-east-1" + }, + "secretsMapping": { + "accessKeyId": "", + "secretAccessKey": "" + } + }' + ``` + + ### Sample response + + ```bash Response + { + "secretRotation": { + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "name": "", + "description": "", + "secretsMapping": { + "accessKeyId": "", + "secretAccessKey": "" + }, + "isAutoRotationEnabled": true, + "activeIndex": 0, + "folderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a", + "createdAt": "2023-11-07T05:31:56Z", + "updatedAt": "2023-11-07T05:31:56Z", + "rotationInterval": 123, + "rotationStatus": "", + "lastRotationAttemptedAt": "2023-11-07T05:31:56Z", + "lastRotatedAt": "2023-11-07T05:31:56Z", + "lastRotationJobId": "", + "nextRotationAt": "2023-11-07T05:31:56Z", + "isLastRotationManual": true, + "connection": { + "app": "aws", + "name": "", + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "environment": { + "slug": "", + "name": "", + "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a" + }, + "projectId": "", + "folder": { + "id": "", + "path": "" + }, + "rotateAtUtc": { + "hours": 11.5, + "minutes": 29.5 + }, + "lastRotationMessage": "", + "type": "aws-iam-user-secret", + "parameters": { + "clientName": "", + "region": "us-east-1" + } + } + } + ``` + + **FAQ** - - There are a few reasons for why this might happen: - - - The strategy configuration is invalid (e.g. the managing IAM user's credentials are incorrect, the target IAM username is incorrect, etc.). - - The managing IAM user is insufficently permissioned to rotate the credentials of the target IAM user. For instance, you may have setup [paths](https://aws.amazon.com/blogs/security/optimize-aws-administration-with-iam-paths/) for the managing IAM user and the policy does not have the necessary permissions to rotate the credentials. - - The target IAM user already has 2 access keys configured in AWS; you should delete one of the access keys to allow for rotation. - - \ No newline at end of file + + There are a few reasons for why this might happen: - The strategy + configuration is invalid (e.g. the managing IAM user's credentials are + incorrect, the target AWS region is incorrect, etc.). - The managing IAM + user is insufficently permissioned to rotate the credentials of the target + IAM user. For instance, you may have setup + [paths](https://aws.amazon.com/blogs/security/optimize-aws-administration-with-iam-paths/) + for the managing IAM user and the policy does not have the necessary + permissions to rotate the credentials. - The target IAM user already has 2 + access keys configured in AWS; you should delete one of the access keys to + allow for rotation. + + diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-configuration.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-configuration.png new file mode 100644 index 0000000000..0e530600bb Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-configuration.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-confirm.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-confirm.png new file mode 100644 index 0000000000..545e8625ae Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-confirm.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-created.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-created.png new file mode 100644 index 0000000000..51f28107e1 Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-created.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-details.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-details.png new file mode 100644 index 0000000000..272c93958d Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-details.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-option.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-option.png new file mode 100644 index 0000000000..92fcc22cd9 Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-option.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-parameters.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-parameters.png new file mode 100644 index 0000000000..1ccfa4d6cd Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-parameters.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-secrets-mapping.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-secrets-mapping.png new file mode 100644 index 0000000000..83d7157ddf Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-secrets-mapping.png differ diff --git a/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-user-names.png b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-user-names.png new file mode 100644 index 0000000000..b8fa47ae3d Binary files /dev/null and b/docs/images/secret-rotations-v2/aws-iam-user-secret/aws-iam-user-secret-user-names.png differ diff --git a/docs/mint.json b/docs/mint.json index ee07fb0482..e5c8f3b3ba 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -180,7 +180,8 @@ "documentation/platform/secret-rotation/overview", "documentation/platform/secret-rotation/auth0-client-secret", "documentation/platform/secret-rotation/postgres-credentials", - "documentation/platform/secret-rotation/mssql-credentials" + "documentation/platform/secret-rotation/mssql-credentials", + "documentation/platform/secret-rotation/aws-iam" ] }, { @@ -861,6 +862,19 @@ "api-reference/endpoints/secret-rotations/auth0-client-secret/update" ] }, + { + "group": "AWS IAM User Secret", + "pages": [ + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/create", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/delete", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-id", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-by-name", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/get-generated-credentials-by-id", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/list", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/rotate-secrets", + "api-reference/endpoints/secret-rotations/aws-iam-user-secret/update" + ] + }, { "group": "Microsoft SQL Server Credentials", "pages": [ diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AwsIamUserSecretRotationParametersFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AwsIamUserSecretRotationParametersFields.tsx new file mode 100644 index 0000000000..d1429dd552 --- /dev/null +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/AwsIamUserSecretRotationParametersFields.tsx @@ -0,0 +1,83 @@ +import { Controller, useFormContext } from "react-hook-form"; +import { SingleValue } from "react-select"; +import { faCircleInfo } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; +import { AwsRegionSelect } from "@app/components/secret-syncs/forms/SecretSyncDestinationFields/shared"; +import { FilterableSelect, FormControl, Tooltip } from "@app/components/v2"; +import { TAwsIamUserSecret, useListAwsConnectionIamUsers } from "@app/hooks/api/appConnections/aws"; +import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; + +export const AwsIamUserSecretRotationParametersFields = () => { + const { control, watch } = useFormContext< + TSecretRotationV2Form & { + type: SecretRotation.AwsIamUserSecret; + } + >(); + + const connectionId = watch("connection.id"); + + const { data: clients, isPending: isClientsPending } = useListAwsConnectionIamUsers({ + connectionId + }); + + return ( + <> + ( + + Ensure that your connection has the correct permissions and the IAM user exists + in the connection's audience. + + } + > +
+ Don't see the IAM user you're looking for?{" "} + +
+ + } + > + client.UserName === value) ?? ""} + onChange={(option) => { + onChange((option as SingleValue)?.UserName ?? ""); + }} + options={clients} + placeholder="Select an IAM user..." + getOptionLabel={(option) => + (option as SingleValue)?.UserName ?? "" + } + getOptionValue={(option) => + (option as SingleValue)?.UserName ?? "" + } + /> +
+ )} + /> + ( + + + + )} + control={control} + name="parameters.region" + /> + + ); +}; diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/SecretRotationV2ParametersFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/SecretRotationV2ParametersFields.tsx index 444510e1ee..e0841d096f 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/SecretRotationV2ParametersFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ParametersFields/SecretRotationV2ParametersFields.tsx @@ -4,12 +4,14 @@ import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; import { TSecretRotationV2Form } from "../schemas"; import { Auth0ClientSecretRotationParametersFields } from "./Auth0ClientSecretRotationParametersFields"; +import { AwsIamUserSecretRotationParametersFields } from "./AwsIamUserSecretRotationParametersFields"; import { SqlCredentialsRotationParametersFields } from "./shared"; const COMPONENT_MAP: Record = { [SecretRotation.PostgresCredentials]: SqlCredentialsRotationParametersFields, [SecretRotation.MsSqlCredentials]: SqlCredentialsRotationParametersFields, - [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationParametersFields + [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationParametersFields, + [SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationParametersFields }; export const SecretRotationV2ParametersFields = () => { diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/AwsIamUserSecretRotationReviewFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/AwsIamUserSecretRotationReviewFields.tsx new file mode 100644 index 0000000000..c685691dcd --- /dev/null +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/AwsIamUserSecretRotationReviewFields.tsx @@ -0,0 +1,30 @@ +import { useFormContext } from "react-hook-form"; + +import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; +import { GenericFieldLabel } from "@app/components/v2"; +import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; + +import { SecretRotationReviewSection } from "./shared"; + +export const AwsIamUserSecretRotationReviewFields = () => { + const { watch } = useFormContext< + TSecretRotationV2Form & { + type: SecretRotation.AwsIamUserSecret; + } + >(); + + const [parameters, { accessKeyId, secretAccessKey }] = watch(["parameters", "secretsMapping"]); + + return ( + <> + + {parameters.region} + {parameters.clientName} + + + {accessKeyId} + {secretAccessKey} + + + ); +}; diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx index 4fb3b6d243..d8624e308e 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx @@ -7,12 +7,14 @@ import { getRotateAtLocal } from "@app/helpers/secretRotationsV2"; import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; import { Auth0ClientSecretRotationReviewFields } from "./Auth0ClientSecretRotationReviewFields"; +import { AwsIamUserSecretRotationReviewFields } from "./AwsIamUserSecretRotationReviewFields"; import { SqlCredentialsRotationReviewFields } from "./shared"; const COMPONENT_MAP: Record = { [SecretRotation.PostgresCredentials]: SqlCredentialsRotationReviewFields, [SecretRotation.MsSqlCredentials]: SqlCredentialsRotationReviewFields, - [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationReviewFields + [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationReviewFields, + [SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationReviewFields }; export const SecretRotationV2ReviewFields = () => { diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AwsIamUserSecretRotationSecretsMappingFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AwsIamUserSecretRotationSecretsMappingFields.tsx new file mode 100644 index 0000000000..f7b32494de --- /dev/null +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/AwsIamUserSecretRotationSecretsMappingFields.tsx @@ -0,0 +1,58 @@ +import { Controller, useFormContext } from "react-hook-form"; + +import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas"; +import { FormControl, Input } from "@app/components/v2"; +import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secretRotationsV2"; + +import { SecretsMappingTable } from "./shared"; + +export const AwsIamUserSecretRotationSecretsMappingFields = () => { + const { control } = useFormContext< + TSecretRotationV2Form & { + type: SecretRotation.AwsIamUserSecret; + } + >(); + + const { rotationOption } = useSecretRotationV2Option(SecretRotation.AwsIamUserSecret); + + const items = [ + { + name: "Access Key ID", + input: ( + ( + + + + )} + control={control} + name="secretsMapping.accessKeyId" + /> + ) + }, + { + name: "Client Secret", + input: ( + ( + + + + )} + control={control} + name="secretsMapping.secretAccessKey" + /> + ) + } + ]; + + return ; +}; diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx index 58277d5932..6ede945db0 100644 --- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx +++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx @@ -4,12 +4,14 @@ import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; import { TSecretRotationV2Form } from "../schemas"; import { Auth0ClientSecretRotationSecretsMappingFields } from "./Auth0ClientSecretRotationSecretsMappingFields"; +import { AwsIamUserSecretRotationSecretsMappingFields } from "./AwsIamUserSecretRotationSecretsMappingFields"; import { SqlCredentialsRotationSecretsMappingFields } from "./shared"; const COMPONENT_MAP: Record = { [SecretRotation.PostgresCredentials]: SqlCredentialsRotationSecretsMappingFields, [SecretRotation.MsSqlCredentials]: SqlCredentialsRotationSecretsMappingFields, - [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationSecretsMappingFields + [SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationSecretsMappingFields, + [SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationSecretsMappingFields }; export const SecretRotationV2SecretsMappingFields = () => { diff --git a/frontend/src/components/secret-rotations-v2/forms/schemas/aws-iam-user-secret-rotation-schema.ts b/frontend/src/components/secret-rotations-v2/forms/schemas/aws-iam-user-secret-rotation-schema.ts new file mode 100644 index 0000000000..799ec22a5b --- /dev/null +++ b/frontend/src/components/secret-rotations-v2/forms/schemas/aws-iam-user-secret-rotation-schema.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; + +import { BaseSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/base-secret-rotation-v2-schema"; +import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; + +export const AwsIamUserSecretRotationSchema = z + .object({ + type: z.literal(SecretRotation.AwsIamUserSecret), + parameters: z.object({ + clientName: z.string().trim().min(1, "Client Name required"), + region: z.string().trim().min(1, "Region required") + }), + secretsMapping: z.object({ + accessKeyId: z.string().trim().min(1, "Access Key ID required"), + secretAccessKey: z.string().trim().min(1, "Secret Access Key required") + }) + }) + .merge(BaseSecretRotationSchema); diff --git a/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts b/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts index 295e199fe3..acd56e5719 100644 --- a/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts +++ b/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts @@ -1,13 +1,15 @@ import { z } from "zod"; import { Auth0ClientSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/auth0-client-secret-rotation-schema"; +import { AwsIamUserSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/aws-iam-user-secret-rotation-schema"; import { MsSqlCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/mssql-credentials-rotation-schema"; import { PostgresCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/postgres-credentials-rotation-schema"; const SecretRotationUnionSchema = z.discriminatedUnion("type", [ PostgresCredentialsRotationSchema, MsSqlCredentialsRotationSchema, - Auth0ClientSecretRotationSchema + Auth0ClientSecretRotationSchema, + AwsIamUserSecretRotationSchema ]); export const SecretRotationV2FormSchema = SecretRotationUnionSchema; diff --git a/frontend/src/helpers/secretRotationsV2.ts b/frontend/src/helpers/secretRotationsV2.ts index 1a57d37cdb..ce026387c5 100644 --- a/frontend/src/helpers/secretRotationsV2.ts +++ b/frontend/src/helpers/secretRotationsV2.ts @@ -19,20 +19,27 @@ export const SECRET_ROTATION_MAP: Record< name: "Auth0 Client Secret", image: "Auth0.png", size: 35 + }, + [SecretRotation.AwsIamUserSecret]: { + name: "AWS IAM User Secret", + image: "Amazon Web Services.png", + size: 35 } }; export const SECRET_ROTATION_CONNECTION_MAP: Record = { [SecretRotation.PostgresCredentials]: AppConnection.Postgres, [SecretRotation.MsSqlCredentials]: AppConnection.MsSql, - [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0 + [SecretRotation.Auth0ClientSecret]: AppConnection.Auth0, + [SecretRotation.AwsIamUserSecret]: AppConnection.AWS }; // if a rotation can potentially have downtime due to rotating a single credential set this to false export const IS_ROTATION_DUAL_CREDENTIALS: Record = { [SecretRotation.PostgresCredentials]: true, [SecretRotation.MsSqlCredentials]: true, - [SecretRotation.Auth0ClientSecret]: false + [SecretRotation.Auth0ClientSecret]: false, + [SecretRotation.AwsIamUserSecret]: true }; export const getRotateAtLocal = ({ hours, minutes }: TSecretRotationV2["rotateAtUtc"]) => { diff --git a/frontend/src/hooks/api/appConnections/aws/queries.tsx b/frontend/src/hooks/api/appConnections/aws/queries.tsx index 87965507bb..895ed35ee2 100644 --- a/frontend/src/hooks/api/appConnections/aws/queries.tsx +++ b/frontend/src/hooks/api/appConnections/aws/queries.tsx @@ -4,15 +4,20 @@ import { apiRequest } from "@app/config/request"; import { appConnectionKeys } from "@app/hooks/api/appConnections"; import { + TAwsConnectionIamUser, TAwsConnectionKmsKey, + TAwsConnectionListIamUsersResponse, TAwsConnectionListKmsKeysResponse, + TListAwsConnectionIamUsers, TListAwsConnectionKmsKeys } from "./types"; const awsConnectionKeys = { all: [...appConnectionKeys.all, "aws"] as const, listKmsKeys: (params: TListAwsConnectionKmsKeys) => - [...awsConnectionKeys.all, "kms-keys", params] as const + [...awsConnectionKeys.all, "kms-keys", params] as const, + listIamUsers: (params: TListAwsConnectionIamUsers) => + [...awsConnectionKeys.all, "iam-users", params] as const }; export const useListAwsConnectionKmsKeys = ( @@ -40,3 +45,28 @@ export const useListAwsConnectionKmsKeys = ( ...options }); }; + +export const useListAwsConnectionIamUsers = ( + { connectionId }: TListAwsConnectionIamUsers, + options?: Omit< + UseQueryOptions< + TAwsConnectionIamUser[], + unknown, + TAwsConnectionIamUser[], + ReturnType + >, + "queryKey" | "queryFn" + > +) => { + return useQuery({ + queryKey: awsConnectionKeys.listIamUsers({ connectionId }), + queryFn: async () => { + const { data } = await apiRequest.get( + `/api/v1/app-connections/aws/${connectionId}/users` + ); + + return data.iamUsers; + }, + ...options + }); +}; diff --git a/frontend/src/hooks/api/appConnections/aws/types.ts b/frontend/src/hooks/api/appConnections/aws/types.ts index 7661b131d1..8509344eaf 100644 --- a/frontend/src/hooks/api/appConnections/aws/types.ts +++ b/frontend/src/hooks/api/appConnections/aws/types.ts @@ -14,3 +14,21 @@ export type TAwsConnectionKmsKey = { export type TAwsConnectionListKmsKeysResponse = { kmsKeys: TAwsConnectionKmsKey[]; }; + +export type TListAwsConnectionIamUsers = { + connectionId: string; +}; + +export type TAwsConnectionIamUser = { + arn: string; + UserName: string; +}; + +export type TAwsConnectionListIamUsersResponse = { + iamUsers: TAwsConnectionIamUser[]; +}; + +export type TAwsIamUserSecret = { + arn: string; + UserName: string; +}; diff --git a/frontend/src/hooks/api/secretRotationsV2/enums.ts b/frontend/src/hooks/api/secretRotationsV2/enums.ts index d43cacb3a8..1e387a3b91 100644 --- a/frontend/src/hooks/api/secretRotationsV2/enums.ts +++ b/frontend/src/hooks/api/secretRotationsV2/enums.ts @@ -1,7 +1,8 @@ export enum SecretRotation { PostgresCredentials = "postgres-credentials", MsSqlCredentials = "mssql-credentials", - Auth0ClientSecret = "auth0-client-secret" + Auth0ClientSecret = "auth0-client-secret", + AwsIamUserSecret = "aws-iam-user-secret" } export enum SecretRotationStatus { diff --git a/frontend/src/hooks/api/secretRotationsV2/types/aws-iam-access-key-rotation.ts b/frontend/src/hooks/api/secretRotationsV2/types/aws-iam-access-key-rotation.ts new file mode 100644 index 0000000000..70d6a74e0c --- /dev/null +++ b/frontend/src/hooks/api/secretRotationsV2/types/aws-iam-access-key-rotation.ts @@ -0,0 +1,38 @@ +import { AppConnection } from "@app/hooks/api/appConnections/enums"; +import { SecretRotation } from "@app/hooks/api/secretRotationsV2"; +import { + TSecretRotationV2Base, + TSecretRotationV2GeneratedCredentialsResponseBase +} from "@app/hooks/api/secretRotationsV2/types/shared"; + +export type TAwsIamUserSecretRotation = TSecretRotationV2Base & { + type: SecretRotation.AwsIamUserSecret; + parameters: { + region: string; + clientName: string; + }; + secretsMapping: { + accessKeyId: string; + secretAccessKey: string; + }; +}; + +export type TAwsIamUserSecretRotationGeneratedCredentials = { + accessKeyId: string; + secretAccessKey: string; +}; + +export type TAwsIamUserSecretRotationGeneratedCredentialsResponse = + TSecretRotationV2GeneratedCredentialsResponseBase< + SecretRotation.AwsIamUserSecret, + TAwsIamUserSecretRotationGeneratedCredentials + >; + +export type TAwsIamUserSecretRotationOption = { + name: string; + type: SecretRotation.AwsIamUserSecret; + connection: AppConnection.AWS; + template: { + secretsMapping: TAwsIamUserSecretRotation["secretsMapping"]; + }; +}; diff --git a/frontend/src/hooks/api/secretRotationsV2/types/index.ts b/frontend/src/hooks/api/secretRotationsV2/types/index.ts index 96d568d746..af860e40e8 100644 --- a/frontend/src/hooks/api/secretRotationsV2/types/index.ts +++ b/frontend/src/hooks/api/secretRotationsV2/types/index.ts @@ -4,6 +4,11 @@ import { TAuth0ClientSecretRotationGeneratedCredentialsResponse, TAuth0ClientSecretRotationOption } from "@app/hooks/api/secretRotationsV2/types/auth0-client-secret-rotation"; +import { + TAwsIamUserSecretRotation, + TAwsIamUserSecretRotationGeneratedCredentialsResponse, + TAwsIamUserSecretRotationOption +} from "@app/hooks/api/secretRotationsV2/types/aws-iam-access-key-rotation"; import { TMsSqlCredentialsRotation, TMsSqlCredentialsRotationGeneratedCredentialsResponse @@ -20,13 +25,15 @@ export type TSecretRotationV2 = ( | TPostgresCredentialsRotation | TMsSqlCredentialsRotation | TAuth0ClientSecretRotation + | TAwsIamUserSecretRotation ) & { secrets: (SecretV3RawSanitized | null)[]; }; export type TSecretRotationV2Option = | TSqlCredentialsRotationOption - | TAuth0ClientSecretRotationOption; + | TAuth0ClientSecretRotationOption + | TAwsIamUserSecretRotationOption; export type TListSecretRotationV2Options = { secretRotationOptions: TSecretRotationV2Option[] }; @@ -35,7 +42,8 @@ export type TSecretRotationV2Response = { secretRotation: TSecretRotationV2 }; export type TViewSecretRotationGeneratedCredentialsResponse = | TPostgresCredentialsRotationGeneratedCredentialsResponse | TMsSqlCredentialsRotationGeneratedCredentialsResponse - | TAuth0ClientSecretRotationGeneratedCredentialsResponse; + | TAuth0ClientSecretRotationGeneratedCredentialsResponse + | TAwsIamUserSecretRotationGeneratedCredentialsResponse; export type TCreateSecretRotationV2DTO = DiscriminativePick< TSecretRotationV2, @@ -82,10 +90,12 @@ export type TSecretRotationOptionMap = { [SecretRotation.PostgresCredentials]: TSqlCredentialsRotationOption; [SecretRotation.MsSqlCredentials]: TSqlCredentialsRotationOption; [SecretRotation.Auth0ClientSecret]: TAuth0ClientSecretRotationOption; + [SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationOption; }; export type TSecretRotationGeneratedCredentialsResponseMap = { [SecretRotation.PostgresCredentials]: TPostgresCredentialsRotationGeneratedCredentialsResponse; [SecretRotation.MsSqlCredentials]: TMsSqlCredentialsRotationGeneratedCredentialsResponse; [SecretRotation.Auth0ClientSecret]: TAuth0ClientSecretRotationGeneratedCredentialsResponse; + [SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationGeneratedCredentialsResponse; };