mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-29 22:02:57 +00:00
Merge pull request #2639 from Infisical/feat/add-assume-role-support-for-aws-parameter-store
feat: add assume role support for aws parameter store
This commit is contained in:
@ -371,7 +371,8 @@ export const integrationAuthServiceFactory = ({
|
||||
let accessId: string | undefined;
|
||||
// this means its not access token based
|
||||
if (
|
||||
integrationAuth.integration === Integrations.AWS_SECRET_MANAGER &&
|
||||
(integrationAuth.integration === Integrations.AWS_SECRET_MANAGER ||
|
||||
integrationAuth.integration === Integrations.AWS_PARAMETER_STORE) &&
|
||||
(shouldUseSecretV2Bridge
|
||||
? integrationAuth.encryptedAwsAssumeIamRoleArn
|
||||
: integrationAuth.awsAssumeIamRoleArnCipherText)
|
||||
|
@ -555,24 +555,63 @@ const syncSecretsAWSParameterStore = async ({
|
||||
secrets,
|
||||
accessId,
|
||||
accessToken,
|
||||
projectId
|
||||
projectId,
|
||||
awsAssumeRoleArn
|
||||
}: {
|
||||
integration: TIntegrations & { secretPath: string; environment: { slug: string } };
|
||||
secrets: Record<string, { value: string; comment?: string }>;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
awsAssumeRoleArn: string | null;
|
||||
projectId?: string;
|
||||
}) => {
|
||||
const appCfg = getConfig();
|
||||
let response: { isSynced: boolean; syncMessage: string } | null = null;
|
||||
|
||||
if (!accessId) {
|
||||
throw new Error("AWS access ID is required");
|
||||
if (!accessId && !awsAssumeRoleArn) {
|
||||
throw new Error("AWS access ID/AWS Assume Role is required");
|
||||
}
|
||||
|
||||
let accessKeyId = "";
|
||||
let secretAccessKey = "";
|
||||
let sessionToken;
|
||||
if (awsAssumeRoleArn) {
|
||||
const client = new STSClient({
|
||||
region: integration.region as string,
|
||||
credentials:
|
||||
appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION
|
||||
? {
|
||||
accessKeyId: appCfg.CLIENT_ID_AWS_INTEGRATION,
|
||||
secretAccessKey: appCfg.CLIENT_SECRET_AWS_INTEGRATION
|
||||
}
|
||||
: undefined
|
||||
});
|
||||
const command = new AssumeRoleCommand({
|
||||
RoleArn: awsAssumeRoleArn,
|
||||
RoleSessionName: `infisical-parameter-store-${randomUUID()}`,
|
||||
DurationSeconds: 900, // 15mins
|
||||
ExternalId: projectId
|
||||
});
|
||||
const assumeRes = await client.send(command);
|
||||
|
||||
if (!assumeRes.Credentials?.AccessKeyId || !assumeRes.Credentials?.SecretAccessKey) {
|
||||
throw new Error("Failed to assume role");
|
||||
}
|
||||
|
||||
accessKeyId = assumeRes.Credentials?.AccessKeyId;
|
||||
secretAccessKey = assumeRes.Credentials?.SecretAccessKey;
|
||||
sessionToken = assumeRes.Credentials?.SessionToken;
|
||||
} else {
|
||||
accessKeyId = accessId as string;
|
||||
secretAccessKey = accessToken;
|
||||
}
|
||||
|
||||
const config = new AWS.Config({
|
||||
region: integration.region as string,
|
||||
credentials: {
|
||||
accessKeyId: accessId,
|
||||
secretAccessKey: accessToken
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
sessionToken
|
||||
}
|
||||
});
|
||||
|
||||
@ -4047,6 +4086,7 @@ export const syncIntegrationSecrets = async ({
|
||||
secrets,
|
||||
accessId,
|
||||
accessToken,
|
||||
awsAssumeRoleArn,
|
||||
projectId
|
||||
});
|
||||
break;
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 523 KiB |
Binary file not shown.
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 528 KiB |
@ -3,76 +3,197 @@ title: "AWS Parameter Store"
|
||||
description: "Learn how to sync secrets from Infisical to AWS Parameter Store."
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
<Tabs>
|
||||
<Tab title="Assume Role (Recommended)">
|
||||
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- Set up AWS and have/create an IAM user
|
||||
Prerequisites:
|
||||
|
||||
<Steps>
|
||||
<Step title="Grant the IAM user permissions to access AWS Parameter Store">
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Parameter Store.
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||

|
||||

|
||||

|
||||
<Accordion title="Self-Hosted Users">
|
||||
To connect your Infisical instance with AWS, you need to set up an AWS IAM User account that can assume the AWS IAM Role for the integration.
|
||||
|
||||
For enhanced security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Parameter Store for the IAM user that you can use:
|
||||
If your instance is deployed on AWS, the aws-sdk will automatically retrieve the credentials. Ensure that you assign the provided permission policy to your deployed instance, such as ECS or EC2.
|
||||
|
||||
The following steps are for instances not deployed on AWS
|
||||
<Steps>
|
||||
<Step title="Create an IAM User">
|
||||
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
|
||||
</Step>
|
||||
<Step title="Create an Inline Policy">
|
||||
Attach the following inline permission policy to the IAM User to allow it to assume any IAM Roles:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowSSMAccess",
|
||||
"Sid": "AllowAssumeAnyRole",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ssm:PutParameter",
|
||||
"ssm:DeleteParameter",
|
||||
"ssm:GetParameters",
|
||||
"ssm:GetParametersByPath",
|
||||
"ssm:DescribeParameters",
|
||||
"ssm:DeleteParameters",
|
||||
"ssm:AddTagsToResource", // if you need to add tags to secrets
|
||||
"kms:ListKeys", // if you need to specify the KMS key
|
||||
"kms:ListAliases", // if you need to specify the KMS key
|
||||
"kms:Encrypt", // if you need to specify the KMS key
|
||||
"kms:Decrypt" // if you need to specify the KMS key
|
||||
],
|
||||
"Resource": "*"
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "arn:aws:iam::*:role/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
<Step title="Obtain the IAM User Credentials">
|
||||
Obtain the AWS access key ID and secret access key for your IAM User by navigating to IAM > Users > [Your User] > Security credentials > Access keys.
|
||||
|
||||
</Step>
|
||||
<Step title="Authorize Infisical for AWS Parameter store">
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||

|
||||

|
||||

|
||||
</Step>
|
||||
<Step title="Set Up Integration Keys">
|
||||
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
|
||||
2. Set the secret key as **CLIENT_SECRET_AWS_INTEGRATION**.
|
||||
</Step>
|
||||
</Steps>
|
||||
</Accordion>
|
||||
|
||||

|
||||

|
||||

|
||||
<Steps>
|
||||
<Step title="Create the Managing User IAM Role for AWS Parameter Store">
|
||||
1. Navigate to the [Create IAM Role](https://console.aws.amazon.com/iamv2/home#/roles/create?step=selectEntities) page in your AWS Console.
|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
2. Select **AWS Account** as the **Trusted Entity Type**.
|
||||
3. Choose **Another AWS Account** and enter **381492033652** (Infisical AWS Account ID). This restricts the role to be assumed only by Infisical. If self-hosting, provide your AWS account number instead.
|
||||
4. Optionally, enable **Require external ID** and enter your **project ID** to further enhance security.
|
||||
</Step>
|
||||
|
||||

|
||||
<Step title="Add Required Permissions for the IAM Role">
|
||||

|
||||
Use the following custom policy to grant the minimum permissions required by Infisical to sync secrets to AWS Parameter Store:
|
||||
|
||||
Press on the AWS Parameter Store tile and input your AWS access key ID and secret access key from the previous step.
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowSSMAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ssm:PutParameter",
|
||||
"ssm:DeleteParameter",
|
||||
"ssm:GetParameters",
|
||||
"ssm:GetParametersByPath",
|
||||
"ssm:DescribeParameters",
|
||||
"ssm:DeleteParameters",
|
||||
"ssm:AddTagsToResource", // if you need to add tags to secrets
|
||||
"kms:ListKeys", // if you need to specify the KMS key
|
||||
"kms:ListAliases", // if you need to specify the KMS key
|
||||
"kms:Encrypt", // if you need to specify the KMS key
|
||||
"kms:Decrypt" // if you need to specify the KMS key
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||

|
||||
<Step title="Copy the AWS IAM Role ARN">
|
||||

|
||||
</Step>
|
||||
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
|
||||
<Step title="Authorize Infisical for AWS Parameter Store">
|
||||
1. Navigate to your project's integrations tab in Infisical.
|
||||
2. Click on the **AWS Parameter Store** tile.
|
||||

|
||||
|
||||

|
||||
3. Select the **AWS Assume Role** option.
|
||||

|
||||
|
||||
<Tip>
|
||||
Infisical requires you to add a path for your secrets to be stored in AWS
|
||||
Parameter Store and recommends setting the path structure to
|
||||
`/[project_name]/[environment]/` according to best practices. This enables a
|
||||
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
|
||||
Parameter Store.
|
||||
</Tip>
|
||||
4. Provide the **AWS IAM Role ARN** obtained from the previous step and press connect.
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||

|
||||
|
||||
<Tip>
|
||||
Infisical requires you to add a path for your secrets to be stored in AWS
|
||||
Parameter Store and recommends setting the path structure to
|
||||
`/[project_name]/[environment]/` according to best practices. This enables a
|
||||
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
|
||||
Parameter Store.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
<Tab title="Access Key">
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
<Steps>
|
||||
<Step title="Grant the IAM user permissions to access AWS Parameter Store">
|
||||
Navigate to your IAM user permissions and add a permission policy to grant access to AWS Parameter Store.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
For enhanced security, here's a custom policy containing the minimum permissions required by Infisical to sync secrets to AWS Parameter Store for the IAM user that you can use:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowSSMAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ssm:PutParameter",
|
||||
"ssm:DeleteParameter",
|
||||
"ssm:GetParameters",
|
||||
"ssm:GetParametersByPath",
|
||||
"ssm:DescribeParameters",
|
||||
"ssm:DeleteParameters",
|
||||
"ssm:AddTagsToResource", // if you need to add tags to secrets
|
||||
"kms:ListKeys", // if you need to specify the KMS key
|
||||
"kms:ListAliases", // if you need to specify the KMS key
|
||||
"kms:Encrypt", // if you need to specify the KMS key
|
||||
"kms:Decrypt" // if you need to specify the KMS key
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Authorize Infisical for AWS Parameter store">
|
||||
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||
|
||||
Press on the AWS Parameter Store tile and select Access Key as the authentication mode. Input your AWS access key ID and secret access key from the previous step.
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
|
||||
|
||||

|
||||
|
||||
<Tip>
|
||||
Infisical requires you to add a path for your secrets to be stored in AWS
|
||||
Parameter Store and recommends setting the path structure to
|
||||
`/[project_name]/[environment]/` according to best practices. This enables a
|
||||
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
|
||||
Parameter Store.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
@ -1,54 +1,73 @@
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useSaveIntegrationAccessToken } from "@app/hooks/api";
|
||||
|
||||
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardTitle,
|
||||
FormControl,
|
||||
Input,
|
||||
Select,
|
||||
SelectItem
|
||||
} from "../../../components/v2";
|
||||
|
||||
enum AwsAuthType {
|
||||
AccessKey = "access-key",
|
||||
AssumeRole = "assume-role"
|
||||
}
|
||||
|
||||
const formSchema = z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
type: z.literal(AwsAuthType.AccessKey),
|
||||
accessKey: z.string().min(1),
|
||||
accessSecretKey: z.string().min(1)
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(AwsAuthType.AssumeRole),
|
||||
iamRoleArn: z.string().min(1)
|
||||
})
|
||||
]);
|
||||
|
||||
type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
export default function AWSParameterStoreAuthorizeIntegrationPage() {
|
||||
const router = useRouter();
|
||||
const { mutateAsync } = useSaveIntegrationAccessToken();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { control, handleSubmit, formState, watch } = useForm<TForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
type: AwsAuthType.AccessKey
|
||||
}
|
||||
});
|
||||
|
||||
const [accessKey, setAccessKey] = useState("");
|
||||
const [accessKeyErrorText, setAccessKeyErrorText] = useState("");
|
||||
const [accessSecretKey, setAccessSecretKey] = useState("");
|
||||
const [accessSecretKeyErrorText, setAccessSecretKeyErrorText] = useState("");
|
||||
const formAwsAuthTypeField = watch("type");
|
||||
|
||||
const handleButtonClick = async () => {
|
||||
const handleFormSubmit = async (data: TForm) => {
|
||||
try {
|
||||
setAccessKeyErrorText("");
|
||||
setAccessSecretKeyErrorText("");
|
||||
|
||||
if (accessKey.length === 0) {
|
||||
setAccessKeyErrorText("Access key cannot be blank");
|
||||
return;
|
||||
}
|
||||
|
||||
if (accessSecretKey.length === 0) {
|
||||
setAccessSecretKeyErrorText("Secret access key cannot be blank");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const integrationAuth = await mutateAsync({
|
||||
workspaceId: localStorage.getItem("projectData.id"),
|
||||
integration: "aws-parameter-store",
|
||||
accessId: accessKey,
|
||||
accessToken: accessSecretKey
|
||||
...(data.type === AwsAuthType.AssumeRole
|
||||
? {
|
||||
awsAssumeIamRoleArn: data.iamRoleArn
|
||||
}
|
||||
: {
|
||||
accessId: data.accessKey,
|
||||
accessToken: data.accessSecretKey
|
||||
})
|
||||
});
|
||||
|
||||
setAccessKey("");
|
||||
setAccessSecretKey("");
|
||||
setIsLoading(false);
|
||||
|
||||
router.push(
|
||||
`/integrations/aws-parameter-store/create?integrationAuthId=${integrationAuth.id}`
|
||||
);
|
||||
@ -92,37 +111,90 @@ export default function AWSParameterStoreAuthorizeIntegrationPage() {
|
||||
</Link>
|
||||
</div>
|
||||
</CardTitle>
|
||||
<FormControl
|
||||
label="Access Key ID"
|
||||
errorText={accessKeyErrorText}
|
||||
isError={accessKeyErrorText !== "" ?? false}
|
||||
className="px-6"
|
||||
>
|
||||
<Input placeholder="" value={accessKey} onChange={(e) => setAccessKey(e.target.value)} />
|
||||
</FormControl>
|
||||
<FormControl
|
||||
label="Secret Access Key"
|
||||
errorText={accessSecretKeyErrorText}
|
||||
isError={accessSecretKeyErrorText !== "" ?? false}
|
||||
className="px-6"
|
||||
>
|
||||
<Input
|
||||
placeholder=""
|
||||
value={accessSecretKey}
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
onChange={(e) => setAccessSecretKey(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
onClick={handleButtonClick}
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
className="mb-6 mt-2 ml-auto mr-6 w-min"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Connect to AWS Parameter Store
|
||||
</Button>
|
||||
<CardBody>
|
||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="type"
|
||||
defaultValue={AwsAuthType.AccessKey}
|
||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Authentication Mode"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Select
|
||||
defaultValue={field.value}
|
||||
{...field}
|
||||
onValueChange={(e) => onChange(e)}
|
||||
className="w-full"
|
||||
>
|
||||
<SelectItem value={AwsAuthType.AccessKey}>Access Key</SelectItem>
|
||||
<SelectItem value={AwsAuthType.AssumeRole}>AWS Assume Role</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{formAwsAuthTypeField === AwsAuthType.AccessKey ? (
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name="accessKey"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Access Key ID"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input placeholder="" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="accessSecretKey"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Secret Access Key"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
placeholder=""
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Controller
|
||||
control={control}
|
||||
name="iamRoleArn"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="IAM Role ARN For Role Assumption"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input placeholder="" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="submit"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
className="mb-6 mt-2 ml-auto mr-6 w-min"
|
||||
isLoading={formState.isSubmitting}
|
||||
>
|
||||
Connect to AWS Parameter Store
|
||||
</Button>
|
||||
</form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
Reference in New Issue
Block a user