Skip to content

Commit

Permalink
fix(Variables): Provide opt-out from forced decryption atssm source
Browse files Browse the repository at this point in the history
(PR #10315)
  • Loading branch information
omerinvia committed Dec 8, 2021
1 parent e4431f2 commit 503c031
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docs/providers/aws/guide/variables.md
Expand Up @@ -340,6 +340,8 @@ New variable resolver, ensures that automatically other types as `SecureString`

All `SecureString` type parameters are automatically decrypted, and automatically parsed if they export stringified JSON content (Note: you can turn off parsing by passing `raw` instruction into variable as: `${ssm(raw):/path/to/secureparam}`, if you need to also pass custom region, put it first as: `${ssm(eu-west-1, raw):/path/to/secureparam}`)

In order to get the encrypted content, you can pass `noDecrypt` instruction into variable as: `${ssm(noDecrypt):/path/to/secureparam}` (it can be passed aside of region param as e.g.: `${ssm(eu-west-1, noDecrypt):/path/to/secureparam})`

## Reference Variables using AWS Secrets Manager

Variables in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) can be referenced [using SSM](https://docs.aws.amazon.com/systems-manager/latest/userguide/integration-ps-secretsmanager.html), just use the `ssm:/aws/reference/secretsmanager/secret_ID_in_Secrets_Manager` syntax. For example:
Expand Down
Expand Up @@ -3,6 +3,7 @@
const ensureString = require('type/string/ensure');
const ServerlessError = require('../../../../serverless-error');
const logDeprecation = require('../../../../utils/logDeprecation');
const _ = require('lodash');

module.exports = (serverlessInstance) => {
return {
Expand All @@ -19,8 +20,10 @@ module.exports = (serverlessInstance) => {
errorMessage: 'Non-string address argument in variable "ssm" source: %v',
errorCode: 'INVALID_SSM_SOURCE_ADDRESS',
});
const region = !params || !params[0] || params[0] === 'raw' ? undefined : params[0];
const shouldReturnRawValue = params && (params[0] === 'raw' || params[1] === 'raw');
const shouldReturnRawValue = params && params.includes('raw');
const shouldSkipDecryption = params && params.includes('noDecrypt');
_.pull(params, 'raw', 'noDecrypt');
const region = params && params[0];

// TODO: Remove legacy instruction separator handling with next major
let legacyShouldEnforceDecrypt = false;
Expand Down Expand Up @@ -54,7 +57,7 @@ module.exports = (serverlessInstance) => {
'getParameter',
{
Name: address,
WithDecryption: true,
WithDecryption: !shouldSkipDecryption,
},
{ useCache: true, region }
);
Expand Down
Expand Up @@ -27,6 +27,8 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s
existingListRaw: '${ssm(raw):existingList}',
secretManager: '${ssm:/aws/reference/secretsmanager/existing}',
existingEncrypted: '${ssm:/secret/existing}',
encryptedWithSkipDecrypt: '${ssm(noDecrypt):/secret/existing}',
encryptedWithSkipDecryptAndRegion: '${ssm(noDecrypt, eu-west-1):/secret/existing}',
existingEncryptedDirect: '${ssm:/secret/direct}',
existingEncryptedRaw: '${ssm(raw):/aws/reference/secretsmanager/existing}',
notExisting: '${ssm:notExisting, null}',
Expand All @@ -39,7 +41,7 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s

serverlessInstance = {
getProvider: () => ({
request: async (name, method, { Name }, { region }) => {
request: async (name, method, { Name, WithDecryption }, { region }) => {
if (!allowedRegionTypes.has(typeof region)) throw new Error('Invalid region value');
if (Name === 'existing') {
return { Parameter: { Type: 'String', Value: region || 'value' } };
Expand All @@ -48,10 +50,20 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s
return { Parameter: { Type: 'StringList', Value: 'one,two,three' } };
}
if (Name === '/secret/existing' || Name === '/aws/reference/secretsmanager/existing') {
return { Parameter: { Type: 'SecureString', Value: '{"someSecret":"someValue"}' } };
return {
Parameter: {
Type: 'SecureString',
Value: WithDecryption ? '{"someSecret":"someValue"}' : 'ENCRYPTED',
},
};
}
if (Name === '/secret/direct') {
return { Parameter: { Type: 'SecureString', Value: '12345678901234567890' } };
return {
Parameter: {
Type: 'SecureString',
Value: WithDecryption ? '12345678901234567890' : 'ENCRYPTED',
},
};
}
if (Name === 'notExisting') {
throw Object.assign(
Expand Down Expand Up @@ -111,8 +123,16 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s
if (variablesMeta.get('custom\0existingDirect')) {
throw variablesMeta.get('custom\0existingDirect').error;
}
if (variablesMeta.get('custom\0encryptedWithSkipDecrypt')) {
throw variablesMeta.get('custom\0encryptedWithSkipDecrypt').error;
}
if (variablesMeta.get('custom\0encryptedWithSkipDecryptAndRegion')) {
throw variablesMeta.get('custom\0encryptedWithSkipDecrypt').error;
}
expect(configuration.custom.existingEncrypted).to.deep.equal({ someSecret: 'someValue' });
expect(configuration.custom.existingEncryptedDirect).to.equal('12345678901234567890');
expect(configuration.custom.encryptedWithSkipDecrypt).to.equal('ENCRYPTED');
expect(configuration.custom.encryptedWithSkipDecryptAndRegion).to.equal('ENCRYPTED');
});

it('should support "raw" output for decrypted data', () => {
Expand Down

0 comments on commit 503c031

Please sign in to comment.