Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to specify config profile for role and region in @aws-crypto/client-node? #771

Closed
mildmojo opened this issue Oct 19, 2021 · 2 comments
Closed

Comments

@mildmojo
Copy link

mildmojo commented Oct 19, 2021

Problem:

Is this library a replacement for AWS.KMS from the original aws-sdk node package? I haven't been able to find detailed documentation (vs. the high-level docs), and the examples in this repo are incomplete (#398).

I attempted to use @aws-crypto/client-node, but quickly found that the examples don't work in my setup. I need to specify a profile from my ~/.aws/config file to load the proper role and region to access my KMS key, but I can't figure out how you'd do that with this library. Maybe the examples assume you're using environment variables?

My AWS config looks like:

# ~/.aws/config
[default]
region=us-east-2
output=json

[profile staging]
role_arn=arn:aws:iam::000000000000:role/StagingAccessRole
source_profile=default
region=us-east-1
# ~/.aws/credentials
[staging]
aws_access_key_id = <redacted>
aws_secret_access_key = <redacted>
aws_session_token = <redacted>

My working aws-sdk code looks like:

// test-aws-sdk.js

// Must set AWS_SDK_LOAD_CONFIG to get SDK to load profiles from config files.
process.env.AWS_SDK_LOAD_CONFIG = '1';
const aws = require('aws-sdk');

// (Redacted.)
const KEY_ID = 'arn:aws:kms:us-east-1:000000000000:key/00000000-0000-0000-0000-000000000000';

aws.config.credentials = new aws.SharedIniFileCredentials({profile: 'staging'});

// WORKAROUND: SDK does not load region from profile or key ARN. Must specify here.
const kms = new aws.KMS({
  apiVersion: '2014-11-01',
  region: 'us-east-1'
});

const params = {
  KeyId: KEY_ID,
  Plaintext: 'hello',
};

kms.encrypt(params, function(err, data) {
  if (err) {
    console.log(err, err.stack);
  } else {
    console.log(data);
    console.log(String(data.CiphertextBlob));
  }
});

My non-functional @aws-crypto/client-node attempt, based on examples:

// test-crypto-sdk.js

// Still needed with new SDK? Unknown, keep it just in case.
process.env.AWS_SDK_LOAD_CONFIG = '1';

const {
  KmsKeyringNode,
  buildClient,
  CommitmentPolicy,
} = require('@aws-crypto/client-node');

const { encrypt, decrypt } = buildClient(
  CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
);

// (Redacted.)
const KEY_ID = ''arn:aws:kms:us-east-1:000000000000:key/00000000-0000-0000-0000-000000000000';

const generatorKeyId = KEY_ID;

const keyring = new KmsKeyringNode({ generatorKeyId });

encrypt(keyring, "hi", { encryptionContext: {foo: '5'} })
  .then(data => console.dir(data))
  .catch(err => console.dir(err));

I don't know what profile the library tries to use, but that code throws errors:

# Without AWS_REGION or AWS_PROFILE or any AWS_* in the environment

$ node test-crypto-sdk.js
Error: A region is required
<stack trace>
# With env vars set

$ AWS_REGION=us-east-1 AWS_PROFILE=staging node test-crypto-sdk.js
Error [CredentialsError]: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1
    at Timeout.connectTimeout [as _onTimeout] (/home/timk/code/xometry/cli/node_modules/aws-sdk/lib/http/node.js:69:15)
    at listOnTimeout (internal/timers.js:554:17)
    at processTimers (internal/timers.js:497:7) {
  code: 'CredentialsError',
  time: 2021-10-19T18:19:20.998Z,
  retryable: true,
  originalError: {
    message: 'Could not load credentials from any providers',
    code: 'CredentialsError',
    time: 2021-10-19T18:19:20.998Z,
    retryable: true,
    originalError: {
      message: 'EC2 Metadata roleName request returned error',
      code: 'TimeoutError',
      time: 2021-10-19T18:19:20.997Z,
      retryable: true,
      originalError: [Object]
    }
  }
}

How do I give @aws-crypto/client-node the profile from which to load credentials? How do I tell it to use the region from the profile, or how do I specify a region in code without using the AWS_PROFILE environment variable?

I ended up going back to aws-sdk for the project I was working on, but it would be really great to have documentation or examples with credentials here.

@seebees
Copy link
Contributor

seebees commented Oct 19, 2021

The AWS Encryption SDK is not a replacement for the AWS SDK.
It can use AWS KMS as a source for materials, but this is not required.

Here is an example using AWS KMS https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-node/src/kms_simple.ts

The KmsKeyringNode is using an AWS SDK to contact AWS KMS.
If you need to configure the client, you can use a clientProvider.
like this

// Get SDK Here so you can configure it
const {
  KMS,
  SharedIniFileCredentials,
  config
} = require('aws-sdk');
process.env.AWS_SDK_LOAD_CONFIG = '1';
config.credentials = new SharedIniFileCredentials({profile: 'staging'});
// This will get called with the `region` from the CMK
// and get back a AWS KMS SDK client configured however you like.
const clientProvider = (region: string) => new KMS({ region })

const {
  KmsKeyringNode,
  buildClient,
  CommitmentPolicy,
} = require('@aws-crypto/client-node');

const { encrypt, decrypt } = buildClient(
  CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
);

// (Redacted.)
const KEY_ID = 'arn:aws:kms:us-east-1:000000000000:key/00000000-0000-0000-0000-000000000000';

const generatorKeyId = KEY_ID;

// Pass the provider to the `Keyring` so that it can use it to get clients
const keyring = new KmsKeyringNode({ generatorKeyId, clientProvider });

encrypt(keyring, "hi", { encryptionContext: {foo: '5'} })
  .then(data => console.dir(data))
  .catch(err => console.dir(err));

@seebees seebees closed this as completed Oct 21, 2021
@mildmojo
Copy link
Author

Thanks for explaining this and showing an example! I hadn't encountered clientProvider yet. It appears in the multi-region example, but it's not clear what it is, and I didn't find documentation of what it was supposed to be (a function that returns, in this case, a KMS object).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants