Update impl for AWS SM/PS integrations with KMS

This commit is contained in:
Tuan Dang
2024-04-29 16:49:53 -07:00
parent c8638479a8
commit 93b65a1534
5 changed files with 170 additions and 178 deletions
@@ -566,20 +566,32 @@ export const integrationAuthServiceFactory = ({
} }
}); });
const kms = new AWS.KMS(); const kms = new AWS.KMS();
const aliases = await kms.listAliases({}).promise(); const aliases = await kms.listAliases({}).promise();
const keys = await kms.listKeys({}).promise();
const response = keys
.Keys!.map((key) => {
const keyAlias = aliases.Aliases!.find((alias) => key.KeyId === alias.TargetKeyId);
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, { id: "null", alias: "default" }]; const keyAliases = aliases.Aliases!.filter((alias) => {
if (!alias.TargetKeyId) return false;
if (integrationAuth.integration === Integrations.AWS_PARAMETER_STORE && alias.AliasName === "alias/aws/ssm")
return true;
if (
integrationAuth.integration === Integrations.AWS_SECRET_MANAGER &&
alias.AliasName === "alias/aws/secretsmanager"
)
return true;
if (alias.AliasName?.includes("alias/aws/")) return false;
return alias.TargetKeyId;
});
const keysWithAliases = keyAliases.map((alias) => {
return {
id: alias.TargetKeyId!,
alias: alias.AliasName!
};
});
return keysWithAliases;
}; };
const getQoveryProjects = async ({ const getQoveryProjects = async ({
@@ -442,95 +442,99 @@ const syncSecretsAWSParameterStore = async ({
accessId: string | null; accessId: string | null;
accessToken: string; accessToken: string;
}) => { }) => {
if (!accessId) return; try {
if (!accessId) return;
const config = new AWS.Config({ const config = new AWS.Config({
region: integration.region as string, region: integration.region as string,
credentials: { credentials: {
accessKeyId: accessId, accessKeyId: accessId,
secretAccessKey: accessToken secretAccessKey: accessToken
} }
}); });
const ssm = new AWS.SSM({ const ssm = new AWS.SSM({
apiVersion: "2014-11-06", apiVersion: "2014-11-06",
region: integration.region as string region: integration.region as string
}); });
ssm.config.update(config); ssm.config.update(config);
const metadata = z.record(z.any()).parse(integration.metadata || {}); const metadata = z.record(z.any()).parse(integration.metadata || {});
const params = { const params = {
Path: integration.path as string, Path: integration.path as string,
Recursive: false, Recursive: false,
WithDecryption: true WithDecryption: true
}; };
const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters; const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters;
const awsParameterStoreSecretsObj = (parameterList || []) const awsParameterStoreSecretsObj = (parameterList || [])
.filter(({ Name }) => Boolean(Name)) .filter(({ Name }) => Boolean(Name))
.reduce( .reduce(
(obj, secret) => ({ (obj, secret) => ({
...obj, ...obj,
[(secret.Name as string).substring((integration.path as string).length)]: secret [(secret.Name as string).substring((integration.path as string).length)]: secret
}), }),
{} as Record<string, AWS.SSM.Parameter> {} as Record<string, AWS.SSM.Parameter>
); );
// Identify secrets to create // Identify secrets to create
await Promise.all( await Promise.all(
Object.keys(secrets).map(async (key) => { Object.keys(secrets).map(async (key) => {
if (!(key in awsParameterStoreSecretsObj)) { if (!(key in awsParameterStoreSecretsObj)) {
// case: secret does not exist in AWS parameter store // case: secret does not exist in AWS parameter store
// -> create secret // -> create secret
if (secrets[key].value) { if (secrets[key].value) {
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId }),
// 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
// -> update secret
await ssm await ssm
.putParameter({ .putParameter({
Name: `${integration.path}${key}`, Name: `${integration.path}${key}`,
Type: "SecureString", Type: "SecureString",
Value: secrets[key].value, Value: secrets[key].value,
KeyId: metadata.kmsKeyId ? metadata.kmsKeyId : undefined, Overwrite: true
// Overwrite: true, // Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
Key: tag.key,
Value: tag.value
}))
: []
}) })
.promise(); .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
// -> update secret
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
Overwrite: true
// Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : []
})
.promise();
}
})
);
// Identify secrets to delete // Identify secrets to delete
await Promise.all( await Promise.all(
Object.keys(awsParameterStoreSecretsObj).map(async (key) => { Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
if (!(key in secrets)) { if (!(key in secrets)) {
// case: // case:
// -> delete secret // -> delete secret
await ssm await ssm
.deleteParameter({ .deleteParameter({
Name: awsParameterStoreSecretsObj[key].Name as string Name: awsParameterStoreSecretsObj[key].Name as string
}) })
.promise(); .promise();
} }
}) })
); );
} catch (err) {
console.error("syncSecretsAWSPS error: ", err);
}
}; };
/** /**
@@ -572,7 +576,6 @@ const syncSecretsAWSSecretManager = async ({
if (awsSecretManagerSecret?.SecretString) { if (awsSecretManagerSecret?.SecretString) {
awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString); awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString);
} }
if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) { if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) {
await secretsManager.send( await secretsManager.send(
new UpdateSecretCommand({ new UpdateSecretCommand({
@@ -587,7 +590,7 @@ const syncSecretsAWSSecretManager = async ({
new CreateSecretCommand({ new CreateSecretCommand({
Name: integration.app as string, Name: integration.app as string,
SecretString: JSON.stringify(secKeyVal), SecretString: JSON.stringify(secKeyVal),
KmsKeyId: metadata.kmsKeyId ? metadata.kmsKeyId : null, ...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
Tags: metadata.secretAWSTag Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value })) ? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value }))
: [] : []
@@ -48,10 +48,9 @@ const integrationAuthKeys = {
integrationAuthId, integrationAuthId,
region region
}: { }: {
integrationAuthId: string, integrationAuthId: string;
region: string region: string;
}) => }) => [{ integrationAuthId, region }, "integrationAuthAwsKmsKeyIds"] as const,
[{ integrationAuthId, region }, "integrationAuthAwsKmsKeyIds"] as const,
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) => getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const, [{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
getIntegrationAuthQoveryProjects: ({ getIntegrationAuthQoveryProjects: ({
@@ -226,27 +225,6 @@ const fetchIntegrationAuthQoveryOrgs = async (integrationAuthId: string) => {
return orgs; return orgs;
}; };
const fetchIntegrationAuthAwsKmsKeys = async ({
integrationAuthId,
region
}: {
integrationAuthId: string;
region: string;
}) => {
const {
data: { kmsKeys }
} = await apiRequest.get<{ kmsKeys: KmsKey[] }>(
`/api/v1/integration-auth/${integrationAuthId}/aws-secrets-manager/kms-keys`,
{
params: {
region
}
}
);
return kmsKeys;
};
const fetchIntegrationAuthQoveryProjects = async ({ const fetchIntegrationAuthQoveryProjects = async ({
integrationAuthId, integrationAuthId,
orgId orgId
@@ -586,11 +564,22 @@ export const useGetIntegrationAuthAwsKmsKeys = ({
integrationAuthId, integrationAuthId,
region region
}), }),
queryFn: () => queryFn: async () => {
fetchIntegrationAuthAwsKmsKeys({ if (!region) return [];
integrationAuthId,
region const {
}), data: { kmsKeys }
} = await apiRequest.get<{ kmsKeys: KmsKey[] }>(
`/api/v1/integration-auth/${integrationAuthId}/aws-secrets-manager/kms-keys`,
{
params: {
region
}
}
);
return kmsKeys;
},
enabled: true enabled: true
}); });
}; };
@@ -100,19 +100,12 @@ export default function AWSParameterStoreCreateIntegrationPage() {
} }
}, [workspace]); }, [workspace]);
const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } =
useGetIntegrationAuthAwsKmsKeys({ useGetIntegrationAuthAwsKmsKeys({
integrationAuthId: String(integrationAuthId), integrationAuthId: String(integrationAuthId),
region: selectedAWSRegion region: selectedAWSRegion
}); });
useEffect(() => {
if (integrationAuthAwsKmsKeys) {
setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "default")[0]?.id))
}
}, [integrationAuthAwsKmsKeys])
const isValidAWSParameterStorePath = (awsStorePath: string) => { const isValidAWSParameterStorePath = (awsStorePath: string) => {
const pattern = /^\/([\w-]+\/)*[\w-]+\/$/; const pattern = /^\/([\w-]+\/)*[\w-]+\/$/;
return pattern.test(awsStorePath) && awsStorePath.length <= 2048; return pattern.test(awsStorePath) && awsStorePath.length <= 2048;
@@ -143,16 +136,15 @@ export default function AWSParameterStoreCreateIntegrationPage() {
metadata: { metadata: {
...(shouldTag ...(shouldTag
? { ? {
secretAWSTag: [{ secretAWSTag: [
key: tagKey, {
value: tagValue key: tagKey,
}] value: tagValue
}
]
} }
: {}), : {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ? ...(kmsKeyId && { kmsKeyId })
{
kmsKeyId
}: {})
} }
}); });
@@ -165,7 +157,10 @@ export default function AWSParameterStoreCreateIntegrationPage() {
} }
}; };
return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? ( return integrationAuth &&
workspace &&
selectedSourceEnvironment &&
!isIntegrationAuthAwsKmsKeysLoading ? (
<div className="flex h-full w-full flex-col items-center justify-center"> <div className="flex h-full w-full flex-col items-center justify-center">
<Head> <Head>
<title>Set Up AWS Parameter Integration</title> <title>Set Up AWS Parameter Integration</title>
@@ -241,7 +236,10 @@ export default function AWSParameterStoreCreateIntegrationPage() {
<FormControl label="AWS Region"> <FormControl label="AWS Region">
<Select <Select
value={selectedAWSRegion} value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)} onValueChange={(val) => {
setSelectedAWSRegion(val);
setKmsKeyId("");
}}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{awsRegions.map((awsRegion) => ( {awsRegions.map((awsRegion) => (
@@ -285,20 +283,16 @@ export default function AWSParameterStoreCreateIntegrationPage() {
</div> </div>
{shouldTag && ( {shouldTag && (
<div className="mt-4"> <div className="mt-4">
<FormControl <FormControl label="Tag Key">
label="Tag Key" <Input
> placeholder="managed-by"
<Input
placeholder="managed-by"
value={tagKey} value={tagKey}
onChange={(e) => setTagKey(e.target.value)} onChange={(e) => setTagKey(e.target.value)}
/> />
</FormControl> </FormControl>
<FormControl <FormControl label="Tag Value">
label="Tag Value" <Input
> placeholder="infisical"
<Input
placeholder="infisical"
value={tagValue} value={tagValue}
onChange={(e) => setTagValue(e.target.value)} onChange={(e) => setTagValue(e.target.value)}
/> />
@@ -309,7 +303,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
<Select <Select
value={kmsKeyId} value={kmsKeyId}
onValueChange={(e) => { onValueChange={(e) => {
setKmsKeyId(e) setKmsKeyId(e);
}} }}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
@@ -362,7 +356,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
<title>Set Up AWS Parameter Store Integration</title> <title>Set Up AWS Parameter Store Integration</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </Head>
{(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? ( {isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading ? (
<img <img
src="/images/loading/loading.gif" src="/images/loading/loading.gif"
height={70} height={70}
@@ -96,19 +96,12 @@ export default function AWSSecretManagerCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [shouldTag, setShouldTag] = useState(false); const [shouldTag, setShouldTag] = useState(false);
const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } =
useGetIntegrationAuthAwsKmsKeys({ useGetIntegrationAuthAwsKmsKeys({
integrationAuthId: String(integrationAuthId), integrationAuthId: String(integrationAuthId),
region: selectedAWSRegion region: selectedAWSRegion
}); });
useEffect(() => {
if (integrationAuthAwsKmsKeys) {
setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "alias/aws/secretsmanager")[0]?.id))
}
}, [integrationAuthAwsKmsKeys])
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace) {
setSelectedSourceEnvironment(workspace.environments[0].slug); setSelectedSourceEnvironment(workspace.environments[0].slug);
@@ -142,16 +135,15 @@ export default function AWSSecretManagerCreateIntegrationPage() {
metadata: { metadata: {
...(shouldTag ...(shouldTag
? { ? {
secretAWSTag: [{ secretAWSTag: [
key: tagKey, {
value: tagValue key: tagKey,
}] value: tagValue
}
]
} }
: {}), : {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ? ...(kmsKeyId && { kmsKeyId })
{
kmsKeyId
}: {})
} }
}); });
@@ -164,7 +156,10 @@ export default function AWSSecretManagerCreateIntegrationPage() {
} }
}; };
return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? ( return integrationAuth &&
workspace &&
selectedSourceEnvironment &&
!isIntegrationAuthAwsKmsKeysLoading ? (
<div className="flex h-full w-full flex-col items-center justify-center"> <div className="flex h-full w-full flex-col items-center justify-center">
<Head> <Head>
<title>Set Up AWS Secrets Manager Integration</title> <title>Set Up AWS Secrets Manager Integration</title>
@@ -240,7 +235,10 @@ export default function AWSSecretManagerCreateIntegrationPage() {
<FormControl label="AWS Region"> <FormControl label="AWS Region">
<Select <Select
value={selectedAWSRegion} value={selectedAWSRegion}
onValueChange={(val) => setSelectedAWSRegion(val)} onValueChange={(val) => {
setSelectedAWSRegion(val);
setKmsKeyId("");
}}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{awsRegions.map((awsRegion) => ( {awsRegions.map((awsRegion) => (
@@ -284,20 +282,16 @@ export default function AWSSecretManagerCreateIntegrationPage() {
</div> </div>
{shouldTag && ( {shouldTag && (
<div className="mt-4"> <div className="mt-4">
<FormControl <FormControl label="Tag Key">
label="Tag Key" <Input
> placeholder="managed-by"
<Input
placeholder="managed-by"
value={tagKey} value={tagKey}
onChange={(e) => setTagKey(e.target.value)} onChange={(e) => setTagKey(e.target.value)}
/> />
</FormControl> </FormControl>
<FormControl <FormControl label="Tag Value">
label="Tag Value" <Input
> placeholder="infisical"
<Input
placeholder="infisical"
value={tagValue} value={tagValue}
onChange={(e) => setTagValue(e.target.value)} onChange={(e) => setTagValue(e.target.value)}
/> />
@@ -308,7 +302,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
<Select <Select
value={kmsKeyId} value={kmsKeyId}
onValueChange={(e) => { onValueChange={(e) => {
setKmsKeyId(e) setKmsKeyId(e);
}} }}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
@@ -361,7 +355,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
<title>Set Up AWS Secrets Manager Integration</title> <title>Set Up AWS Secrets Manager Integration</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
</Head> </Head>
{(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? ( {isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading ? (
<img <img
src="/images/loading/loading.gif" src="/images/loading/loading.gif"
height={70} height={70}