Skip to content

Commit

Permalink
Merge 9f44374 into 38b93dc
Browse files Browse the repository at this point in the history
  • Loading branch information
timo92 committed Dec 13, 2022
2 parents 38b93dc + 9f44374 commit 354f146
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/providers/aws/guide/functions.md
Expand Up @@ -591,6 +591,8 @@ You can opt out of the default behavior by setting `disableLogs: true`

You can also specify the duration for CloudWatch log retention by setting `logRetentionInDays`.

You can specify the DataProtectionPolicy for the LogGroup by setting `logDataProtectionPolicy`. On how to define the policy consult the [aws docs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/mask-sensitive-log-data-start.html).

```yml
functions:
hello:
Expand All @@ -599,6 +601,8 @@ functions:
goodBye:
handler: handler.goodBye
logRetentionInDays: 14
logDataProtectionPolicy:
Name: data-protection-policy
```

## Versioning Deployed Functions
Expand Down
4 changes: 4 additions & 0 deletions docs/providers/aws/guide/serverless.yml.md
Expand Up @@ -128,6 +128,10 @@ provider:
# Duration for CloudWatch log retention (default: forever)
# Valid values: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html
logRetentionInDays: 14
# Policy defining how to monitor and mask sensitive data in CloudWatch logs
# Policy format: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/mask-sensitive-log-data-start.html
logDataProtectionPolicy:
Name: data-protection-policy
# KMS key ARN to use for encryption for all functions
kmsKeyArn: arn:aws:kms:us-east-1:XXXXXX:key/some-hash
# Version of hashing algorithm used by Serverless Framework for function packaging
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/aws/custom-resources/index.js
Expand Up @@ -197,6 +197,7 @@ async function addCustomResourceToService(awsProvider, resourceName, iamRoleStat
Properties: {
LogGroupName: awsProvider.naming.getLogGroupName(absoluteFunctionName),
RetentionInDays: awsProvider.getLogRetentionInDays(),
DataProtectionPolicy: awsProvider.getLogDataProtectionPolicy(),
},
},
});
Expand Down
Expand Up @@ -113,5 +113,10 @@ function getLogGroupResource(service, stage, provider) {
resource.Properties.RetentionInDays = logRetentionInDays;
}

const logDataProtectionPolicy = provider.getLogDataProtectionPolicy();
if (logDataProtectionPolicy) {
resource.Properties.DataProtectionPolicy = logDataProtectionPolicy;
}

return resource;
}
5 changes: 5 additions & 0 deletions lib/plugins/aws/package/compile/events/http-api.js
Expand Up @@ -143,6 +143,11 @@ class HttpApiEvents {
resource.Properties.RetentionInDays = logRetentionInDays;
}

const logDataProtectionPolicy = this.provider.getLogDataProtectionPolicy();
if (logDataProtectionPolicy) {
resource.Properties.DataProtectionPolicy = logDataProtectionPolicy;
}

