constructor()

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();
  }