From b46643584f585b055e34a48c0bb451b2dda7826d Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Mon, 13 Jan 2025 17:21:17 +0100 Subject: [PATCH 1/9] feat: refactor / additional flag autoGenerateIamPermissions - remove CreationType (Single, Multi) - replace with ResourceType (PARAMETER_MULTI) and move it to properties fixes: #1076 - add property 'autoGenerateIamPermissions' fixes: #1087 - add property 'role' for SopsSyncProvider fixes: #1087 - move resourceType from syncOptions to syncProperties, as it shouldn't be set by users - move permissionhandling to own functions, to reduce cyclomatic compexity --- .gitleaks.toml | 40 ++ .../event_create_s3_parameter_raw_simple.json | 1 - ...vent_create_s3_parameter_yaml_complex.json | 3 +- ...s3_parameter_yaml_complex_custom_keys.json | 3 +- lambda/main.go | 63 ++- src/MultiStringParameter.ts | 10 +- src/SopsSecret.ts | 8 +- src/SopsStringParameter.ts | 5 +- src/SopsSync.ts | 361 ++++++++++-------- test/permissions.test.ts | 58 +++ .../SecretIntegrationAsset.assets.json | 10 +- .../SecretIntegrationAsset.template.json | 52 +-- .../SecretIntegrationInline.assets.json | 10 +- .../SecretIntegrationInline.template.json | 35 +- .../SecretIntegrationAsset.assets.json | 10 +- .../SecretIntegrationAsset.template.json | 29 +- .../SecretMultiKms.assets.json | 10 +- .../SecretMultiKms.template.json | 60 ++- test/secret.test.ts | 4 +- 19 files changed, 428 insertions(+), 344 deletions(-) create mode 100644 .gitleaks.toml create mode 100644 test/permissions.test.ts diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..48362bae --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,40 @@ +[extend] +useDefault = true + +[[rules]] +id = "generic-api-key" +# all the other attributes from the default rule are inherited + + [[rules.allowlists]] + regexTarget = "line" + regexes = [ + '''objectKey''', + '''S3Key''', + '''SopsAgeKey''', + '''s3Key''', + ] + +[[rules]] +id = "private-key" + + [[rules.allowlists]] + regexTarget = "line" + regexes = [ + '''(.*)OAdqlMznWINBDoyR\+PESgQJlUptwnh(.*)''', + ] + +[allowlist] +description = "global allow list" +paths = [ + '''\.gitleaks\.toml''', + '''lambda/events/(.*?)json''', + '''lambda/__snapshots__/(.*?)snap''', + '''test-secrets/(.*?)(json|yaml|yml|env|binary)''', + '''test/(.*)\.integ\.snapshot/(.*?)json''' +] + +regexTarget = "match" +regexes = [ + '''AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3''', +] + diff --git a/lambda/events/event_create_s3_parameter_raw_simple.json b/lambda/events/event_create_s3_parameter_raw_simple.json index e3a94b67..0309d8c2 100644 --- a/lambda/events/event_create_s3_parameter_raw_simple.json +++ b/lambda/events/event_create_s3_parameter_raw_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "PARAMETER", - "CreationType": "SINGLE", "ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret", "SopsS3File": { "Bucket": "..", diff --git a/lambda/events/event_create_s3_parameter_yaml_complex.json b/lambda/events/event_create_s3_parameter_yaml_complex.json index 620541ba..b84319a1 100644 --- a/lambda/events/event_create_s3_parameter_yaml_complex.json +++ b/lambda/events/event_create_s3_parameter_yaml_complex.json @@ -2,8 +2,7 @@ "RequestType": "Create", "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { - "ResourceType": "PARAMETER", - "CreationType": "MULTI", + "ResourceType": "PARAMETER_MULTI", "ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret", "SopsS3File": { "Bucket": "..", diff --git a/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json b/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json index 1e8723e7..893a909e 100644 --- a/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json +++ b/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json @@ -2,8 +2,7 @@ "RequestType": "Create", "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { - "ResourceType": "PARAMETER", - "CreationType": "MULTI", + "ResourceType": "PARAMETER_MULTI", "ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret", "SopsS3File": { "Bucket": "..", diff --git a/lambda/main.go b/lambda/main.go index 2b6f3f8e..69bc408f 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -295,44 +295,41 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy returnData["VersionStages"] = updateSecretResp.VersionStages returnData["VersionId"] = *updateSecretResp.VersionId return *updateSecretResp.ARN, returnData, nil - } else if resourceProperties.ResourceType == "PARAMETER" { - if resourceProperties.CreationType == "MULTI" && resourcePropertiesFlatten { - log.Printf("Patching multiple string parameters") - v := reflect.ValueOf(finalInterface) - returnData := make(map[string]interface{}) - keys := v.MapKeys() - keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) } - sort.Slice(keys, keysOrder) - for _, key := range keys { - strKey := resourceProperties.ParameterKeyPrefix + key.String() - log.Printf("Parameter: " + strKey) - value := v.MapIndex(key).Interface() - strValue, ok := value.(string) - if !ok { - return tempArn, nil, nil - } - - _, err := a.updateSSMParameter(strKey, []byte(strValue), resourceProperties.EncryptionKey) - if err != nil { - return tempArn, nil, err - } - // A returnData map for each parameter is not created, because it would limit the number of possible parameters unnecessarily + } else if resourceProperties.ResourceType == "PARAMETER_MULTI" { + log.Printf("Patching multiple string parameters") + v := reflect.ValueOf(finalInterface) + returnData := make(map[string]interface{}) + keys := v.MapKeys() + keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) } + sort.Slice(keys, keysOrder) + for _, key := range keys { + strKey := resourceProperties.ParameterKeyPrefix + key.String() + log.Printf("Parameter: " + strKey) + value := v.MapIndex(key).Interface() + strValue, ok := value.(string) + if !ok { + return tempArn, nil, nil } - returnData["Prefix"] = resourceProperties.ParameterKeyPrefix - returnData["Count"] = len(keys) - return tempArn, returnData, nil - } else { - log.Printf("Patching single string parameter") - response, err := a.updateSSMParameter(resourceProperties.ParameterName, decryptedContent, resourceProperties.EncryptionKey) + _, err := a.updateSSMParameter(strKey, []byte(strValue), resourceProperties.EncryptionKey) if err != nil { return tempArn, nil, err } - returnData := make(map[string]interface{}) - returnData["ParameterName"] = resourceProperties.ParameterName - returnData["Version"] = response.Version - returnData["Tier"] = response.Tier - return tempArn, returnData, nil + // A returnData map for each parameter is not created, because it would limit the number of possible parameters unnecessarily } + returnData["Prefix"] = resourceProperties.ParameterKeyPrefix + returnData["Count"] = len(keys) + return tempArn, returnData, nil + } else if resourceProperties.ResourceType == "PARAMETER" { + log.Printf("Patching single string parameter") + response, err := a.updateSSMParameter(resourceProperties.ParameterName, decryptedContent, resourceProperties.EncryptionKey) + if err != nil { + return tempArn, nil, err + } + returnData := make(map[string]interface{}) + returnData["ParameterName"] = resourceProperties.ParameterName + returnData["Version"] = response.Version + returnData["Tier"] = response.Tier + return tempArn, returnData, nil } else { // Should never happen ... return tempArn, nil, errors.New("Neither SecretARN nor ParameterName is provided") diff --git a/src/MultiStringParameter.ts b/src/MultiStringParameter.ts index cc7f886c..a2c98478 100644 --- a/src/MultiStringParameter.ts +++ b/src/MultiStringParameter.ts @@ -5,12 +5,7 @@ import { ResourceEnvironment, Stack } from 'aws-cdk-lib/core'; import { Construct } from 'constructs'; import * as YAML from 'yaml'; import { SopsStringParameterProps } from './SopsStringParameter'; -import { - CreationType, - ResourceType, - SopsSync, - SopsSyncOptions, -} from './SopsSync'; +import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync'; interface JSONObject { [key: string]: any; @@ -83,8 +78,7 @@ export class MultiStringParameter extends Construct { this.sync = new SopsSync(this, 'SopsSync', { encryptionKey: this.encryptionKey, - resourceType: ResourceType.PARAMETER, - creationType: CreationType.MULTI, + resourceType: ResourceType.PARAMETER_MULTI, flatten: true, flattenSeparator: this.keySeparator, parameterKeyPrefix: this.keyPrefix, diff --git a/src/SopsSecret.ts b/src/SopsSecret.ts index a3c8c121..ba574851 100644 --- a/src/SopsSecret.ts +++ b/src/SopsSecret.ts @@ -20,12 +20,7 @@ import { Stack, } from 'aws-cdk-lib/core'; import { Construct } from 'constructs'; -import { - CreationType, - ResourceType, - SopsSync, - SopsSyncOptions, -} from './SopsSync'; +import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync'; /** * The configuration options of the SopsSecret @@ -63,7 +58,6 @@ export class SopsSecret extends Construct implements ISecret { this.sync = new SopsSync(this, 'SopsSync', { secret: this.secret, resourceType: ResourceType.SECRET, - creationType: CreationType.SINGLE, flattenSeparator: '.', ...(props as SopsSyncOptions), }); diff --git a/src/SopsStringParameter.ts b/src/SopsStringParameter.ts index cbb3aad2..42321106 100644 --- a/src/SopsStringParameter.ts +++ b/src/SopsStringParameter.ts @@ -7,7 +7,7 @@ import { } from 'aws-cdk-lib/aws-ssm'; import { RemovalPolicy, ResourceEnvironment, Stack } from 'aws-cdk-lib/core'; import { Construct } from 'constructs'; -import { SopsSync, SopsSyncOptions } from './SopsSync'; +import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync'; /** * The configuration options of the StringParameter @@ -58,7 +58,8 @@ export class SopsStringParameter extends Construct implements IStringParameter { this.sync = new SopsSync(this, 'SopsSync', { encryptionKey: this.parameter.encryptionKey, - parameterName: this.parameter.parameterName, + parameterNames: [this.parameter.parameterName], + resourceType: ResourceType.PARAMETER, ...(props as SopsSyncOptions), }); } diff --git a/src/SopsSync.ts b/src/SopsSync.ts index 244d0976..d0fabe64 100644 --- a/src/SopsSync.ts +++ b/src/SopsSync.ts @@ -33,20 +33,10 @@ export enum UploadType { ASSET = 'ASSET', } -export enum CreationType { - /** - * Create or update a single secret/parameter - */ - SINGLE = 'SINGLE', - /** - * Create or update a multiple secrets/parameters by flattening the SOPS file - */ - MULTI = 'MULTI', -} - export enum ResourceType { SECRET = 'SECRET', PARAMETER = 'PARAMETER', + PARAMETER_MULTI = 'PARAMETER_MULTI', } /** @@ -141,12 +131,16 @@ export interface SopsSyncOptions { */ readonly stringifyValues?: boolean; - readonly creationType?: CreationType; - readonly resourceType?: ResourceType; + /** + * Should this construct automatically create IAM permissions? + * + * @default true + */ + readonly autoGenerateIamPermissions?: boolean; } /** - * The configuration options extended by the target Secret + * The configuration options extended by the target Secret / Parameter */ export interface SopsSyncProps extends SopsSyncOptions { /** @@ -155,12 +149,7 @@ export interface SopsSyncProps extends SopsSyncOptions { readonly secret?: ISecret; /** - * The parameter name. If set this creates an encrypted SSM Parameter instead of a secret. - */ - readonly parameterName?: string; - - /** - * The parameter name. If set this creates an encrypted SSM Parameter instead of a secret. + * The parameter names. If set this creates encrypted SSM Parameters instead of a secret. */ readonly parameterNames?: string[]; @@ -168,6 +157,11 @@ export interface SopsSyncProps extends SopsSyncOptions { * The encryption key used for encrypting the ssm parameter if `parameterName` is set. */ readonly encryptionKey?: IKey; + + /** + * Will this Sync deploy a Secret or Parameter(s) + */ + readonly resourceType?: ResourceType; } /** @@ -192,6 +186,14 @@ export interface SopsSyncProviderProps { * @default - A dedicated security group will be created for the lambda function. */ readonly securityGroups?: ISecurityGroup[]; + + /** + * The role that should be used for the custom resource provider. + * If you don't specify any, a new role will be created with all required permissions + * + * @default - a new role will be created + */ + readonly role?: IRole; } export class SopsSyncProvider extends SingletonFunction implements IGrantable { @@ -206,6 +208,7 @@ export class SopsSyncProvider extends SingletonFunction implements IGrantable { runtime: Runtime.PROVIDED_AL2, handler: 'bootstrap', uuid: 'SopsSyncProvider', + role: props?.role, timeout: Duration.seconds(60), environment: { SOPS_AGE_KEY: Lazy.string({ @@ -268,8 +271,11 @@ export class SopsSync extends Construct { let sopsS3File: { Bucket: string; Key: string } | undefined = undefined; if ( - props.sopsFilePath !== undefined && - (props.sopsS3Bucket !== undefined || props.sopsS3Key !== undefined) + (props.sopsFilePath == undefined && + (props.sopsS3Bucket == undefined || props.sopsS3Key == undefined)) || + (props.sopsFilePath !== undefined && + props.sopsS3Bucket !== undefined && + props.sopsS3Key !== undefined) ) { throw new Error( 'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!', @@ -312,11 +318,12 @@ export class SopsSync extends Construct { if (!fs.existsSync(props.sopsFilePath)) { throw new Error(`File ${props.sopsFilePath} does not exist!`); } + const sopsFileContent = fs.readFileSync(props.sopsFilePath); switch (uploadType) { case UploadType.INLINE: { sopsInline = { - Content: fs.readFileSync(props.sopsFilePath).toString('base64'), + Content: sopsFileContent.toString('base64'), // We calculate the hash the same way as it would be done by new Asset(..) - so we can ensure stable version names even if switching from INLINE to ASSET and viceversa. Hash: FileSystem.fingerprint(props.sopsFilePath), }; @@ -334,71 +341,24 @@ export class SopsSync extends Construct { } } - if (provider.role !== undefined) { - if (props.sopsKmsKey !== undefined) { - props.sopsKmsKey.forEach((key) => key.grantDecrypt(provider.role!)); - } - const fileContent = fs.readFileSync(props.sopsFilePath); - // Handle keys - const regexKey = /arn:aws:kms:[a-z0-9-]+:[\d]+:key\/[a-z0-9-]+/g; - const resultsKey = fileContent.toString().match(regexKey); - if (resultsKey !== undefined) { - resultsKey?.forEach((result, index) => - Key.fromKeyArn(this, `SopsKey${index}`, result).grantDecrypt( - provider.role!, - ), - ); - } - const regexAlias = /arn:aws:kms:[a-z0-9-]+:[\d]+:alias\/[a-z0-9-]+/g; - const resultsAlias = fileContent.toString().match(regexAlias); - if (resultsAlias !== undefined) { - resultsAlias?.forEach((result, index) => - Key.fromLookup(this, `SopsAlias${index}`, { - aliasName: `alias/${result.split('/').slice(1).join('/')}`, - }).grantDecrypt(provider.role!), - ); - } - if (props.secret) { - props.secret.grantWrite(provider); - props.secret.encryptionKey?.grantEncryptDecrypt(provider); - if (props.secret?.encryptionKey !== undefined) { - props.secret.encryptionKey.grantEncryptDecrypt(provider); - } - } - if (props.parameterName) { - provider.addToRolePolicy( - new PolicyStatement({ - actions: ['ssm:PutParameter'], - resources: [ - `arn:aws:ssm:${Stack.of(this).region}:${ - Stack.of(this).account - }:parameter${ - props.parameterName.startsWith('/') - ? props.parameterName - : `/${props.parameterName}` - }`, - ], - }), - ); - props.encryptionKey?.grantEncryptDecrypt(provider); - } - if (props.parameterNames) { - this.createReducedParameterPolicy( - props.parameterNames, - provider.role, - ); - props.encryptionKey?.grantEncryptDecrypt(provider); - } - if (sopsAsset !== undefined) { - sopsAsset.bucket.grantRead(provider); - } + if (provider.role !== undefined && !props.autoGenerateIamPermissions) { + Permissions.sopsKeys(this, { + userDefinedKeys: props.sopsKmsKey, + role: provider.role, + sopsFileContent: sopsFileContent.toString(), + }); + Permissions.assetBucket(sopsAsset, provider.role); + Permissions.encryptionKey(props.encryptionKey, provider.role); + Permissions.secret(props.secret, provider.role); + Permissions.parameters(this, props.parameterNames, provider.role); } else { Annotations.of(this).addWarning( - `Please ensure proper permissions for the passed lambda function:\n - write Access to the secret\n - encrypt with the sopsKmsKey${ - uploadType === UploadType.ASSET - ? '\n - download from asset bucket' - : '' - }`, + [ + 'Please ensure proper permissions for the passed lambda function:', + ' - write Access to the secret/parameters', + ' - encrypt with the sopsKmsKey', + ' - download from asset bucket', + ].join('\n'), ); } if (props.sopsAgeKey !== undefined) { @@ -416,10 +376,6 @@ export class SopsSync extends Construct { Annotations.of(this).addWarning( 'You have to manually add permissions to the sops provider to (permission to download file, to decrypt sops file)!', ); - } else { - throw new Error( - 'You have to specify both sopsS3Bucket and sopsS3Key or neither!', - ); } if (sopsFileFormat === undefined) { @@ -439,90 +395,169 @@ export class SopsSync extends Construct { ParameterKeyPrefix: props.parameterKeyPrefix, Format: sopsFileFormat, StringifiedValues: this.stringifiedValues, - ParameterName: props.parameterName, + ParameterName: + // Dirty Workaround, refactor ... + props.parameterNames && props.parameterNames.length == 1 + ? props.parameterNames[0] + : undefined, EncryptionKey: props.secret !== undefined ? undefined : props.encryptionKey?.keyId, ResourceType: props.resourceType ? props.resourceType.toString() : ResourceType.SECRET.toString(), - CreationType: props.creationType - ? props.creationType.toString() - : CreationType.SINGLE.toString(), }, }); this.versionId = cr.getAttString('VersionId'); } +} - private createReducedParameterPolicy(parameters: string[], role: IRole) { - // Avoid too large policies - // The maximum size of a managed policy is 6.144 bytes -> 1 character = 1 byte - const maxPolicyBytes = 6000; // Keep some bytes as a buffer - const arnPrefixBytes = 55; // Content for "arn:aws:ssm:ap-southeast-3::parameter/ - let startAtParameter = 0; - let currentPolicyBytes = 300; // Reserve some byte space for basic stuff inside the policy - for (let i = 0; i < parameters.length; i += 1) { - if ( - // Check if the current parameter would fit into the policy - arnPrefixBytes + parameters[i].length + currentPolicyBytes < - maxPolicyBytes - ) { - // If so increase the byte counter - currentPolicyBytes = - arnPrefixBytes + parameters[i].length + currentPolicyBytes; - } else { - const parameterNamesChunk = parameters.slice( - startAtParameter, - i, //end of slice is not included - ); - startAtParameter = i; - currentPolicyBytes = 300; - // Create the policy for the selected chunk - const putPolicy = new ManagedPolicy( - this, - `SopsSecretParameterProviderManagedPolicyParameterAccess${i}`, - { - description: - 'Policy to grant parameter provider permissions to put parameter', - }, - ); - putPolicy.addStatements( - new PolicyStatement({ - actions: ['ssm:PutParameter'], - resources: parameterNamesChunk.map( - (param) => - `arn:aws:ssm:${Stack.of(this).region}:${ - Stack.of(this).account - }:parameter${param.startsWith('/') ? param : `/${param}`}`, - ), - }), - ); - role.addManagedPolicy(putPolicy); +export namespace Permissions { + /** + * Grants the necessary permissions for encrypt/decrypt on the customer managed encryption key + * for the secrets / parameters. + */ + export function encryptionKey(key: IKey | undefined, target: IGrantable) { + if (key === undefined) { + return; + } + key.grantEncryptDecrypt(target); + } + + export function keysFromSopsContent(ctx: Construct, c: string): IKey[] { + const regexKey = /arn:aws:kms:[a-z0-9-]+:[\d]+:key\/[a-z0-9-]+/g; + const resultsKey = c.match(regexKey); + if (resultsKey !== null) { + return resultsKey.map((result, index) => + Key.fromKeyArn(ctx, `SopsKey${index}`, result), + ); + } + return []; + } + + export function keysFromSopsContentAlias(ctx: Construct, c: string): IKey[] { + const regexAlias = /arn:aws:kms:[a-z0-9-]+:[\d]+:alias\/[a-z0-9-]+/g; + const resultsAlias = c.match(regexAlias); + if (resultsAlias !== null) { + return resultsAlias.map((result, index) => + Key.fromLookup(ctx, `SopsAlias${index}`, { + aliasName: `alias/${result.split('/').slice(1).join('/')}`, + }), + ); + } + return []; + } + + /** + * Grants the necessary permissions to decrypt the given sops file content. + * Takes user defined keys, and searches the sops file for keys and aliases. + */ + export function sopsKeys( + ctx: Construct, + props: { + userDefinedKeys?: IKey[]; + sopsFileContent: string; + role: IRole; + }, + ) { + (props.userDefinedKeys ?? []) + .concat( + keysFromSopsContent(ctx, props.sopsFileContent), + keysFromSopsContentAlias(ctx, props.sopsFileContent), + ) + .forEach((key) => key.grantDecrypt(props.role)); + } + + /** + * Grants the necessary permissions to write the given secrets. + */ + export function secret( + targetSecret: ISecret | undefined, + target: IGrantable, + ) { + if (targetSecret === undefined) { + return; + } + targetSecret.grantWrite(target); + } + + function sliceParameters(params: string[]): string[][] { + const result: string[][] = []; + /** + * The maximum size of a managed policy is 6.144 bytes -> 1 character = 1 byte + * bout 300 characters are reserved for the policy apart from resource arns + * with some buffer, we end with an upper limit of 5750 bytes + */ + const limit = 5750; + + /** + * Content for "arn:aws:ssm:ap-southeast-3::parameter/ + */ + const prefix = 55; + + let currentSize = 0; + let currentChunk: string[] = []; + for (const param of params) { + const paramLength = param.length + prefix; + if (currentSize + paramLength > limit) { + result.push(currentChunk); + currentChunk = []; + currentSize = 0; } + currentChunk.push(param); + currentSize += paramLength; } - const parameterNamesChunk = parameters.slice( - startAtParameter, - parameters.length, - ); - // Create the policy for the remaning elements - const putPolicy = new ManagedPolicy( - this, - `SopsSecretParameterProviderManagedPolicyParameterAccess${parameters.length}`, - { - description: - 'Policy to grant parameter provider permissions to put parameter', - }, - ); - putPolicy.addStatements( - new PolicyStatement({ - actions: ['ssm:PutParameter'], - resources: parameterNamesChunk.map( - (param) => - `arn:aws:ssm:${Stack.of(this).region}:${ - Stack.of(this).account - }:parameter${param.startsWith('/') ? param : `/${param}`}`, - ), - }), - ); - role.addManagedPolicy(putPolicy); + + if (currentChunk.length > 0) { + result.push(currentChunk); + } + return result; + } + + /** + * Grants the necessary permissions to write the given parameters. + */ + export function parameters( + ctx: Construct, + targetParameters: string[] | undefined, + role: IRole, + ) { + if (targetParameters === undefined) { + return; + } + + const paramSlices = sliceParameters(targetParameters); + + for (let i = 0; i < paramSlices.length; i++) { + const putPolicy = new ManagedPolicy( + ctx, + `SopsSecretParameterProviderManagedPolicyParameterAccess${i}`, + { + description: + 'Policy to grant parameter provider permissions to put parameter', + }, + ); + putPolicy.addStatements( + new PolicyStatement({ + actions: ['ssm:PutParameter'], + resources: paramSlices[i].map( + (param) => + `arn:aws:ssm:${Stack.of(ctx).region}:${ + Stack.of(ctx).account + }:parameter${param.startsWith('/') ? param : `/${param}`}`, + ), + }), + ); + role.addManagedPolicy(putPolicy); + } + } + + /** + * Grants the necessary permissions to read the given asset from S3. + */ + export function assetBucket(asset: Asset | undefined, target: IGrantable) { + if (asset === undefined) { + return; + } + asset.bucket.grantRead(target); } } diff --git a/test/permissions.test.ts b/test/permissions.test.ts new file mode 100644 index 00000000..8e89216a --- /dev/null +++ b/test/permissions.test.ts @@ -0,0 +1,58 @@ +import * as fs from 'fs'; +import path from 'path'; +import { Stack } from 'aws-cdk-lib'; +import { Permissions } from '../src/SopsSync'; + +describe('keysFromSopsContent', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + test('returns an empty array when no keys are found', () => { + const content = 'no keys here'; + const result = Permissions.keysFromSopsContent(stack, content); + expect(result).toEqual([]); + }); + + test('returns an array of IKey objects when keys are found', () => { + const content = fs + .readFileSync( + path.join(__dirname, '../test-secrets/yaml/sopsfile.enc-kms.yaml'), + ) + .toString(); + const result = Permissions.keysFromSopsContent(stack, content); + expect(result).toHaveLength(1); + expect(result[0].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab', + ); + expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab'); + }); + + test('returns multiple IKey objects when multiple keys are found', () => { + const content = fs + .readFileSync( + path.join(__dirname, '../test-secrets/yaml/sopsfile.enc-multikms.yaml'), + ) + .toString(); + const result = Permissions.keysFromSopsContent(stack, content); + expect(result).toHaveLength(4); + expect(result[0].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab', + ); + expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab'); + expect(result[1].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000001-1234-4321-abcd-1234abcd12ab', + ); + expect(result[1].keyId).toBe('00000001-1234-4321-abcd-1234abcd12ab'); + expect(result[2].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000002-1234-4321-abcd-1234abcd12ab', + ); + expect(result[2].keyId).toBe('00000002-1234-4321-abcd-1234abcd12ab'); + expect(result[3].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000003-1234-4321-abcd-1234abcd12ab', + ); + expect(result[3].keyId).toBe('00000003-1234-4321-abcd-1234abcd12ab'); + }); +}); diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json index a5efac8f..33cf5aa9 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": { + "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { "source": { - "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -79,7 +79,7 @@ } } }, - "e363e1c42928a35c199fd17c68fdac2df3407feb9216f8cdf1511a609a2b0f72": { + "ee003947bd7743f60ea9e9f3107b58b66df0259acd6bc9d131615a1fa10a8429": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -87,7 +87,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e363e1c42928a35c199fd17c68fdac2df3407feb9216f8cdf1511a609a2b0f72.json", + "objectKey": "ee003947bd7743f60ea9e9f3107b58b66df0259acd6bc9d131615a1fa10a8429.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json index 40e8b4df..bd9d18e4 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json @@ -31,8 +31,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -73,16 +72,6 @@ "Properties": { "PolicyDocument": { "Statement": [ - { - "Action": [ - "secretsmanager:PutSecretValue", - "secretsmanager:UpdateSecret" - ], - "Effect": "Allow", - "Resource": { - "Ref": "SopsSecretJSON72040543" - } - }, { "Action": [ "s3:GetObject*", @@ -124,6 +113,16 @@ } ] }, + { + "Action": [ + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecret" + ], + "Effect": "Allow", + "Resource": { + "Ref": "SopsSecretJSON72040543" + } + }, { "Action": [ "secretsmanager:PutSecretValue", @@ -232,7 +231,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip" + "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" }, "Environment": { "Variables": { @@ -285,8 +284,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -322,8 +320,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -359,8 +356,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -396,8 +392,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -433,8 +428,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -470,8 +464,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -507,8 +500,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -544,8 +536,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -581,8 +572,7 @@ "FlattenSeparator": ".", "Format": "binary", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json index f17b8c6a..0f0e0e10 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": { + "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { "source": { - "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "3dcfa0a64e513705636a673bbca1b4c1ca9804d58579498b3479f581295035fa": { + "5a5e753daa5d4633ab32a983522828d7452f745cc537f206a8e7d375994fcb64": { "source": { "path": "SecretIntegrationInline.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3dcfa0a64e513705636a673bbca1b4c1ca9804d58579498b3479f581295035fa.json", + "objectKey": "5a5e753daa5d4633ab32a983522828d7452f745cc537f206a8e7d375994fcb64.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json index 5ff61693..62d9aa55 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json @@ -29,8 +29,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -199,7 +198,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip" + "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" }, "Environment": { "Variables": { @@ -250,8 +249,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -285,8 +283,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -320,8 +317,7 @@ "FlattenSeparator": ".", "Format": "dotenv", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -355,8 +351,7 @@ "FlattenSeparator": ".", "Format": "dotenv", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -390,8 +385,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -425,8 +419,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -460,8 +453,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -495,8 +487,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -530,8 +521,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -565,8 +555,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json index 3aa36317..2be4ce76 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": { + "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { "source": { - "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "d4e0b1978d4b4a8bd4a3460f5250588a128ba18a716657ee533bbc19a93d6ce7": { + "c0217209da80af0f706b2861e7a73ee40c2169f5d2276de65475e2256d077401": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "d4e0b1978d4b4a8bd4a3460f5250588a128ba18a716657ee533bbc19a93d6ce7.json", + "objectKey": "c0217209da80af0f706b2861e7a73ee40c2169f5d2276de65475e2256d077401.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json index 059453d4..995a0a96 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json @@ -29,8 +29,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -73,7 +72,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip" + "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" }, "Environment": { "Variables": { @@ -123,8 +122,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -158,8 +156,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -193,8 +190,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -228,8 +224,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -263,8 +258,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -298,8 +292,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -333,8 +326,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -368,8 +360,7 @@ "FlattenSeparator": ".", "Format": "yaml", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json index 735fd0c3..9ef048df 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": { + "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { "source": { - "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip", + "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -27,7 +27,7 @@ } } }, - "b2891d8d1afc3baddc99715abe47f05ff84ad390166c5277a91fc50103fc4457": { + "66e5133c6541e8a6ba203faa3f313ab163bfc78f98fa4185c342a364934a303b": { "source": { "path": "SecretMultiKms.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b2891d8d1afc3baddc99715abe47f05ff84ad390166c5277a91fc50103fc4457.json", + "objectKey": "66e5133c6541e8a6ba203faa3f313ab163bfc78f98fa4185c342a364934a303b.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json index 9a9960ee..35d6ab07 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json @@ -191,8 +191,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -233,31 +232,6 @@ "Properties": { "PolicyDocument": { "Statement": [ - { - "Action": [ - "secretsmanager:PutSecretValue", - "secretsmanager:UpdateSecret" - ], - "Effect": "Allow", - "Resource": { - "Ref": "SopsSecretOwnKmsMey0B320436" - } - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CustomKey1E6D0D07", - "Arn" - ] - } - }, { "Action": [ "s3:GetObject*", @@ -299,6 +273,21 @@ } ] }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "CustomKey1E6D0D07", + "Arn" + ] + } + }, { "Action": [ "secretsmanager:PutSecretValue", @@ -306,7 +295,7 @@ ], "Effect": "Allow", "Resource": { - "Ref": "SopsSecretForeignKmsMey8C3BA0B7" + "Ref": "SopsSecretOwnKmsMey0B320436" } }, { @@ -318,6 +307,16 @@ ], "Effect": "Allow", "Resource": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012" + }, + { + "Action": [ + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecret" + ], + "Effect": "Allow", + "Resource": { + "Ref": "SopsSecretForeignKmsMey8C3BA0B7" + } } ], "Version": "2012-10-17" @@ -337,7 +336,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip" + "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" }, "Environment": { "Variables": { @@ -391,8 +390,7 @@ "FlattenSeparator": ".", "Format": "json", "StringifiedValues": true, - "ResourceType": "SECRET", - "CreationType": "SINGLE" + "ResourceType": "SECRET" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/test/secret.test.ts b/test/secret.test.ts index 7797589e..92cfcdcd 100644 --- a/test/secret.test.ts +++ b/test/secret.test.ts @@ -506,7 +506,7 @@ test('Allowed options for SopsSync', () => { sopsS3Key: 'test', }), ).toThrowError( - 'You have to specify both sopsS3Bucket and sopsS3Key or neither!', + 'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!', ); expect( () => @@ -514,7 +514,7 @@ test('Allowed options for SopsSync', () => { sopsS3Bucket: 'test', }), ).toThrowError( - 'You have to specify both sopsS3Bucket and sopsS3Key or neither!', + 'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!', ); expect( () => From c98282ea1f820ef0a03b4e3d04adaf6a877ba2b1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 13 Jan 2025 16:29:48 +0000 Subject: [PATCH 2/9] chore: self mutation Signed-off-by: github-actions --- API.md | 233 +++++++----------- .../SecretIntegrationAsset.assets.json | 10 +- .../SecretIntegrationAsset.template.json | 2 +- .../SecretIntegrationInline.assets.json | 10 +- .../SecretIntegrationInline.template.json | 2 +- .../SecretIntegrationAsset.assets.json | 10 +- .../SecretIntegrationAsset.template.json | 2 +- .../SecretMultiKms.assets.json | 10 +- .../SecretMultiKms.template.json | 2 +- 9 files changed, 118 insertions(+), 163 deletions(-) diff --git a/API.md b/API.md index 4658531e..a986522a 100644 --- a/API.md +++ b/API.md @@ -1717,12 +1717,11 @@ const multiStringParameterProps: MultiStringParameterProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| autoGenerateIamPermissions | boolean | Should this construct automatically create IAM permissions? | | convertToJSON | boolean | Should the encrypted sops value should be converted to JSON? | -| creationType | CreationType | *No description.* | | flatten | boolean | Should the structure be flattened? | | flattenSeparator | string | If the structure should be flattened use the provided separator between keys. | | parameterKeyPrefix | string | Add this prefix to parameter names. | -| resourceType | ResourceType | *No description.* | | sopsAgeKey | aws-cdk-lib.SecretValue | The age key that should be used for encryption. | | sopsFileFormat | string | The format of the sops file. | | sopsFilePath | string | The filepath to the sops file. | @@ -1746,28 +1745,31 @@ const multiStringParameterProps: MultiStringParameterProps = { ... } --- -##### `convertToJSON`Optional +##### `autoGenerateIamPermissions`Optional ```typescript -public readonly convertToJSON: boolean; +public readonly autoGenerateIamPermissions: boolean; ``` - *Type:* boolean - *Default:* true -Should the encrypted sops value should be converted to JSON? - -Only JSON can be handled by cloud formations dynamic references. +Should this construct automatically create IAM permissions? --- -##### `creationType`Optional +##### `convertToJSON`Optional ```typescript -public readonly creationType: CreationType; +public readonly convertToJSON: boolean; ``` -- *Type:* CreationType +- *Type:* boolean +- *Default:* true + +Should the encrypted sops value should be converted to JSON? + +Only JSON can be handled by cloud formations dynamic references. --- @@ -1813,16 +1815,6 @@ Add this prefix to parameter names. --- -##### `resourceType`Optional - -```typescript -public readonly resourceType: ResourceType; -``` - -- *Type:* ResourceType - ---- - ##### `sopsAgeKey`Optional ```typescript @@ -2117,12 +2109,11 @@ const sopsSecretProps: SopsSecretProps = { ... } | secretObjectValue | {[ key: string ]: aws-cdk-lib.SecretValue} | Initial value for a JSON secret. | | secretStringBeta1 | aws-cdk-lib.aws_secretsmanager.SecretStringValueBeta1 | Initial value for the secret. | | secretStringValue | aws-cdk-lib.SecretValue | Initial value for the secret. | +| autoGenerateIamPermissions | boolean | Should this construct automatically create IAM permissions? | | convertToJSON | boolean | Should the encrypted sops value should be converted to JSON? | -| creationType | CreationType | *No description.* | | flatten | boolean | Should the structure be flattened? | | flattenSeparator | string | If the structure should be flattened use the provided separator between keys. | | parameterKeyPrefix | string | Add this prefix to parameter names. | -| resourceType | ResourceType | *No description.* | | sopsAgeKey | aws-cdk-lib.SecretValue | The age key that should be used for encryption. | | sopsFileFormat | string | The format of the sops file. | | sopsFilePath | string | The filepath to the sops file. | @@ -2308,28 +2299,31 @@ Only one of `secretStringBeta1`, `secretStringValue`, 'secretObjectValue', and ` --- -##### `convertToJSON`Optional +##### `autoGenerateIamPermissions`Optional ```typescript -public readonly convertToJSON: boolean; +public readonly autoGenerateIamPermissions: boolean; ``` - *Type:* boolean - *Default:* true -Should the encrypted sops value should be converted to JSON? - -Only JSON can be handled by cloud formations dynamic references. +Should this construct automatically create IAM permissions? --- -##### `creationType`Optional +##### `convertToJSON`Optional ```typescript -public readonly creationType: CreationType; +public readonly convertToJSON: boolean; ``` -- *Type:* CreationType +- *Type:* boolean +- *Default:* true + +Should the encrypted sops value should be converted to JSON? + +Only JSON can be handled by cloud formations dynamic references. --- @@ -2375,16 +2369,6 @@ Add this prefix to parameter names. --- -##### `resourceType`Optional - -```typescript -public readonly resourceType: ResourceType; -``` - -- *Type:* ResourceType - ---- - ##### `sopsAgeKey`Optional ```typescript @@ -2522,12 +2506,11 @@ const sopsStringParameterProps: SopsStringParameterProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| autoGenerateIamPermissions | boolean | Should this construct automatically create IAM permissions? | | convertToJSON | boolean | Should the encrypted sops value should be converted to JSON? | -| creationType | CreationType | *No description.* | | flatten | boolean | Should the structure be flattened? | | flattenSeparator | string | If the structure should be flattened use the provided separator between keys. | | parameterKeyPrefix | string | Add this prefix to parameter names. | -| resourceType | ResourceType | *No description.* | | sopsAgeKey | aws-cdk-lib.SecretValue | The age key that should be used for encryption. | | sopsFileFormat | string | The format of the sops file. | | sopsFilePath | string | The filepath to the sops file. | @@ -2549,28 +2532,31 @@ const sopsStringParameterProps: SopsStringParameterProps = { ... } --- -##### `convertToJSON`Optional +##### `autoGenerateIamPermissions`Optional ```typescript -public readonly convertToJSON: boolean; +public readonly autoGenerateIamPermissions: boolean; ``` - *Type:* boolean - *Default:* true -Should the encrypted sops value should be converted to JSON? - -Only JSON can be handled by cloud formations dynamic references. +Should this construct automatically create IAM permissions? --- -##### `creationType`Optional +##### `convertToJSON`Optional ```typescript -public readonly creationType: CreationType; +public readonly convertToJSON: boolean; ``` -- *Type:* CreationType +- *Type:* boolean +- *Default:* true + +Should the encrypted sops value should be converted to JSON? + +Only JSON can be handled by cloud formations dynamic references. --- @@ -2616,16 +2602,6 @@ Add this prefix to parameter names. --- -##### `resourceType`Optional - -```typescript -public readonly resourceType: ResourceType; -``` - -- *Type:* ResourceType - ---- - ##### `sopsAgeKey`Optional ```typescript @@ -2891,12 +2867,11 @@ const sopsSyncOptions: SopsSyncOptions = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| autoGenerateIamPermissions | boolean | Should this construct automatically create IAM permissions? | | convertToJSON | boolean | Should the encrypted sops value should be converted to JSON? | -| creationType | CreationType | *No description.* | | flatten | boolean | Should the structure be flattened? | | flattenSeparator | string | If the structure should be flattened use the provided separator between keys. | | parameterKeyPrefix | string | Add this prefix to parameter names. | -| resourceType | ResourceType | *No description.* | | sopsAgeKey | aws-cdk-lib.SecretValue | The age key that should be used for encryption. | | sopsFileFormat | string | The format of the sops file. | | sopsFilePath | string | The filepath to the sops file. | @@ -2909,28 +2884,31 @@ const sopsSyncOptions: SopsSyncOptions = { ... } --- -##### `convertToJSON`Optional +##### `autoGenerateIamPermissions`Optional ```typescript -public readonly convertToJSON: boolean; +public readonly autoGenerateIamPermissions: boolean; ``` - *Type:* boolean - *Default:* true -Should the encrypted sops value should be converted to JSON? - -Only JSON can be handled by cloud formations dynamic references. +Should this construct automatically create IAM permissions? --- -##### `creationType`Optional +##### `convertToJSON`Optional ```typescript -public readonly creationType: CreationType; +public readonly convertToJSON: boolean; ``` -- *Type:* CreationType +- *Type:* boolean +- *Default:* true + +Should the encrypted sops value should be converted to JSON? + +Only JSON can be handled by cloud formations dynamic references. --- @@ -2976,16 +2954,6 @@ Add this prefix to parameter names. --- -##### `resourceType`Optional - -```typescript -public readonly resourceType: ResourceType; -``` - -- *Type:* ResourceType - ---- - ##### `sopsAgeKey`Optional ```typescript @@ -3109,7 +3077,7 @@ How should the secret be passed to the CustomResource? ### SopsSyncProps -The configuration options extended by the target Secret. +The configuration options extended by the target Secret / Parameter. #### Initializer @@ -3123,12 +3091,11 @@ const sopsSyncProps: SopsSyncProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| autoGenerateIamPermissions | boolean | Should this construct automatically create IAM permissions? | | convertToJSON | boolean | Should the encrypted sops value should be converted to JSON? | -| creationType | CreationType | *No description.* | | flatten | boolean | Should the structure be flattened? | | flattenSeparator | string | If the structure should be flattened use the provided separator between keys. | | parameterKeyPrefix | string | Add this prefix to parameter names. | -| resourceType | ResourceType | *No description.* | | sopsAgeKey | aws-cdk-lib.SecretValue | The age key that should be used for encryption. | | sopsFileFormat | string | The format of the sops file. | | sopsFilePath | string | The filepath to the sops file. | @@ -3139,34 +3106,37 @@ const sopsSyncProps: SopsSyncProps = { ... } | stringifyValues | boolean | Shall all values be flattened? | | uploadType | UploadType | How should the secret be passed to the CustomResource? | | encryptionKey | aws-cdk-lib.aws_kms.IKey | The encryption key used for encrypting the ssm parameter if `parameterName` is set. | -| parameterName | string | The parameter name. | -| parameterNames | string[] | The parameter name. | +| parameterNames | string[] | The parameter names. | +| resourceType | ResourceType | Will this Sync deploy a Secret or Parameter(s). | | secret | aws-cdk-lib.aws_secretsmanager.ISecret | The secret that will be populated with the encrypted sops file content. | --- -##### `convertToJSON`Optional +##### `autoGenerateIamPermissions`Optional ```typescript -public readonly convertToJSON: boolean; +public readonly autoGenerateIamPermissions: boolean; ``` - *Type:* boolean - *Default:* true -Should the encrypted sops value should be converted to JSON? - -Only JSON can be handled by cloud formations dynamic references. +Should this construct automatically create IAM permissions? --- -##### `creationType`Optional +##### `convertToJSON`Optional ```typescript -public readonly creationType: CreationType; +public readonly convertToJSON: boolean; ``` -- *Type:* CreationType +- *Type:* boolean +- *Default:* true + +Should the encrypted sops value should be converted to JSON? + +Only JSON can be handled by cloud formations dynamic references. --- @@ -3212,16 +3182,6 @@ Add this prefix to parameter names. --- -##### `resourceType`Optional - -```typescript -public readonly resourceType: ResourceType; -``` - -- *Type:* ResourceType - ---- - ##### `sopsAgeKey`Optional ```typescript @@ -3355,31 +3315,29 @@ The encryption key used for encrypting the ssm parameter if `parameterName` is s --- -##### `parameterName`Optional +##### `parameterNames`Optional ```typescript -public readonly parameterName: string; +public readonly parameterNames: string[]; ``` -- *Type:* string +- *Type:* string[] -The parameter name. +The parameter names. -If set this creates an encrypted SSM Parameter instead of a secret. +If set this creates encrypted SSM Parameters instead of a secret. --- -##### `parameterNames`Optional +##### `resourceType`Optional ```typescript -public readonly parameterNames: string[]; +public readonly resourceType: ResourceType; ``` -- *Type:* string[] - -The parameter name. +- *Type:* ResourceType -If set this creates an encrypted SSM Parameter instead of a secret. +Will this Sync deploy a Secret or Parameter(s). --- @@ -3411,12 +3369,28 @@ const sopsSyncProviderProps: SopsSyncProviderProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| role | aws-cdk-lib.aws_iam.IRole | The role that should be used for the custom resource provider. | | securityGroups | aws-cdk-lib.aws_ec2.ISecurityGroup[] | Only if `vpc` is supplied: The list of security groups to associate with the Lambda's network interfaces. | | vpc | aws-cdk-lib.aws_ec2.IVpc | VPC network to place Lambda network interfaces. | | vpcSubnets | aws-cdk-lib.aws_ec2.SubnetSelection | Where to place the network interfaces within the VPC. | --- +##### `role`Optional + +```typescript +public readonly role: IRole; +``` + +- *Type:* aws-cdk-lib.aws_iam.IRole +- *Default:* a new role will be created + +The role that should be used for the custom resource provider. + +If you don't specify any, a new role will be created with all required permissions + +--- + ##### `securityGroups`Optional ```typescript @@ -3460,31 +3434,6 @@ Where to place the network interfaces within the VPC. ## Enums -### CreationType - -#### Members - -| **Name** | **Description** | -| --- | --- | -| SINGLE | Create or update a single secret/parameter. | -| MULTI | Create or update a multiple secrets/parameters by flattening the SOPS file. | - ---- - -##### `SINGLE` - -Create or update a single secret/parameter. - ---- - - -##### `MULTI` - -Create or update a multiple secrets/parameters by flattening the SOPS file. - ---- - - ### ResourceType #### Members @@ -3493,6 +3442,7 @@ Create or update a multiple secrets/parameters by flattening the SOPS file. | --- | --- | | SECRET | *No description.* | | PARAMETER | *No description.* | +| PARAMETER_MULTI | *No description.* | --- @@ -3506,6 +3456,11 @@ Create or update a multiple secrets/parameters by flattening the SOPS file. --- +##### `PARAMETER_MULTI` + +--- + + ### UploadType #### Members diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json index 33cf5aa9..7e334d13 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { + "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { "source": { - "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -79,7 +79,7 @@ } } }, - "ee003947bd7743f60ea9e9f3107b58b66df0259acd6bc9d131615a1fa10a8429": { + "76ae6b126c78a065078fae6c04e4062fb434dabe184b6f01cdff5d7c8b4b9610": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -87,7 +87,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "ee003947bd7743f60ea9e9f3107b58b66df0259acd6bc9d131615a1fa10a8429.json", + "objectKey": "76ae6b126c78a065078fae6c04e4062fb434dabe184b6f01cdff5d7c8b4b9610.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json index bd9d18e4..0f1ea581 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json @@ -231,7 +231,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" + "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" }, "Environment": { "Variables": { diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json index 0f0e0e10..4ff13f03 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { + "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { "source": { - "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "5a5e753daa5d4633ab32a983522828d7452f745cc537f206a8e7d375994fcb64": { + "0c5708f02afd19879e2de4293a656e79dc61639b3e2eb0a318efc0984b4f6cf2": { "source": { "path": "SecretIntegrationInline.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "5a5e753daa5d4633ab32a983522828d7452f745cc537f206a8e7d375994fcb64.json", + "objectKey": "0c5708f02afd19879e2de4293a656e79dc61639b3e2eb0a318efc0984b4f6cf2.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json index 62d9aa55..6742f3c1 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json @@ -198,7 +198,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" + "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" }, "Environment": { "Variables": { diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json index 2be4ce76..9011900d 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { + "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { "source": { - "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "c0217209da80af0f706b2861e7a73ee40c2169f5d2276de65475e2256d077401": { + "97fd60ebfa8440e4000cef3de6f7fe157f40d4d8f917942aa0ee7e4f229b489d": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "c0217209da80af0f706b2861e7a73ee40c2169f5d2276de65475e2256d077401.json", + "objectKey": "97fd60ebfa8440e4000cef3de6f7fe157f40d4d8f917942aa0ee7e4f229b489d.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json index 995a0a96..787b1cb4 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json @@ -72,7 +72,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" + "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" }, "Environment": { "Variables": { diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json index 9ef048df..cfa2a38b 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee": { + "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { "source": { - "path": "asset.3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip", + "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -27,7 +27,7 @@ } } }, - "66e5133c6541e8a6ba203faa3f313ab163bfc78f98fa4185c342a364934a303b": { + "e30d5ef78da253f697f3bf0aaf72550fceb9a9eefae9469f6eb7008380f429cb": { "source": { "path": "SecretMultiKms.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "66e5133c6541e8a6ba203faa3f313ab163bfc78f98fa4185c342a364934a303b.json", + "objectKey": "e30d5ef78da253f697f3bf0aaf72550fceb9a9eefae9469f6eb7008380f429cb.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json index 35d6ab07..e5b70937 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json @@ -336,7 +336,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "3b70dd74522545fd1512f35f09eee7be6b317571aa28afc14540a494b3e5c4ee.zip" + "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" }, "Environment": { "Variables": { From 68005f25debccccf3fbdb7ca7eaf724f37c7e699 Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Tue, 14 Jan 2025 11:17:30 +0100 Subject: [PATCH 3/9] chore(Tests): add more tests for permissions testing --- test/__snapshots__/permissions.test.ts.snap | 62 ++++++++++++++ test/permissions.test.ts | 93 +++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 test/__snapshots__/permissions.test.ts.snap diff --git a/test/__snapshots__/permissions.test.ts.snap b/test/__snapshots__/permissions.test.ts.snap new file mode 100644 index 00000000..3c64327c --- /dev/null +++ b/test/__snapshots__/permissions.test.ts.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parameters 1 parameter - full snapshot 1`] = ` +Object { + "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3": Object { + "Properties": Object { + "Description": "Policy to grant parameter provider permissions to put parameter", + "Path": "/", + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": "ssm:PutParameter", + "Effect": "Allow", + "Resource": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:aws:ssm:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":parameter/parameter0", + ], + ], + }, + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::IAM::ManagedPolicy", + }, +} +`; + +exports[`parameters 100 parameter - snapshot count 1`] = ` +Array [ + "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3", + "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2", +] +`; + +exports[`parameters 1000 parameter - snapshot count 1`] = ` +Array [ + "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3", + "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2", + "SopsSecretParameterProviderManagedPolicyParameterAccess204C56AEE", + "SopsSecretParameterProviderManagedPolicyParameterAccess39C2C7238", + "SopsSecretParameterProviderManagedPolicyParameterAccess4810168FC", + "SopsSecretParameterProviderManagedPolicyParameterAccess5A4020F4D", + "SopsSecretParameterProviderManagedPolicyParameterAccess6D18916A5", + "SopsSecretParameterProviderManagedPolicyParameterAccess7C1D2D5DD", + "SopsSecretParameterProviderManagedPolicyParameterAccess881B83B3B", + "SopsSecretParameterProviderManagedPolicyParameterAccess9F6C18C5F", + "SopsSecretParameterProviderManagedPolicyParameterAccess10B80D3BA8", + "SopsSecretParameterProviderManagedPolicyParameterAccess118D826D5D", +] +`; diff --git a/test/permissions.test.ts b/test/permissions.test.ts index 8e89216a..e10ab9f1 100644 --- a/test/permissions.test.ts +++ b/test/permissions.test.ts @@ -1,6 +1,9 @@ import * as fs from 'fs'; import path from 'path'; import { Stack } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { IRole, Role } from 'aws-cdk-lib/aws-iam'; +import { Key } from 'aws-cdk-lib/aws-kms'; import { Permissions } from '../src/SopsSync'; describe('keysFromSopsContent', () => { @@ -56,3 +59,93 @@ describe('keysFromSopsContent', () => { expect(result[3].keyId).toBe('00000003-1234-4321-abcd-1234abcd12ab'); }); }); + +describe('keysFromSopsContentAlias', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + test('returns an empty array when no keys are found', () => { + const content = 'no keys here'; + const result = Permissions.keysFromSopsContentAlias(stack, content); + expect(result).toEqual([]); + }); + + test('returns an array of IKey objects when keys are found', () => { + jest + .spyOn(Key, 'fromLookup') + .mockReturnValue( + Key.fromKeyArn( + stack, + 'Key', + 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab', + ), + ); + + const content = fs + .readFileSync( + path.join( + __dirname, + '../test-secrets/yaml/sopsfile.enc-kms-alias.yaml', + ), + ) + .toString(); + const result = Permissions.keysFromSopsContentAlias(stack, content); + expect(result[0].keyArn).toBe( + 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab', + ); + expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab'); + }); +}); + +describe('parameters', () => { + let stack: Stack; + let role: IRole; + + beforeEach(() => { + stack = new Stack(); + role = Role.fromRoleArn( + stack, + 'TestRole', + 'arn:aws:iam::123456789012:role/test-role', + ); + }); + + function genParameters(prefix: string, count: number) { + const params: string[] = []; + for (let i = 0; i < count; i++) { + params.push(`${prefix}${i}`); + } + return params; + } + + test('1 parameter - full snapshot', () => { + const params = genParameters('parameter', 1); + Permissions.parameters(stack, params, role); + expect( + Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'), + ).toMatchSnapshot(); + }); + + test('100 parameter - snapshot count', () => { + const params = genParameters('parameter', 100); + Permissions.parameters(stack, params, role); + expect( + Object.keys( + Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'), + ), + ).toMatchSnapshot(); + }); + + test('1000 parameter - snapshot count', () => { + const params = genParameters('parameter', 1000); + Permissions.parameters(stack, params, role); + expect( + Object.keys( + Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'), + ), + ).toMatchSnapshot(); + }); +}); From 043b1ccae3a89baccba0321c979f3ecfd16b5377 Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Tue, 14 Jan 2025 16:14:19 +0100 Subject: [PATCH 4/9] fix: autogenerate --- src/SopsSync.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SopsSync.ts b/src/SopsSync.ts index d0fabe64..fcffb5aa 100644 --- a/src/SopsSync.ts +++ b/src/SopsSync.ts @@ -341,7 +341,12 @@ export class SopsSync extends Construct { } } - if (provider.role !== undefined && !props.autoGenerateIamPermissions) { + if ( + // Is allways true, but to satisfy TS we check explicitly + provider.role !== undefined && + // Check if user has disabled automatic generation + props.autoGenerateIamPermissions !== false + ) { Permissions.sopsKeys(this, { userDefinedKeys: props.sopsKmsKey, role: provider.role, From 83cf35e3abd015d4e94bed4d4911c1f556d1ce90 Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Tue, 14 Jan 2025 18:15:33 +0100 Subject: [PATCH 5/9] fix: init s3Api --- lambda/main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lambda/main.go b/lambda/main.go index 69bc408f..95ca977d 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -156,6 +156,9 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy attr, err := a.s3Api.GetObjectAttributes(&s3.GetObjectAttributesInput{ Bucket: &sopsFile.Bucket, Key: &sopsFile.Key, + ObjectAttributes: []*string{ + aws.String("ETag"), + }, }) encryptedContent, err = a.getS3FileContent(sopsFile) if err != nil { @@ -347,6 +350,7 @@ func handleRequest(ctx context.Context, event cfn.Event) (physicalResourceID str secretsmanager: secretsmanager.New(awsSession), ssm: ssm.New(awsSession), s3Downloader: s3manager.NewDownloader(awsSession), + s3Api: s3.New(awsSession), } return a.syncSopsToSecretsmanager(ctx, event) } From 8c030c50e51137958ccb809319dbec6fab94ac64 Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Wed, 15 Jan 2025 10:00:01 +0100 Subject: [PATCH 6/9] fix: remove CreationType everywhere --- lambda/events/event_create_s3_secret_env_as_json_simple.json | 1 - lambda/events/event_create_s3_secret_env_simple.json | 1 - lambda/events/event_create_s3_secret_json_complex.json | 1 - lambda/events/event_create_s3_secret_json_complex_flat.json | 1 - lambda/events/event_create_s3_secret_json_complex_stringify.json | 1 - lambda/events/event_create_s3_secret_json_simple.json | 1 - lambda/events/event_create_s3_secret_raw_simple.json | 1 - lambda/events/event_create_s3_secret_yaml_as_json_complex.json | 1 - .../events/event_create_s3_secret_yaml_as_json_complex_flat.json | 1 - lambda/events/event_create_s3_secret_yaml_as_json_simple.json | 1 - lambda/events/event_create_s3_secret_yaml_complex.json | 1 - lambda/events/event_create_s3_secret_yaml_complex_flat.json | 1 - lambda/events/event_create_s3_secret_yaml_simple.json | 1 - lambda/main.go | 1 - 14 files changed, 14 deletions(-) diff --git a/lambda/events/event_create_s3_secret_env_as_json_simple.json b/lambda/events/event_create_s3_secret_env_as_json_simple.json index ba1dc2a5..bc276b4c 100644 --- a/lambda/events/event_create_s3_secret_env_as_json_simple.json +++ b/lambda/events/event_create_s3_secret_env_as_json_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_env_simple.json b/lambda/events/event_create_s3_secret_env_simple.json index f39d3d8c..9b1baf91 100644 --- a/lambda/events/event_create_s3_secret_env_simple.json +++ b/lambda/events/event_create_s3_secret_env_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_json_complex.json b/lambda/events/event_create_s3_secret_json_complex.json index 74f08b32..a94df6b3 100644 --- a/lambda/events/event_create_s3_secret_json_complex.json +++ b/lambda/events/event_create_s3_secret_json_complex.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_json_complex_flat.json b/lambda/events/event_create_s3_secret_json_complex_flat.json index d579f7ae..5a7286bc 100644 --- a/lambda/events/event_create_s3_secret_json_complex_flat.json +++ b/lambda/events/event_create_s3_secret_json_complex_flat.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_json_complex_stringify.json b/lambda/events/event_create_s3_secret_json_complex_stringify.json index 533eb648..b08f818d 100644 --- a/lambda/events/event_create_s3_secret_json_complex_stringify.json +++ b/lambda/events/event_create_s3_secret_json_complex_stringify.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_json_simple.json b/lambda/events/event_create_s3_secret_json_simple.json index 4f423508..5d657957 100644 --- a/lambda/events/event_create_s3_secret_json_simple.json +++ b/lambda/events/event_create_s3_secret_json_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_raw_simple.json b/lambda/events/event_create_s3_secret_raw_simple.json index 8c3a1b85..8f85ba91 100644 --- a/lambda/events/event_create_s3_secret_raw_simple.json +++ b/lambda/events/event_create_s3_secret_raw_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_complex.json b/lambda/events/event_create_s3_secret_yaml_as_json_complex.json index 9e44b39c..48801dad 100644 --- a/lambda/events/event_create_s3_secret_yaml_as_json_complex.json +++ b/lambda/events/event_create_s3_secret_yaml_as_json_complex.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json b/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json index 9e88f100..995f00ed 100644 --- a/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json +++ b/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_simple.json b/lambda/events/event_create_s3_secret_yaml_as_json_simple.json index d43d869f..02420ea7 100644 --- a/lambda/events/event_create_s3_secret_yaml_as_json_simple.json +++ b/lambda/events/event_create_s3_secret_yaml_as_json_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_complex.json b/lambda/events/event_create_s3_secret_yaml_complex.json index 4ee77841..a2e0403d 100644 --- a/lambda/events/event_create_s3_secret_yaml_complex.json +++ b/lambda/events/event_create_s3_secret_yaml_complex.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_complex_flat.json b/lambda/events/event_create_s3_secret_yaml_complex_flat.json index 7e4ad675..d7d8cc78 100644 --- a/lambda/events/event_create_s3_secret_yaml_complex_flat.json +++ b/lambda/events/event_create_s3_secret_yaml_complex_flat.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/events/event_create_s3_secret_yaml_simple.json b/lambda/events/event_create_s3_secret_yaml_simple.json index 6d82161d..8fecbe62 100644 --- a/lambda/events/event_create_s3_secret_yaml_simple.json +++ b/lambda/events/event_create_s3_secret_yaml_simple.json @@ -3,7 +3,6 @@ "LogicalResourceId": "LogicalResourceId", "ResourceProperties": { "ResourceType": "SECRET", - "CreationType": "SINGLE", "FlattenSeparator": ".", "SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret", "SopsS3File": { diff --git a/lambda/main.go b/lambda/main.go index 95ca977d..1fe2285a 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -51,7 +51,6 @@ type SopsSyncResourcePropertys struct { FlattenSeparator string `json:"FlattenSeparator,omitempty"` ParameterKeyPrefix string `json:"ParameterKeyPrefix,omitempty"` StringifyValues string `json:"StringifyValues,omitempty"` - CreationType string `json:"CreationType,omitempty"` ResourceType string `json:"ResourceType,omitempty"` } From aaa02a27b0bafdca3da96f63caaae2b1cc8fe21f Mon Sep 17 00:00:00 2001 From: lennartrommeiss Date: Wed, 15 Jan 2025 13:49:50 +0100 Subject: [PATCH 7/9] fix: contribution guide, error messages and sha1sum Signed-off-by: lennartrommeiss --- CONTRIBUTING.md | 24 ++++++++++++++++++- lambda/main.go | 52 +++++++++++++++++++++-------------------- scripts/lambda-build.sh | 4 ++-- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ace1023..44316f21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,26 @@ Thanks for your interest in our project. Contributions are welcome. Feel free to [open an issue](issues) with questions or reporting ideas and bugs, or [open pull requests](pulls) to contribute code. -We are committed to fostering a welcoming, respectful, and harassment-free environment. Be kind! \ No newline at end of file +We are committed to fostering a welcoming, respectful, and harassment-free environment. Be kind! + +## How to buidl/deploy local + +Install all necessary tools with `yarn install` and others manually like `go` + +Build the go Lambda code: +``` +./scripts/build.sh +``` +Build the package (for CDK development only the first `js` build has to complete): +``` +yarn projen build +``` +Link the package: +``` +yarn link +``` +Switch to the path/project where you would like to use cdk-sops-secrets. \ +Link the package to your local build source: +``` +yarn link "cdk-sops-secrets" +``` \ No newline at end of file diff --git a/lambda/main.go b/lambda/main.go index 1fe2285a..5ab7fd30 100644 --- a/lambda/main.go +++ b/lambda/main.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/json" - "errors" "fmt" "log" "reflect" @@ -69,7 +68,7 @@ func (a AWS) getS3FileContent(file SopsS3File) (data []byte, err error) { Key: &file.Key, }) if err != nil { - return nil, errors.New(fmt.Sprintf("S3 download error:\n%v\n", err)) + return nil, fmt.Errorf("S3 download error:\n%v", err) } log.Printf("Downloaded %d bytes", resp) return buf.Bytes(), nil @@ -79,7 +78,7 @@ func decryptSopsFileContent(content []byte, format string) (data []byte, err err log.Printf("Decrypting content with format %s\n", format) resp, err := decrypt.Data(content, format) if err != nil { - return nil, errors.New(fmt.Sprintf("Decryption error:\n%v\n", err)) + return nil, fmt.Errorf("decryption error:\n%v", err) } log.Println("Decrypted") return resp, nil @@ -94,7 +93,7 @@ func (a AWS) updateSecret(sopsHash string, secretArn string, secretContent []byt } secretResp, secretErr := a.secretsmanager.PutSecretValue(input) if secretErr != nil { - return nil, errors.New(fmt.Sprintf("Failed to store secret value:\nsecretArn: %s\nClientRequestToken: %s\n%v\n", secretArn, sopsHash, secretErr)) + return nil, fmt.Errorf("failed to store secret value:\nsecretArn: %s\nClientRequestToken: %s\n%v", secretArn, sopsHash, secretErr) } arn := generatePhysicalResourceId(*secretResp.ARN) secretResp.ARN = &arn @@ -113,7 +112,7 @@ func (a AWS) updateSSMParameter(parameterName string, parameterContent []byte, k } paramResp, paramErr := a.ssm.PutParameter(input) if paramErr != nil { - return nil, errors.New(fmt.Sprintf("Failed to store parameter value:\nparameterName: %s\n%v\n", parameterName, paramErr)) + return nil, fmt.Errorf("failed to store parameter value:\nparameterName: %s\n%v", parameterName, paramErr) } log.Printf("Successfully stored parameter:\n%v\n", paramResp) return paramResp, nil @@ -159,12 +158,15 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy aws.String("ETag"), }, }) + if err != nil { + return tempArn, nil, fmt.Errorf("error while getting S3 object attributes:\n%v", err) + } encryptedContent, err = a.getS3FileContent(sopsFile) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("error while getting S3 file content:\n%v", err) } if attr.ETag == nil { - return tempArn, nil, errors.New("No ETag checksum found in S3 object") + return tempArn, nil, fmt.Errorf("no ETag checksum found in S3 object:\n%v", err) } sopsHash = *attr.ETag } @@ -179,15 +181,15 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy } if encryptedContent == nil { - return tempArn, nil, errors.New("No encrypted content found! Did you pass SopsS3File or SopsInline?") + return tempArn, nil, fmt.Errorf("no encrypted content found! Did you pass SopsS3File or SopsInline:\n%v", err) } if sopsHash == "" { - return tempArn, nil, errors.New("No sopsHash found! Did you pass SopsS3File or SopsInline?") + return tempArn, nil, fmt.Errorf("no sopsHash found! Did you pass SopsS3File or SopsInline:\n%v", err) } decryptedContent, err := decryptSopsFileContent(encryptedContent, resourceProperties.Format) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("error while decrypting file content\n%v", err) } var decryptedInterface interface{} switch resourceProperties.Format { @@ -195,14 +197,14 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy { err := json.Unmarshal(decryptedContent, &decryptedInterface) if err != nil { - return tempArn, nil, fmt.Errorf("Failed to parse json content: %v", err) + return tempArn, nil, fmt.Errorf("failed to parse json content:\n%v", err) } } case "yaml": { err := yaml.Unmarshal(decryptedContent, &decryptedInterface) if err != nil { - return tempArn, nil, fmt.Errorf("Failed to parse yaml content: %v", err) + return tempArn, nil, fmt.Errorf("failed to parse yaml content:\n%v", err) } } case "dotenv": @@ -230,14 +232,14 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy resourceProperties.ConvertToJSON = "false" } default: - return "", nil, errors.New(fmt.Sprintf("Format %s not supported", resourceProperties.Format)) + return "", nil, fmt.Errorf("format %s not supported", resourceProperties.Format) } if resourceProperties.Flatten == "" { resourceProperties.Flatten = "true" } resourcePropertiesFlatten, err := strconv.ParseBool(resourceProperties.Flatten) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err) } var finalInterface interface{} @@ -246,7 +248,7 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy flattenedInterface := make(map[string]interface{}) err := flatten("", decryptedInterface, flattenedInterface, resourceProperties.FlattenSeparator) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to flatten:\n%v", err) } finalInterface = flattenedInterface } else { @@ -258,12 +260,12 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy } resourcePropertiesStringifyValues, err := strconv.ParseBool(resourceProperties.StringifyValues) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err) } if resourcePropertiesStringifyValues { finalInterface, _, err = stringifyValues(finalInterface) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to stringify values:\n%v", err) } } @@ -272,24 +274,24 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy } resourcePropertieConvertToJSON, err := strconv.ParseBool(resourceProperties.ConvertToJSON) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err) } if resourcePropertieConvertToJSON || resourceProperties.Format == "json" { decryptedContent, err = toJSON(finalInterface) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to convert to JSON:\n%v", err) } } else if resourceProperties.Format == "yaml" { decryptedContent, err = toYAML(finalInterface) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to convert to YAML:\n%v", err) } } if resourceProperties.ResourceType == "SECRET" { updateSecretResp, err := a.updateSecret(sopsHash, resourceProperties.SecretARN, decryptedContent) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to update secret:\n%v", err) } returnData := make(map[string]interface{}) returnData["ARN"] = *updateSecretResp.ARN @@ -314,7 +316,7 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy } _, err := a.updateSSMParameter(strKey, []byte(strValue), resourceProperties.EncryptionKey) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to update ssm parameter:\n%v", err) } // A returnData map for each parameter is not created, because it would limit the number of possible parameters unnecessarily } @@ -325,7 +327,7 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy log.Printf("Patching single string parameter") response, err := a.updateSSMParameter(resourceProperties.ParameterName, decryptedContent, resourceProperties.EncryptionKey) if err != nil { - return tempArn, nil, err + return tempArn, nil, fmt.Errorf("failed to update ssm parameter:\n%v", err) } returnData := make(map[string]interface{}) returnData["ParameterName"] = resourceProperties.ParameterName @@ -334,12 +336,12 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy return tempArn, returnData, nil } else { // Should never happen ... - return tempArn, nil, errors.New("Neither SecretARN nor ParameterName is provided") + return tempArn, nil, fmt.Errorf("neither SecretARN nor ParameterName is provided:\n%v", err) } } else if event.RequestType == cfn.RequestDelete { return "", nil, nil } else { - return "", nil, errors.New(fmt.Sprintf("RequestType '%s' not supported", event.RequestType)) + return "", nil, fmt.Errorf("requestType '%s' not supported", event.RequestType) } } diff --git a/scripts/lambda-build.sh b/scripts/lambda-build.sh index 7f46f771..06fda667 100755 --- a/scripts/lambda-build.sh +++ b/scripts/lambda-build.sh @@ -8,8 +8,8 @@ export GOPROXY=https://proxy.golang.org,direct export CGO_ENABLED=0 go build -trimpath -buildvcs=false -tags lambda.norpc -o bootstrap -ldflags="-s -w -buildid=" ls -la bootstrap -shasum bootstrap +sha1sum bootstrap touch -t 202002020000 bootstrap chmod 755 bootstrap ls -la bootstrap -shasum bootstrap +sha1sum bootstrap From e39c0ed2c9a911df592bcc16cd8e257e0f141f60 Mon Sep 17 00:00:00 2001 From: Markus Siebert Date: Wed, 15 Jan 2025 15:39:27 +0100 Subject: [PATCH 8/9] fix: tests --- .../handler_parameter_raw_test.snap | 3 ++- .../handler_parameter_yaml_multi_test.snap | 6 ++++-- .../handler_secret_env_test.snap | 6 ++++-- .../handler_secret_json_test.snap | 12 +++++++---- .../handler_secret_raw_test.snap | 3 ++- .../handler_secret_yaml_test.snap | 21 ++++++++++++------- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/lambda/__snapshots__/handler_parameter_raw_test.snap b/lambda/__snapshots__/handler_parameter_raw_test.snap index cf7705da..46007613 100755 --- a/lambda/__snapshots__/handler_parameter_raw_test.snap +++ b/lambda/__snapshots__/handler_parameter_raw_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/binary/sopsfile.enc-age.binary" + Key: "../test-secrets/binary/sopsfile.enc-age.binary", + ObjectAttributes: ["ETag"] } --- diff --git a/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap b/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap index b33091f7..8826756e 100755 --- a/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap +++ b/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -65,7 +66,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- diff --git a/lambda/__snapshots__/handler_secret_env_test.snap b/lambda/__snapshots__/handler_secret_env_test.snap index 20b065d1..0488553e 100755 --- a/lambda/__snapshots__/handler_secret_env_test.snap +++ b/lambda/__snapshots__/handler_secret_env_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/dotenv/encrypted-best-secret.env" + Key: "../test-secrets/dotenv/encrypted-best-secret.env", + ObjectAttributes: ["ETag"] } --- @@ -46,7 +47,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/dotenv/encrypted-best-secret.env" + Key: "../test-secrets/dotenv/encrypted-best-secret.env", + ObjectAttributes: ["ETag"] } --- diff --git a/lambda/__snapshots__/handler_secret_json_test.snap b/lambda/__snapshots__/handler_secret_json_test.snap index bbe043fc..83ca106c 100755 --- a/lambda/__snapshots__/handler_secret_json_test.snap +++ b/lambda/__snapshots__/handler_secret_json_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/json/sopsfile.enc-age.json" + Key: "../test-secrets/json/sopsfile.enc-age.json", + ObjectAttributes: ["ETag"] } --- @@ -46,7 +47,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/json/sopsfile-complex.enc-age.json" + Key: "../test-secrets/json/sopsfile-complex.enc-age.json", + ObjectAttributes: ["ETag"] } --- @@ -89,7 +91,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/json/sopsfile-complex.enc-age.json" + Key: "../test-secrets/json/sopsfile-complex.enc-age.json", + ObjectAttributes: ["ETag"] } --- @@ -132,7 +135,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/json/sopsfile-complex.enc-age.json" + Key: "../test-secrets/json/sopsfile-complex.enc-age.json", + ObjectAttributes: ["ETag"] } --- diff --git a/lambda/__snapshots__/handler_secret_raw_test.snap b/lambda/__snapshots__/handler_secret_raw_test.snap index 78323e57..674c2da6 100755 --- a/lambda/__snapshots__/handler_secret_raw_test.snap +++ b/lambda/__snapshots__/handler_secret_raw_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/binary/sopsfile.enc-age.binary" + Key: "../test-secrets/binary/sopsfile.enc-age.binary", + ObjectAttributes: ["ETag"] } --- diff --git a/lambda/__snapshots__/handler_secret_yaml_test.snap b/lambda/__snapshots__/handler_secret_yaml_test.snap index 72c49a19..9e19453e 100755 --- a/lambda/__snapshots__/handler_secret_yaml_test.snap +++ b/lambda/__snapshots__/handler_secret_yaml_test.snap @@ -3,7 +3,8 @@ >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -46,7 +47,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -89,7 +91,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -132,7 +135,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -175,7 +179,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -218,7 +223,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- @@ -342,7 +348,8 @@ nil >>>S3ApiMockClient.GetObjectAttributes.Input { Bucket: "..", - Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml" + Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml", + ObjectAttributes: ["ETag"] } --- From 7a573212f96e41e88f609a0aaa0c9b2ccc51b001 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 15 Jan 2025 14:46:21 +0000 Subject: [PATCH 9/9] chore: self mutation Signed-off-by: github-actions --- .../SecretIntegrationAsset.assets.json | 10 +++++----- .../SecretIntegrationAsset.template.json | 2 +- .../SecretIntegrationInline.assets.json | 10 +++++----- .../SecretIntegrationInline.template.json | 2 +- .../SecretIntegrationAsset.assets.json | 10 +++++----- .../SecretIntegrationAsset.template.json | 2 +- .../SecretMultiKms.assets.json | 10 +++++----- .../SecretMultiKms.template.json | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json index 7e334d13..c412a32a 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { + "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": { "source": { - "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -79,7 +79,7 @@ } } }, - "76ae6b126c78a065078fae6c04e4062fb434dabe184b6f01cdff5d7c8b4b9610": { + "403db9f2066ea2fc6a72ec5211cda6589c0049fadd0dd5ea36461a810960d709": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -87,7 +87,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "76ae6b126c78a065078fae6c04e4062fb434dabe184b6f01cdff5d7c8b4b9610.json", + "objectKey": "403db9f2066ea2fc6a72ec5211cda6589c0049fadd0dd5ea36461a810960d709.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json index 0f1ea581..410ea70d 100644 --- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json @@ -231,7 +231,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" + "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip" }, "Environment": { "Variables": { diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json index 4ff13f03..cdd252e3 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { + "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": { "source": { - "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "0c5708f02afd19879e2de4293a656e79dc61639b3e2eb0a318efc0984b4f6cf2": { + "55aa34bad424010bf095d40fbfbbd4b7250f7c373c25a117c203aa788214e7a3": { "source": { "path": "SecretIntegrationInline.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "0c5708f02afd19879e2de4293a656e79dc61639b3e2eb0a318efc0984b4f6cf2.json", + "objectKey": "55aa34bad424010bf095d40fbfbbd4b7250f7c373c25a117c203aa788214e7a3.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json index 6742f3c1..827a6514 100644 --- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json +++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json @@ -198,7 +198,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" + "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip" }, "Environment": { "Variables": { diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json index 9011900d..d52b2917 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json @@ -1,20 +1,20 @@ { "version": "36.0.0", "files": { - "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { + "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": { "source": { - "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "97fd60ebfa8440e4000cef3de6f7fe157f40d4d8f917942aa0ee7e4f229b489d": { + "d991538757fc85d0c7183933598d0517ea224a4d379c5cc40388ef064f255f29": { "source": { "path": "SecretIntegrationAsset.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "97fd60ebfa8440e4000cef3de6f7fe157f40d4d8f917942aa0ee7e4f229b489d.json", + "objectKey": "d991538757fc85d0c7183933598d0517ea224a4d379c5cc40388ef064f255f29.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json index 787b1cb4..32e01b29 100644 --- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json +++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json @@ -72,7 +72,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" + "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip" }, "Environment": { "Variables": { diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json index cfa2a38b..86fe4c53 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json @@ -1,15 +1,15 @@ { "version": "36.0.0", "files": { - "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2": { + "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": { "source": { - "path": "asset.bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip", + "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -27,7 +27,7 @@ } } }, - "e30d5ef78da253f697f3bf0aaf72550fceb9a9eefae9469f6eb7008380f429cb": { + "6bf23bfa8222ed50eef10cf00cfb221343b0f056159410f55f8a28dc7c92b9b6": { "source": { "path": "SecretMultiKms.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e30d5ef78da253f697f3bf0aaf72550fceb9a9eefae9469f6eb7008380f429cb.json", + "objectKey": "6bf23bfa8222ed50eef10cf00cfb221343b0f056159410f55f8a28dc7c92b9b6.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json index e5b70937..1b9cd2fb 100644 --- a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json +++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json @@ -336,7 +336,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "S3Key": "bb705793a0bd8690a29d62191cd5ca3e4e0ebf81a9bf0a2677e8a30f841cafa2.zip" + "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip" }, "Environment": { "Variables": {