-
Notifications
You must be signed in to change notification settings - Fork 103
feat: initial implementation for function secret access #845
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
Conversation
…ope, limit getParameters arguments
🦋 Changeset detectedLatest commit: 5f9f1a9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
packages/backend/src/engine/backend-secret/backend_secret_resolver.ts
Outdated
Show resolved
Hide resolved
...s/integration-tests/src/test-projects/data-storage-auth-with-triggers-ts/amplify/function.ts
Outdated
Show resolved
Hide resolved
.../src/test-projects/data-storage-auth-with-triggers-ts/amplify/func-src/response_generator.ts
Outdated
Show resolved
Hide resolved
| } | ||
| }; | ||
|
|
||
| await resolveSecretBanner(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to wrap all this code (maybe except import?) into anonymous function to avoid declaring global name, i.e. eliminate a chance that we'll collide with customer symbols?
I.e. something like:
await (async function() {
/// body of this file goes here;
}) ();
(see https://www.javascripttutorial.net/javascript-anonymous-functions/ )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we will be able to test the code if we wrap it in an anonymous function, which was why it is done this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By looking at test it seems the output assertion is using env vars, so that part is covered, so if there's some way to mock SSM client globally this could be an option.
Other option to solve testing is to test this with live SSM (i.e. not mock SSM and add e2e test for this fragment.
But if that's not very practical then another option is to keep it as is but make sure that function name is long and hard to guess. I.e. internalAmplifyFunctionBannerResolveSecrets or something more obfuscated.
It seems that function name is the only global symbol that matters, so maybe obfuscating name is good enough.
| @@ -0,0 +1,31 @@ | |||
| import { SSM } from '@aws-sdk/client-ssm'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does bundler handle this import?
The reason I'm asking is that for other files that are bundled I'm guessing bunder generates some form of import/require in right place, but I'm not sure how this works for banner (which they advertise as a place to inject comments), so I wonder if theres a chance that in some more complex lambdas this might become a problem.
Might be helpful if you could share how sample bundled code looks like in a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bundling format is ESM to allow the top level await to work. The code with an example handler in lambda console looks like this:
/* The code in this line replaces placeholder text in environment variables for secrets with values fetched from SSM, this is a noop if there are no secrets */import { SSM } from '@aws-sdk/client-ssm';/** * The body of this function will be used to resolve secrets for Lambda functions */export const internalAmplifyFunctionBannerResolveSecrets = async (client) => { if (!client) { client = new SSM(); } const envPathObj = JSON.parse(process.env.AMPLIFY_SECRET_PATHS ?? '{}'); const paths = Object.values(envPathObj); if (paths.length === 0) { return; } const response = await client.getParameters({ Names: paths, WithDecryption: true, }); if (response.Parameters && response.Parameters?.length > 0) { for (const parameter of response.Parameters) { for (const [env, path] of Object.entries(envPathObj)) { if (parameter.Name === path) { process.env[env] = parameter.Value; } } } }};await internalAmplifyFunctionBannerResolveSecrets();//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzb2x2ZV9zZWNyZXRfYmFubmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3Jlc29sdmVfc2VjcmV0X2Jhbm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxtSkFBbUo7QUFDbkosT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTFDOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMkNBQTJDLEdBQUcsS0FBSyxFQUM5RCxNQUFZLEVBQ1osRUFBRTtJQUNGLElBQUksQ0FBQyxNQUFNLEVBQUU7UUFDWCxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztLQUNwQjtJQUVELE1BQU0sVUFBVSxHQUEyQixJQUFJLENBQUMsS0FBSyxDQUNuRCxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixJQUFJLElBQUksQ0FDekMsQ0FBQztJQUNGLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFeEMsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUN0QixPQUFPO0tBQ1I7SUFFRCxNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxhQUFhLENBQUM7UUFDMUMsS0FBSyxFQUFFLEtBQUs7UUFDWixjQUFjLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUM7SUFDSCxJQUFJLFFBQVEsQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDLFVBQVUsRUFBRSxNQUFNLEdBQUcsQ0FBQyxFQUFFO1FBQzFELEtBQUssTUFBTSxTQUFTLElBQUksUUFBUSxDQUFDLFVBQVUsRUFBRTtZQUMzQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsRUFBRTtnQkFDcEQsSUFBSSxTQUFTLENBQUMsSUFBSSxLQUFLLElBQUksRUFBRTtvQkFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDO2lCQUNwQzthQUNGO1NBQ0Y7S0FDRjtBQUNILENBQUMsQ0FBQztBQUVGLE1BQU0sMkNBQTJDLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIFRoaXMgY29kZSByZXBsYWNlcyBwbGFjZWhvbGRlciB0ZXh0IGluIGVudmlyb25tZW50IHZhcmlhYmxlcyBmb3Igc2VjcmV0cyB3aXRoIHZhbHVlcyBmZXRjaGVkIGZyb20gU1NNLCB0aGlzIGlzIGEgbm9vcCBpZiB0aGVyZSBhcmUgbm8gc2VjcmV0cyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LXNzbSc7XG5cbi8qKlxuICogVGhlIGJvZHkgb2YgdGhpcyBmdW5jdGlvbiB3aWxsIGJlIHVzZWQgdG8gcmVzb2x2ZSBzZWNyZXRzIGZvciBMYW1iZGEgZnVuY3Rpb25zXG4gKi9cbmV4cG9ydCBjb25zdCBpbnRlcm5hbEFtcGxpZnlGdW5jdGlvbkJhbm5lclJlc29sdmVTZWNyZXRzID0gYXN5bmMgKFxuICBjbGllbnQ/OiBTU01cbikgPT4ge1xuICBpZiAoIWNsaWVudCkge1xuICAgIGNsaWVudCA9IG5ldyBTU00oKTtcbiAgfVxuXG4gIGNvbnN0IGVudlBhdGhPYmo6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSBKU09OLnBhcnNlKFxuICAgIHByb2Nlc3MuZW52LkFNUExJRllfU0VDUkVUX1BBVEhTID8/ICd7fSdcbiAgKTtcbiAgY29uc3QgcGF0aHMgPSBPYmplY3QudmFsdWVzKGVudlBhdGhPYmopO1xuXG4gIGlmIChwYXRocy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5nZXRQYXJhbWV0ZXJzKHtcbiAgICBOYW1lczogcGF0aHMsXG4gICAgV2l0aERlY3J5cHRpb246IHRydWUsXG4gIH0pO1xuICBpZiAocmVzcG9uc2UuUGFyYW1ldGVycyAmJiByZXNwb25zZS5QYXJhbWV0ZXJzPy5sZW5ndGggPiAwKSB7XG4gICAgZm9yIChjb25zdCBwYXJhbWV0ZXIgb2YgcmVzcG9uc2UuUGFyYW1ldGVycykge1xuICAgICAgZm9yIChjb25zdCBbZW52LCBwYXRoXSBvZiBPYmplY3QuZW50cmllcyhlbnZQYXRoT2JqKSkge1xuICAgICAgICBpZiAocGFyYW1ldGVyLk5hbWUgPT09IHBhdGgpIHtcbiAgICAgICAgICBwcm9jZXNzLmVudltlbnZdID0gcGFyYW1ldGVyLlZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59O1xuXG5hd2FpdCBpbnRlcm5hbEFtcGxpZnlGdW5jdGlvbkJhbm5lclJlc29sdmVTZWNyZXRzKCk7XG4iXX0=
// test-projects/testapp/amplify/function/handler.ts
var handler = async (event) => {
console.log(`TEST_SECRET is ${process.env.TEST_SECRET}`);
console.log(`TEST_SECRET2 is ${process.env.TEST_SECRET2}`);
};
export {
handler
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to minify like this, we should remove all whitespace. We should also remove the source map
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this working fine if test-projects/testapp/amplify/function/handler.ts has its own imports as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure of a good way to remove all the meaningless whitespace (we would need whitespace between await and internalAmplifyFunctionBannerResolveSecrets(); for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this working fine if
test-projects/testapp/amplify/function/handler.tshas its own imports as well?
Yes, all the contents of handler.ts will be under the comment // test-projects/testapp/amplify/function/handler.ts and still work as expected from what I've tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some interesting gotchas for this file. For example, an inline comment // some comment would break everything after that after it gets collapsed into a single line. I think imports of 3p modules or other files in resolve_secret_banner would also break as we are not following those imports and bundling them as well.
The "real" fix for all of this would be to run resolve_secret_banner through esbuild, then add that bundled output as the banner for esbuild of the customer lambda. But I'm okay if we just want to capture this thread as a GH issue and revisit if/when we need to do more complicated stuff here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like there was some windows / unix issue related with keeping the newlines, but if we kept newlines in, that would remove the inline comment gotcha and make the output more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup which is why I opted for /* */ for comments. Also it could be a problem with encoding but newlines were just kept as \n in the console and caused errors which was why I removed them.
edwardfoyle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving as long as we capture the concerns from this thread in a GH issue. However, if there are any quick wins from that thread we can include here we should do that.
Original PR #836
Problem
defineFunctiondoes not have access to secrets created withnpx amplify sandbox secret.Issue number, if available:
Changes
environmentparameter ofdefineFunction.backend-secrettoplatform-coreto be used across multiple packages.Corresponding docs PR, if applicable:
Validation
Updated
data_storage_auth_with_triggers.tse2e test to ensure secrets are properly resolved and can be accessed in the lambda function.Used a test project with local changes to deploy a function with secrets using
defineFunctionand verified the following:Example code with code snippet as seen in lambda console:
Checklist
run-e2elabel set.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.