Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added kms key selector for parameter store #1754

Merged
merged 4 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -572,14 +572,14 @@ export const integrationAuthServiceFactory = ({
const response = keys
.Keys!.map((key) => {
const keyAlias = aliases.Aliases!.find((alias) => key.KeyId === alias.TargetKeyId);
if (!keyAlias?.AliasName?.includes("alias/aws/") || keyAlias?.AliasName?.includes("alias/aws/secretsmanager")) {
if (!keyAlias?.AliasName?.includes("alias/aws/")) {
return { id: String(key.KeyId), alias: String(keyAlias?.AliasName || key.KeyId) };
}
return { id: "null", alias: "null" };
})
.filter((elem) => elem.id !== "null");

return response;
return [...response, { id: "null", alias: "default" }];
};

const getQoveryProjects = async ({
Expand Down
29 changes: 17 additions & 12 deletions backend/src/services/integration-auth/integration-sync-secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,24 +477,29 @@ const syncSecretsAWSParameterStore = async ({
}),
{} as Record<string, AWS.SSM.Parameter>
);

// Identify secrets to create
await Promise.all(
Object.keys(secrets).map(async (key) => {
if (!(key in awsParameterStoreSecretsObj)) {
// case: secret does not exist in AWS parameter store
// -> create secret
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
// Overwrite: true,
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value }))
: []
})
.promise();
if (secrets[key].value) {
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
KeyId: metadata.kmsKeyId ? metadata.kmsKeyId : undefined,
// Overwrite: true,
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
Key: tag.key,
Value: tag.value
}))
: []
})
.promise();
}
// case: secret exists in AWS parameter store
} else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
// case: secret value doesn't match one in AWS parameter store
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 9 additions & 3 deletions docs/integrations/cloud/aws-parameter-store.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,26 @@ Prerequisites:
"ssm:DeleteParameter",
"ssm:GetParametersByPath",
"ssm:DeleteParameters",
"ssm:AddTagsToResource" // if you need to add tags to secrets
"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

![access key 1](../../images/integrations/aws/integrations-aws-access-key-1.png)
![access key 2](../../images/integrations/aws/integrations-aws-access-key-2.png)
![access key 3](../../images/integrations/aws/integrations-aws-access-key-3.png)

Navigate to your project's integrations tab in Infisical.

![integrations](../../images/integrations.png)
Expand All @@ -59,6 +64,7 @@ Prerequisites:
breaks E2EE, it's necessary for Infisical to sync the environment variables to
the cloud platform.
</Info>

</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.
Expand All @@ -72,6 +78,6 @@ Prerequisites:
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
Parameter Store.
</Tip>

</Step>
</Steps>

Binary file added frontend/public/images/integrations/Agent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/images/integrations/ECS.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/images/integrations/Jenkins.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 0 additions & 24 deletions frontend/public/json/frameworkIntegrations.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,4 @@
[
{
"name": "Docker",
"slug": "docker",
"image": "Docker",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker"
},
{
"name": "Docker Compose",
"slug": "docker-compose",
"image": "Docker Compose",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
},
{
"name": "Kubernetes",
"slug": "kubernetes",
"image": "Kubernetes",
"docsLink": "https://infisical.com/docs/integrations/platforms/kubernetes"
},
{
"name": "Terraform",
"slug": "terraform",
"image": "Terraform",
"docsLink": "https://infisical.com/docs/integrations/frameworks/terraform"
},
{
"name": "React",
"slug": "react",
Expand Down
50 changes: 50 additions & 0 deletions frontend/public/json/infrastructureIntegrations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"name": "Docker",
"slug": "docker",
"image": "Docker",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker"
},
{
"name": "Docker Compose",
"slug": "docker-compose",
"image": "Docker Compose",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
},
{
"name": "Kubernetes",
"slug": "kubernetes",
"image": "Kubernetes",
"docsLink": "https://infisical.com/docs/integrations/platforms/kubernetes"
},
{
"name": "Terraform",
"slug": "terraform",
"image": "Terraform",
"docsLink": "https://infisical.com/docs/integrations/frameworks/terraform"
},
{
"name": "Jenkins",
"slug": "jenkins",
"image": "Jenkins",
"docsLink": "https://infisical.com/docs/integrations/cicd/jenkins"
},
{
"name": "Infisical Agent",
"slug": "agent",
"image": "Agent",
"docsLink": "https://infisical.com/docs/integrations/platforms/infisical-agent"
},
{
"name": "Amazon ECS",
"slug": "ecs",
"image": "ECS",
"docsLink": "https://infisical.com/docs/integrations/platforms/ecs-with-agent"
},
{
"name": "Ansible",
"slug": "ansible",
"image": "Ansible",
"docsLink": "https://infisical.com/docs/integrations/platforms/ansible"
}
]
2 changes: 1 addition & 1 deletion frontend/public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"available": "Platform & Cloud Integrations",
"available-text1": "Click on the integration you want to connect. This will let your environment variables flow automatically into selected third-party services.",
"available-text2": "Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. The core Infisical service will always stay end-to-end encrypted. With any questions, reach out [email protected].",
"cloud-integrations": "Cloud Integrations",
"cloud-integrations": "Native Integrations",
"framework-integrations": "Framework Integrations",
"click-to-start": "Click on an integration to begin syncing secrets to it.",
"click-to-setup": "Click on a framework to get the setup instructions.",
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/pages/integrations/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import frameworkIntegrationOptions from "public/json/frameworkIntegrations.json";
import infrastructureIntegrationOptions from "public/json/infrastructureIntegrations.json";

