Skip to content

Commit

Permalink
apply comments
Browse files Browse the repository at this point in the history
  • Loading branch information
moelasmar committed Sep 18, 2024
1 parent e84a533 commit 7205d4c
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 38 deletions.
7 changes: 0 additions & 7 deletions lib/__tests__/test-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,6 @@ export class TestStack extends Stack {
bumpCommand: 'npm i && npm run bump',
});

//
// AUTO-BUILD

pipeline.autoBuild({
publicLogs: true,
});

//
// CHANGE CONTROL
//
Expand Down
3 changes: 3 additions & 0 deletions lib/code-signing/certificate-signing-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ export class CertificateSigningRequest extends Construct {
buildArgs: {
FUN_SRC_DIR: 'certificate-signing-request',
},
invalidation: {
buildArgs: true,
},
}),
timeout: Duration.seconds(300),
});
Expand Down
43 changes: 40 additions & 3 deletions lib/code-signing/code-signing-certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
aws_s3 as s3,
aws_secretsmanager as secretsManager,
aws_ssm as ssm,
ArnFormat,
} from 'aws-cdk-lib';
import { Construct, IConstruct } from 'constructs';
import { CertificateSigningRequest, DistinguishedName } from './certificate-signing-request';
Expand Down Expand Up @@ -125,12 +126,48 @@ export class CodeSigningCertificate extends Construct implements ICodeSigningCer
description: 'The PEM-encoded private key of the x509 Code-Signing Certificate',
keySize: props.rsaKeySize || 2048,
secretEncryptionKey: props.secretEncryptionKey,
// rename the secret name, as it will be deleted wile the previous custom resource got deleted,
// and so when the new custom resource got created, it will fail as it will try to create a new secret
// with same name
// rename the secret name, as since this resource will be deleted and create a new resource,
// so the new resource will be created before the old one got deleted, and so we will not be able
// to create a new secrete with the same name, and even we could not reuse it, as it will be deleted once
// the old resource got deleted.
secretName: `${baseName}/RSAPrivateKeyV2`,
});

// this change to keep the permissions to access the old secret for the custom resource Lambda function role, so it can
// delete the old secret.
const oldSecretArnLike = Stack.of(this).formatArn({
service: 'secretsmanager',
resource: 'secret',
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
// The ARN of a secret has "-" followed by 6 random characters appended at the end
resourceName: `${baseName}/RSAPrivateKey-??????`,
});
privateKey.customResource.addToRolePolicy(new iam.PolicyStatement({
actions: [
'secretsmanager:CreateSecret',
'secretsmanager:DeleteSecret',
'secretsmanager:UpdateSecret',
],
resources: [oldSecretArnLike],
}));

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(privateKey.customResource.role!.roleArn)],
actions: ['kms:Decrypt', 'kms:GenerateDataKey'],
resources: ['*'],
conditions: {
StringEquals: {
'kms:ViaService': `secretsmanager.${Stack.of(this).region}.amazonaws.com`,
},
ArnLike: {
'kms:EncryptionContext:SecretARN': oldSecretArnLike,
},
},
}));
}