this.cfTemplate.Resources[this.provider.naming.getHttpApiLogGroupLogicalId()] = resource;
}
compileStage() {
Expand Down
Expand Up @@ -108,5 +108,9 @@ function getLogGroupResource(service, stage, provider) {
if (logRetentionInDays) {
resource.Properties.RetentionInDays = logRetentionInDays;
}
const logDataProtectionPolicy = provider.getLogDataProtectionPolicy();
if (logDataProtectionPolicy) {
resource.Properties.DataProtectionPolicy = logDataProtectionPolicy;
}
return resource;
}
6 changes: 6 additions & 0 deletions lib/plugins/aws/package/lib/merge-iam-templates.js
Expand Up @@ -32,6 +32,12 @@ module.exports = {
newLogGroup[logGroupLogicalId].Properties.RetentionInDays = logRetentionInDays;
}

const logDataProtectionPolicy =
functionObject.logDataProtectionPolicy || this.provider.getLogDataProtectionPolicy();
if (logDataProtectionPolicy) {
newLogGroup[logGroupLogicalId].Properties.DataProtectionPolicy = logDataProtectionPolicy;
}

_.merge(
this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
newLogGroup
Expand Down
21 changes: 21 additions & 0 deletions lib/plugins/aws/provider.js
Expand Up @@ -675,6 +675,17 @@ class AwsProvider {
3288, 3653,
],
},
awsLogDataProtectionPolicy: {
type: 'object',
properties: {
Name: { type: 'string' },
Description: { type: 'string' },
Version: { type: 'string' },
Statement: { type: 'array' },
},
additionalProperties: false,
required: ['Name', 'Version', 'Statement'],
},
awsResourceCondition: { type: 'string' },
awsResourceDependsOn: { type: 'array', items: { type: 'string' } },
awsResourcePolicyResource: {
Expand Down Expand Up @@ -1105,6 +1116,9 @@ class AwsProvider {
logRetentionInDays: {
$ref: '#/definitions/awsLogRetentionInDays',
},
logDataProtectionPolicy: {
$ref: '#/definitions/awsLogDataProtectionPolicy',
},
logs: {
type: 'object',
properties: {
Expand Down Expand Up @@ -1374,6 +1388,9 @@ class AwsProvider {
logRetentionInDays: {
$ref: '#/definitions/awsLogRetentionInDays',
},
logDataProtectionPolicy: {
$ref: '#/definitions/awsLogDataProtectionPolicy',
},
maximumEventAge: { type: 'integer', minimum: 60, maximum: 21600 },
maximumRetryAttempts: { type: 'integer', minimum: 0, maximum: 2 },
memorySize: { $ref: '#/definitions/awsLambdaMemorySize' },
Expand Down Expand Up @@ -1883,6 +1900,10 @@ class AwsProvider {
return this.serverless.service.provider.logRetentionInDays;
}

getLogDataProtectionPolicy() {
return this.serverless.service.provider.logDataProtectionPolicy;
}

getStageSourceValue() {
const values = this.getValues(this, [
['options', 'stage'],
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/programmatic/http-api/serverless.yml
Expand Up @@ -7,6 +7,10 @@ provider:
name: aws
runtime: nodejs12.x
logRetentionInDays: 14
logDataProtectionPolicy:
Name: data-protection-policy
Version: 2021-06-01
Statement: []

functions:
foo:
Expand Down
Expand Up @@ -379,4 +379,32 @@ describe('test/unit/lib/plugins/aws/package/compile/events/apiGateway/lib/stage/
},
});
});

it('should set DataProtectionPolicy if provider.logDataProtectionPolicy is set', async () => {
const policy = {
Name: 'data-protection-policy',
Version: '2021-06-01',
Statement: [],
};
const { cfTemplate, awsNaming, serverless } = await runServerless({
fixture: 'api-gateway',
command: 'package',
configExt: {
provider: {
logs: {
restApi: true,
},
logDataProtectionPolicy: policy,
},
},
});

expect(cfTemplate.Resources[awsNaming.getApiGatewayLogGroupLogicalId()]).to.deep.equal({
Type: 'AWS::Logs::LogGroup',
Properties: {
LogGroupName: `/aws/api-gateway/${serverless.service.service}-dev`,
DataProtectionPolicy: policy,
},
});
});
});
Expand Up @@ -247,7 +247,13 @@ describe('lib/plugins/aws/package/compile/events/websockets/lib/stage.test.js',

describe('logs with full custom options', () => {
let resource;
let logGroupResource;
const customLogFormat = ['$context.identity.sourceIp', '$context.requestId'].join(' ');
const logDataProtectionPolicy = {
Name: 'data-protection-policy',
Version: '2021-06-01',
Statement: [],
};

before(async () => {
const { cfTemplate, awsNaming } = await runServerless({
Expand All @@ -263,12 +269,14 @@ describe('lib/plugins/aws/package/compile/events/websockets/lib/stage.test.js',
format: customLogFormat,
},
},
logDataProtectionPolicy,
},
},
command: 'package',
});
const stageLogicalId = awsNaming.getWebsocketsStageLogicalId();
resource = cfTemplate.Resources[stageLogicalId];
logGroupResource = cfTemplate.Resources[awsNaming.getWebsocketsLogGroupLogicalId()];
});

it('should set accessLogging off', async () => {
Expand All @@ -282,5 +290,11 @@ describe('lib/plugins/aws/package/compile/events/websockets/lib/stage.test.js',
it('should set fullExecutionData true', async () => {
expect(resource.Properties.DefaultRouteSettings.DataTraceEnabled).to.equal(true);
});

it('should set DataProtectionPolicy', () => {
expect(logGroupResource.Properties.DataProtectionPolicy).to.deep.equal(
logDataProtectionPolicy
);
});
});
});
31 changes: 31 additions & 0 deletions test/unit/lib/plugins/aws/package/lib/merge-iam-templates.test.js
Expand Up @@ -306,6 +306,11 @@ describe('lib/plugins/aws/package/lib/mergeIamTemplates.test.js', () => {
subnetIds: ['xxx'],
},
logRetentionInDays: 5,
logDataProtectionPolicy: {
Name: 'data-protection-policy',
Version: '2021-06-01',
Statement: [],
},
},
},
});
Expand Down Expand Up @@ -429,6 +434,12 @@ describe('lib/plugins/aws/package/lib/mergeIamTemplates.test.js', () => {
expect(iamResource.Properties.LogGroupName).to.be.equal(`/aws/lambda/${service}-dev-basic`);
});

it('should support `provider.logDataProtectionPolicy`', () => {
const normalizedName = naming.getLogGroupLogicalId('basic');
const iamResource = cfResources[normalizedName];
expect(iamResource.Properties.DataProtectionPolicy.Name).to.equal('data-protection-policy');
});

it('should support `provider.iam.role.tags`', () => {
const IamRoleLambdaExecution = naming.getRoleLogicalId();
const iamResource = cfResources[IamRoleLambdaExecution];
Expand Down Expand Up @@ -477,6 +488,14 @@ describe('lib/plugins/aws/package/lib/mergeIamTemplates.test.js', () => {
handler: 'index.handler',
logRetentionInDays: 5,
},
fnLogDataProtectionPolicy: {
handler: 'index.handler',
logDataProtectionPolicy: {
Name: 'data-protection-policy',
Version: '2021-06-01',
Statement: [],
},
},
fnWithVpc: {
handler: 'index.handler',
vpc: {
Expand Down Expand Up @@ -531,6 +550,18 @@ describe('lib/plugins/aws/package/lib/mergeIamTemplates.test.js', () => {
);
});

it('should support `functions[].logDataProtectionPolicy`', async () => {
const functionName = serverless.service.getFunction('fnLogDataProtectionPolicy').name;
const normalizedName = naming.getLogGroupLogicalId('fnLogDataProtectionPolicy');
const logResource = cfResources[normalizedName];

expect(logResource.Type).to.be.equal('AWS::Logs::LogGroup');
expect(logResource.Properties.DataProtectionPolicy.Name).to.equal('data-protection-policy');
expect(logResource.Properties.LogGroupName).to.be.equal(
naming.getLogGroupName(functionName)
);
});

it('should not have allow rights to put logs for custom named function when disableLogs option is enabled', async () => {
expect(
cfResources[naming.getRoleLogicalId()].Properties.Policies[0].PolicyDocument.Statement[0]
Expand Down

0 comments on commit 354f146

Please sign in to comment.