diff --git a/API.md b/API.md
index fbb44db6..e6f455a9 100644
--- a/API.md
+++ b/API.md
@@ -1735,7 +1735,7 @@ const multiStringParameterProps: MultiStringParameterProps = { ... }
| type
| aws-cdk-lib.aws_ssm.ParameterType
| The type of the string parameter. |
| encryptionKey
| aws-cdk-lib.aws_kms.IKey
| *No description.* |
| keyPrefix
| string
| *No description.* |
-| keySeperator
| string
| *No description.* |
+| keySeparator
| string
| *No description.* |
---
@@ -2075,10 +2075,10 @@ public readonly keyPrefix: string;
---
-##### `keySeperator`Optional
+##### `keySeparator`Optional
```typescript
-public readonly keySeperator: string;
+public readonly keySeparator: string;
```
- *Type:* string
diff --git a/lambda/main.go b/lambda/main.go
index e415ae2d..5aaca1a6 100644
--- a/lambda/main.go
+++ b/lambda/main.go
@@ -298,7 +298,7 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
keys := v.MapKeys()
keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) }
sort.Slice(keys, keysOrder)
- for i, key := range keys {
+ for _, key := range keys {
strKey := resourceProperties.ParameterKeyPrefix + key.String()
log.Printf("Parameter: " + strKey)
value := v.MapIndex(key).Interface()
@@ -311,8 +311,10 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
if err != nil {
return tempArn, nil, err
}
- returnData[fmt.Sprintf("ParameterName[%v]", i)] = strKey
+ // 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 {
log.Printf("Patching single string parameter")
diff --git a/src/MultiStringParameter.ts b/src/MultiStringParameter.ts
index 143c3e24..cc7f886c 100644
--- a/src/MultiStringParameter.ts
+++ b/src/MultiStringParameter.ts
@@ -17,7 +17,7 @@ interface JSONObject {
}
export interface MultiStringParameterProps extends SopsStringParameterProps {
- readonly keySeperator?: string;
+ readonly keySeparator?: string;
readonly keyPrefix?: string;
}
@@ -67,7 +67,7 @@ export class MultiStringParameter extends Construct {
region: this.stack.region,
};
this.keyPrefix = props.keyPrefix ?? '/';
- this.keySeparator = props.keySeperator ?? '/';
+ this.keySeparator = props.keySeparator ?? '/';
const keys = this.parseFile(props.sopsFilePath!, this.keySeparator)
.filter((key) => !key.startsWith('sops'))
diff --git a/src/SopsSync.ts b/src/SopsSync.ts
index 95f70732..f072964c 100644
--- a/src/SopsSync.ts
+++ b/src/SopsSync.ts
@@ -9,7 +9,12 @@ import {
CustomResource,
FileSystem,
} from 'aws-cdk-lib';
-import { IGrantable, PolicyStatement } from 'aws-cdk-lib/aws-iam';
+import {
+ IGrantable,
+ IRole,
+ ManagedPolicy,
+ PolicyStatement,
+} from 'aws-cdk-lib/aws-iam';
import { IKey, Key } from 'aws-cdk-lib/aws-kms';
import { SingletonFunction, Code, Runtime } from 'aws-cdk-lib/aws-lambda';
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
@@ -342,16 +347,9 @@ export class SopsSync extends Construct {
props.encryptionKey?.grantEncryptDecrypt(provider);
}
if (props.parameterNames) {
- provider.addToRolePolicy(
- new PolicyStatement({
- actions: ['ssm:PutParameter'],
- resources: props.parameterNames.map(
- (param) =>
- `arn:aws:ssm:${Stack.of(this).region}:${
- Stack.of(this).account
- }:parameter${param.startsWith('/') ? param : `/${param}`}`,
- ),
- }),
+ this.createReducedParameterPolicy(
+ props.parameterNames,
+ provider.role,
);
props.encryptionKey?.grantEncryptDecrypt(provider);
}
@@ -429,4 +427,77 @@ export class SopsSync extends Construct {
});
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);
+ }
+ }
+ 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);
+ }
}
diff --git a/test-secrets/yaml/sopsfile-parameters-large.yaml b/test-secrets/yaml/sopsfile-parameters-large.yaml
new file mode 100644
index 00000000..99761eca
--- /dev/null
+++ b/test-secrets/yaml/sopsfile-parameters-large.yaml
@@ -0,0 +1,100 @@
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting1: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting2: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting3: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting4: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting5: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting6: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting7: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting8: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting9: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting10: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting11: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting12: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting13: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting14: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting15: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting16: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting17: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting18: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting19: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting20: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting21: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting22: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting23: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting24: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting25: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting26: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting27: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting28: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting29: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting30: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting31: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting32: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting33: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting34: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting35: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting36: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting37: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting38: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting39: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting40: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting41: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting42: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting43: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting44: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting45: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting46: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting47: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting48: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting49: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting50: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting51: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting52: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting53: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting54: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting55: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting56: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting57: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting58: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting59: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting60: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting61: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting62: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting63: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting64: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting65: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting66: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting67: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting68: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting69: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting70: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting71: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting72: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting73: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting74: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting75: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting76: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting77: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting78: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting79: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting80: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting81: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting82: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting83: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting84: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting85: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting86: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting87: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting88: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting89: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting90: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting91: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting92: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting93: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting94: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting95: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting96: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting97: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting98: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting99: value
+DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting100: value
diff --git a/test/secret.test.ts b/test/secret.test.ts
index 6f2a9f62..0c85fe9d 100644
--- a/test/secret.test.ts
+++ b/test/secret.test.ts
@@ -549,6 +549,24 @@ test('Multiple parameters from yaml file', () => {
});
template.hasResourceProperties('AWS::IAM::Policy', {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ 'kms:Decrypt',
+ 'kms:Encrypt',
+ 'kms:ReEncrypt*',
+ 'kms:GenerateDataKey*',
+ ],
+ Effect: 'Allow',
+ Resource:
+ 'arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
+ },
+ ],
+ },
+ });
+
+ template.hasResourceProperties('AWS::IAM::ManagedPolicy', {
PolicyDocument: {
Statement: [
{
@@ -581,17 +599,6 @@ test('Multiple parameters from yaml file', () => {
},
],
},
- {
- Action: [
- 'kms:Decrypt',
- 'kms:Encrypt',
- 'kms:ReEncrypt*',
- 'kms:GenerateDataKey*',
- ],
- Effect: 'Allow',
- Resource:
- 'arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
- },
],
},
});
@@ -604,7 +611,7 @@ test('Multiple parameters from yaml file with custom key structure', () => {
simpleName: false,
sopsFilePath: 'test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml',
keyPrefix: '_',
- keySeperator: '.',
+ keySeparator: '.',
encryptionKey: Key.fromKeyArn(
stack,
'Key',
@@ -623,6 +630,24 @@ test('Multiple parameters from yaml file with custom key structure', () => {
});
template.hasResourceProperties('AWS::IAM::Policy', {
+ PolicyDocument: {
+ Statement: [
+ {
+ Action: [
+ 'kms:Decrypt',
+ 'kms:Encrypt',
+ 'kms:ReEncrypt*',
+ 'kms:GenerateDataKey*',
+ ],
+ Effect: 'Allow',
+ Resource:
+ 'arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
+ },
+ ],
+ },
+ });
+
+ template.hasResourceProperties('AWS::IAM::ManagedPolicy', {
PolicyDocument: {
Statement: [
{
@@ -655,18 +680,31 @@ test('Multiple parameters from yaml file with custom key structure', () => {
},
],
},
- {
- Action: [
- 'kms:Decrypt',
- 'kms:Encrypt',
- 'kms:ReEncrypt*',
- 'kms:GenerateDataKey*',
- ],
- Effect: 'Allow',
- Resource:
- 'arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
- },
],
},
});
});
+
+test('Large set of parameters to split in multiple policies', () => {
+ const app = new App();
+ const stack = new Stack(app, 'ParameterIntegration');
+ new MultiStringParameter(stack, 'SopsSecret1', {
+ simpleName: false,
+ sopsFilePath: 'test-secrets/yaml/sopsfile-parameters-large.yaml',
+ keyPrefix: '_',
+ keySeparator: '.',
+ encryptionKey: Key.fromKeyArn(
+ stack,
+ 'Key',
+ 'arn:aws:kms:eu-central-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
+ ),
+ stringValue: ' ',
+ });
+ const template = Template.fromStack(stack);
+
+ template.hasResourceProperties('AWS::SSM::Parameter', {
+ Name: '_DefineSomeExtraExtraLongNameForParameterWhichHaveAExtraExtraLongNameToTestPolicySplitting1',
+ });
+
+ template.resourceCountIs('AWS::IAM::ManagedPolicy', 3);
+});