in lib/code-signing/private-key.ts [61:150]
constructor(parent: Construct, id: string, props: RsaPrivateKeySecretProps) {
super(parent, id);
const codeLocation = path.resolve(__dirname, '..', 'custom-resource-handlers', 'bin', 'private-key');
const customResource = new lambda.SingletonFunction(this, 'ResourceHandler', {
lambdaPurpose: 'RSAPrivate-Key',
uuid: '72FD327D-3813-4632-9340-28EC437AA486',
description: 'Generates an RSA Private Key and stores it in AWS Secrets Manager',
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
code: new lambda.AssetCode(codeLocation),
timeout: Duration.seconds(300),
// add the layer that contains the OpenSSL CLI binary
layers: [new lambda.LayerVersion(this, 'OpenSslCliLayer', {
code: lambda.Code.fromAsset(path.join(__dirname, '..', 'custom-resource-handlers', 'layers', 'openssl-cli-layer.zip')),
})],
});
this.secretArnLike = Stack.of(this).formatArn({
service: 'secretsmanager',
resource: 'secret',
sep: ':',
// The ARN of a secret has "-" followed by 6 random characters appended at the end
resourceName: `${props.secretName}-??????`,
});
customResource.addToRolePolicy(new iam.PolicyStatement({
actions: [
'secretsmanager:CreateSecret',
'secretsmanager:DeleteSecret',
'secretsmanager:UpdateSecret',
],
resources: [this.secretArnLike],
}));
if (props.secretEncryptionKey) {
props.secretEncryptionKey.addToResourcePolicy(new iam.PolicyStatement({
// description: `Allow use via AWS Secrets Manager by CustomResource handler ${customResource.functionName}`,
principals: [new iam.ArnPrincipal(customResource.role!.roleArn)],
actions: ['kms:Decrypt', 'kms:GenerateDataKey'],
resources: ['*'],
conditions: {
StringEquals: {
'kms:ViaService': `secretsmanager.${Stack.of(this).region}.amazonaws.com`,
},
ArnLike: {
'kms:EncryptionContext:SecretARN': this.secretArnLike,
},
},
}));
}
const privateKey = new cfn.CustomResource(this, 'Resource', {
provider: cfn.CustomResourceProvider.lambda(customResource),
resourceType: 'Custom::RsaPrivateKeySecret',
properties: {
resourceVersion: hashFileOrDirectory(codeLocation),
description: props.description,
keySize: props.keySize,
secretName: props.secretName,
kmsKeyId: props.secretEncryptionKey && props.secretEncryptionKey.keyArn,
},
removalPolicy: props.removalPolicy || RemovalPolicy.RETAIN,
});
if (customResource.role) {
privateKey.node.addDependency(customResource.role);
if (props.secretEncryptionKey) {
// Modeling as a separate Policy to evade a dependency cycle (Role -> Key -> Role), as the Key refers to the
// role in it's resource policy.
privateKey.node.addDependency(new iam.Policy(this, 'GrantLambdaRoleKeyAccess', {
roles: [customResource.role],
statements: [
new iam.PolicyStatement({
// description: `AWSSecretsManager${props.secretName.replace(/[^0-9A-Za-z]/g, '')}CMK`,
actions: ['kms:Decrypt', 'kms:GenerateDataKey'],
resources: [props.secretEncryptionKey.keyArn],
conditions: {
StringEquals: {
'kms:ViaService': `secretsmanager.${Stack.of(this).region}.amazonaws.com`,
},
StringLike: { 'kms:EncryptionContext:SecretARN': [this.secretArnLike, 'RequestToValidateKeyAccess'] },
},
}),
],
}));
}
}
this.masterKey = props.secretEncryptionKey;
this.secretArn = privateKey.getAtt('SecretArn').toString();
}