this.credential = secretsManager.Secret.fromSecretAttributes(this, 'Credential', {
encryptionKey: props.secretEncryptionKey,
secretCompleteArn: privateKey.secretArn,
Expand Down
18 changes: 11 additions & 7 deletions lib/code-signing/private-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class RsaPrivateKeySecret extends Construct {
* The ARN of the secret that holds the private key.
*/
public secretArn: string;
public customResource: lambda.SingletonFunction;

private secretArnLike: string;
private masterKey?: kms.IKey;
Expand All @@ -65,7 +66,7 @@ export class RsaPrivateKeySecret extends Construct {

const codeLocation = path.resolve(__dirname, '..', 'custom-resource-handlers');
// change the resource id to force deleting existing function, and create new one, as Package type change is not allowed
const customResource = new lambda.SingletonFunction(this, 'ResourceHandlerV2', {
this.customResource = new lambda.SingletonFunction(this, 'ResourceHandlerV2', {
lambdaPurpose: 'RSAPrivate-Key',
// change the uuid to force deleting existing function, and create new one, as Package type change is not allowed
uuid: '517D342F-A590-447B-B525-5D06E403A406',
Expand All @@ -78,6 +79,9 @@ export class RsaPrivateKeySecret extends Construct {
buildArgs: {
FUN_SRC_DIR: 'private-key',
},
invalidation: {
buildArgs: true,
},
}),
timeout: Duration.seconds(300),
});
Expand All @@ -89,7 +93,7 @@ export class RsaPrivateKeySecret extends Construct {
// The ARN of a secret has "-" followed by 6 random characters appended at the end
resourceName: `${props.secretName}-??????`,
});
customResource.addToRolePolicy(new iam.PolicyStatement({
this.customResource.addToRolePolicy(new iam.PolicyStatement({
actions: [
'secretsmanager:CreateSecret',
'secretsmanager:DeleteSecret',
Expand All @@ -101,7 +105,7 @@ export class RsaPrivateKeySecret extends Construct {
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)],
principals: [new iam.ArnPrincipal(this.customResource.role!.roleArn)],
actions: ['kms:Decrypt', 'kms:GenerateDataKey'],
resources: ['*'],
conditions: {
Expand All @@ -117,7 +121,7 @@ export class RsaPrivateKeySecret extends Construct {

//change the custom resource id to force recreating new one because the change of the underneath lambda function
const privateKey = new CustomResource(this, 'ResourceV2', {
serviceToken: customResource.functionArn,
serviceToken: this.customResource.functionArn,
resourceType: 'Custom::RsaPrivateKeySecret',
pascalCaseProperties: true,
properties: {
Expand All @@ -129,13 +133,13 @@ export class RsaPrivateKeySecret extends Construct {
},
removalPolicy: props.removalPolicy || RemovalPolicy.RETAIN,
});
if (customResource.role) {
privateKey.node.addDependency(customResource.role);
if (this.customResource.role) {
privateKey.node.addDependency(this.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],
roles: [this.customResource.role],
statements: [
new iam.PolicyStatement({
// description: `AWSSecretsManager${props.secretName.replace(/[^0-9A-Za-z]/g, '')}CMK`,
Expand Down
2 changes: 2 additions & 0 deletions lib/custom-resource-handlers/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Use a NodeJS 20.x runtime
FROM public.ecr.aws/lambda/nodejs:20-x86_64

ARG FUN_SRC_DIR

# install openssel
RUN dnf install -y openssl
ENV LD_LIBRARY_PATH=""
Expand Down
14 changes: 1 addition & 13 deletions lib/custom-resource-handlers/src/pgp-secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ async function handleEvent(event: cfn.Event, context: lambda.Context): Promise<c
});
}

let newKey = event.RequestType === cfn.RequestType.CREATE ?
! event.ResourceProperties.ReuseSecret || ! await _doesSecretExist(event.ResourceProperties.SecretName) : false;
let newKey = event.RequestType === cfn.RequestType.CREATE;

if (event.RequestType === cfn.RequestType.UPDATE) {
const oldProps = event.OldResourceProperties;
Expand All @@ -75,17 +74,6 @@ async function handleEvent(event: cfn.Event, context: lambda.Context): Promise<c
}
}

async function _doesSecretExist(secretName: string): Promise<boolean> {
try {
await secretsManager.getSecretValue({ SecretId: secretName });
} catch (e: any) {
if (e.name === 'ResourceNotFoundException') {
return false;
}
}
return true;
}

async function _createNewKey(event: cfn.CreateEvent | cfn.UpdateEvent, context: lambda.Context): Promise<ResourceAttributes> {
const passPhrase = crypto.randomBytes(32).toString('base64');
const tempDir = await mkdtemp(path.join(os.tmpdir(), 'OpenPGP-'));
Expand Down
11 changes: 3 additions & 8 deletions lib/open-pgp-key-pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,6 @@ interface OpenPGPKeyPairProps {
*/
secretName: string;

/**
* Use this flag to mention to reuse an existing secret to be updated even during the resource create event.
*
* @default false
*/
reuseSecret?: boolean;

/**
* Name of SSM parameter to store public key
*/
Expand Down Expand Up @@ -132,6 +125,9 @@ export class OpenPGPKeyPair extends Construct implements ICredentialPair {
buildArgs: {
FUN_SRC_DIR: 'pgp-secret',
},
invalidation: {
buildArgs: true,
},
}),
handler: lambda.Handler.FROM_IMAGE,
timeout: Duration.seconds(300),
Expand Down Expand Up @@ -183,7 +179,6 @@ export class OpenPGPKeyPair extends Construct implements ICredentialPair {
expiry: props.expiry,
keySizeBits: props.keySizeBits,
secretName: props.secretName,
reuseSecret: props.reuseSecret ?? false,
keyArn: props.encryptionKey && props.encryptionKey.keyArn,
version: props.version,
description: props.description,
Expand Down

0 comments on commit 7205d4c

Please sign in to comment.