aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.7k stars 3.93k forks source link

(custom resources): Can not get public key for a KMS key #19065

Open sashee opened 2 years ago

sashee commented 2 years ago

What is the problem?

I tried to extract the public key for an asymmetric KMS key but I get a Response is not valid JSON error.

Reproduction Steps

const key = new aws_kms.Key(
  this,
  "key",
  {
    keySpec: aws_kms.KeySpec.ECC_NIST_P384,
    keyUsage: aws_kms.KeyUsage.SIGN_VERIFY,
  }
);
const publicKey = new custom_resources.AwsCustomResource(
  this,
  "publicKey",
  {
    onCreate: {
      service: "KMS",
      action: "getPublicKey",
      parameters: {
        KeyId: key.keyArn,
      },
      physicalResourceId: custom_resources.PhysicalResourceId.of(key.keyArn),
      outputPaths: ['PublicKey'],
    },
    policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
      resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE
    }),
  }
);

What did you expect to happen?

I expected that the stack deploys and I can extract the public key.

What actually happened?

Response is not valid JSON

In the CloudWatch Logs, I see that the PublicKey is retrieved, but it seems in a unusable format:

image

CDK CLI Version

2.12.0

Framework Version

No response

Node.js Version

v16.14.0

OS

Ubuntu

Language

Typescript

Language Version

No response

Other information

No response

lacteolus commented 2 years ago

The same issue with CDK 2.20.0. Tried to create our own custom resource backed by custom lambda to get public key using boto3 but faced the problem described in https://github.com/aws-samples/aws-cdk-examples/discussions/641

peterwoodworth commented 2 years ago

I wonder if this is because the response is a blob?

skinny85 commented 2 years ago

Unfortunately, I'm not sure CDK can do much here - we don't really control wha the response from the services is.

github-actions[bot] commented 2 years ago

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

stefanopallicca-imagicle commented 2 years ago

Unfortunately, I'm not sure CDK can do much here - we don't really control wha the response from the services is.

Maybe it's a matter of reformatting (I'm sorry if the word is not the proper one) the command output to a sort of human readable?

After all, the aws kms get-public-key cli command output has the expected format:

image

skinny85 commented 2 years ago

The fact that you get that error message might also suggest that the response was an "Access Denied", which is not a JSON object.

Can you try temporarly giving the Custom Resource admin permissions, and see if that changes anything? (I guess also allow all principals from the account "kms:*" on the Key, just in case)

stefanopallicca-imagicle commented 2 years ago

@skinny85 thanks for the answer. I've tried what you asked for, but still receiving Response is not valid JSON

skinny85 commented 2 years ago

Hmm, I'm kind of lost then.

Maybe I would try to do a call using the JavaScript AWS SDK, and see what response it gives me there perhaps...?

johannes-idealayer commented 2 years ago

The SDK returns this object. Of particular interest is PublicKey, which is a Uint8Array (i.e. it's not a base64-encoded string).

skinny85 commented 2 years ago

Interesting! That's probably the source of the Response is not valid JSON error.

johannes-idealayer commented 2 years ago

Edit: This comment is in response to a now-deleted comment pointing out this line as a potential cause for the error:

const childKey = Buffer.isBuffer(child[key]) ? child[key].toString('utf8') : child[key];

I don't think this is what's causing the "not valid JSON" error, but I'd like to point out that decoding an arbitrary buffer with .toString('utf8') is potentially a lossy conversion. The code will try to decode the buffer as UTF-8, and any part of it that is not valid UTF-8 will be replaced by the Unicode replacement character � (U+FFFD), making it impossible to get the original buffer.

dmitridr commented 2 years ago

Had the same issue as everyone here.

The line @johannes-sscrc linked does seem to be related to the error. Indeed, Publickey seems to be in a binary format: DER, and the proper way to encode it appears to be base64 and not utf8.

Thus, a quick fix for me was to replace the above line with:

const childKey = Buffer.isBuffer(child[key]) ? child[key].toString('base64') : child[key];

Then the PublicKey is returned in base64, and there's no error anymore. Generally, it would be good to have a hook/param here to allow the caller to specify how to encode the buffer for specific keys, in order to unblock such issues in the general case.

l-irizarry commented 1 year ago

@sashee did you end up finding a work-around for getting the public key of a KMS key using a custom resource? I'm getting the same invalid JSON error.

sashee commented 1 year ago

@synthetic-luis , yeah, a custom resource can fetch the public key and output it for other resources. Unfortunately, I can't provide code example as it was done for a client, but the implementation was straightforward.

l-irizarry commented 1 year ago

@sashee thanks for the response. Can you please elaborate on how you then overcame the invalid JSON issue (in general terms) as it relates to the code you posted above? For example, did you have to change your code above (e.g. add a new parameter to the getPublicKey call?)

sashee commented 1 year ago

@synthetic-luis , I just checked the code and I remember wrongly. So I needed a keypair for IVS playback key and I thought to use KMS for that. It did not work (partly because of the issue here) so I implemented a lambda function that generates the key. Not the ideal solution, but it works reliably so far.

l-irizarry commented 1 year ago

@sashee this helps a lot. Thank you!

DanielLaberge commented 1 year ago

Would love to have a fix or workaround for this.

l-irizarry commented 1 year ago

@DanielLaberge if it helps, what worked for me was creating a post deploy script that uses the KMS SDK to do what I needed to do