import { IntegrationsPage } from "@app/views/IntegrationsPage";

type Props = {
frameworkIntegrations: typeof frameworkIntegrationOptions;
infrastructureIntegrations: typeof infrastructureIntegrationOptions;
};

const Integration = ({ frameworkIntegrations }: Props) => {
const Integration = ({ frameworkIntegrations, infrastructureIntegrations }: Props) => {
const { t } = useTranslation();

return (
Expand All @@ -20,15 +22,16 @@ const Integration = ({ frameworkIntegrations }: Props) => {
<meta property="og:title" content="Manage your .env files in seconds" />
<meta name="og:description" content={t("integrations.description") as string} />
</Head>
<IntegrationsPage frameworkIntegrations={frameworkIntegrations} />
<IntegrationsPage frameworkIntegrations={frameworkIntegrations} infrastructureIntegrations={infrastructureIntegrations} />
</>
);
};

export const getStaticProps = () => {
return {
props: {
frameworkIntegrations: frameworkIntegrationOptions
frameworkIntegrations: frameworkIntegrationOptions,
infrastructureIntegrations: infrastructureIntegrationOptions
}
};
};
Expand Down
50 changes: 47 additions & 3 deletions frontend/src/pages/integrations/aws-parameter-store/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { motion } from "framer-motion";
import queryString from "query-string";

import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";

import {
Button,
Expand Down Expand Up @@ -90,6 +91,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [shouldTag, setShouldTag] = useState(false);
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
const [kmsKeyId, setKmsKeyId] = useState("");

useEffect(() => {
if (workspace) {
Expand All @@ -98,6 +100,19 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
}, [workspace]);


const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } =
useGetIntegrationAuthAwsKmsKeys({
integrationAuthId: String(integrationAuthId),
region: selectedAWSRegion
});

useEffect(() => {
if (integrationAuthAwsKmsKeys) {
setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "default")[0]?.id))
}
}, [integrationAuthAwsKmsKeys])

const isValidAWSParameterStorePath = (awsStorePath: string) => {
const pattern = /^\/([\w-]+\/)*[\w-]+\/$/;
return pattern.test(awsStorePath) && awsStorePath.length <= 2048;
Expand Down Expand Up @@ -133,7 +148,11 @@ export default function AWSParameterStoreCreateIntegrationPage() {
value: tagValue
}]
}
: {})
: {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ?
{
kmsKeyId
}: {})
}
});

Expand All @@ -146,7 +165,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
};

return integrationAuth && workspace && selectedSourceEnvironment ? (
return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? (
<div className="flex h-full w-full flex-col items-center justify-center">
<Head>
<title>Set Up AWS Parameter Integration</title>
Expand Down Expand Up @@ -286,6 +305,31 @@ export default function AWSParameterStoreCreateIntegrationPage() {
</FormControl>
</div>
)}
<FormControl label="Encryption Key" className="mt-4">
<Select
value={kmsKeyId}
onValueChange={(e) => {
setKmsKeyId(e)
}}
className="w-full border border-mineshaft-500"
>
{integrationAuthAwsKmsKeys?.length ? (
integrationAuthAwsKmsKeys.map((key) => {
return (
<SelectItem
value={key.id as string}
key={`repo-id-${key.id}`}
className="w-[28.4rem] text-sm"
>
{key.alias}
</SelectItem>
);
})
) : (
<div />
)}
</Select>
</FormControl>
</motion.div>
</TabPanel>
</Tabs>
Expand Down Expand Up @@ -318,7 +362,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
<title>Set Up AWS Parameter Store Integration</title>
<link rel="icon" href="/infisical.ico" />
</Head>
{isintegrationAuthLoading ? (
{(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? (
<img
src="/images/loading/loading.gif"
height={70}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
}]
}
: {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "alias/aws/secretsmanager") ?
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ?
{
kmsKeyId
}: {})
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/views/IntegrationsPage/IntegrationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ import { ProjectVersion } from "@app/hooks/api/workspace/types";

import { CloudIntegrationSection } from "./components/CloudIntegrationSection";
import { FrameworkIntegrationSection } from "./components/FrameworkIntegrationSection";
import { InfrastructureIntegrationSection } from "./components/InfrastructureIntegrationSection/InfrastructureIntegrationSection";
import { IntegrationsSection } from "./components/IntegrationsSection";
import { generateBotKey, redirectForProviderAuth } from "./IntegrationPage.utils";

type Props = {
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
infrastructureIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
};

export const IntegrationsPage = withProjectPermission(
({ frameworkIntegrations }: Props) => {
({ frameworkIntegrations, infrastructureIntegrations }: Props) => {
const { t } = useTranslation();


Expand Down Expand Up @@ -228,6 +230,7 @@ export const IntegrationsPage = withProjectPermission(
</ModalContent>
</Modal>
<FrameworkIntegrationSection frameworks={frameworkIntegrations} />
<InfrastructureIntegrationSection integrations={infrastructureIntegrations} />
</div>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const CloudIntegrationSection = ({
</div>
{cloudIntegration.isAvailable &&
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
<div className="absolute top-0 right-0 z-40 h-full">
<div className="absolute top-0 right-0 z-30 h-full">
<div className="relative h-full">
<div className="absolute top-0 right-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-tr-md rounded-bl-md bg-primary py-0.5 px-2 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0">
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />
Expand Down
Loading
Loading