Skip to content

Commit

Permalink
fix(aws-ecs): token is added to Options instead of SecretOptions in S…
Browse files Browse the repository at this point in the history
…plunkLogDriver (aws#15408)

----
This PR closes [aws#7264](aws#7264).

The `token` field of the Splunk log driver populates the `Options` property of the Log Configuration which leads to the secret being resolved to its value on deploying, and then the token is viewable in plain text in the console and may be stored in plain text elsewhere. Thus, we are deprecating the `token` field of the Splunk log driver and are introducing a new `secretToken` field. `secretToken` can be used to provide the Splunk token as a Secrets Manager Secret or a Systems Manager Parameter and will be populated in the `SecretOptions` property of the Log Configuration. 

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
upparekh authored and TikiTDO committed Aug 3, 2021
1 parent 480e371 commit 7b12d8d
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ taskDefinition.addContainer('TheContainer', {
image: ecs.ContainerImage.fromRegistry('example-image'),
memoryLimitMiB: 256,
logging: ecs.LogDrivers.splunk({
token: cdk.SecretValue.secretsManager('my-splunk-token'),
secretToken: cdk.SecretValue.secretsManager('my-splunk-token'),
url: 'my-splunk-url'
})
});
Expand Down
60 changes: 42 additions & 18 deletions packages/@aws-cdk/aws-ecs/lib/log-drivers/splunk-log-driver.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SecretValue } from '@aws-cdk/core';
import { ContainerDefinition } from '../container-definition';
import { ContainerDefinition, Secret } from '../container-definition';
import { BaseLogDriverProps } from './base-log-driver';
import { LogDriver, LogDriverConfig } from './log-driver';
import { ensureInRange, renderCommonLogDriverOptions, stringifyOptions } from './utils';
import { ensureInRange, renderCommonLogDriverOptions, renderLogDriverSecretOptions, stringifyOptions } from './utils';

// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
Expand All @@ -25,8 +25,26 @@ export enum SplunkLogFormat {
export interface SplunkLogDriverProps extends BaseLogDriverProps {
/**
* Splunk HTTP Event Collector token.
*
* The splunk-token is added to the Options property of the Log Driver Configuration. So the secret value will be resolved and
* viewable in plain text in the console.
*
* Please provide at least one of `token` or `secretToken`.
* @deprecated Use {@link SplunkLogDriverProps.secretToken} instead.
* @default - token not provided.
*/
readonly token: SecretValue;
readonly token?: SecretValue;

/**
* Splunk HTTP Event Collector token (Secret).
*
* The splunk-token is added to the SecretOptions property of the Log Driver Configuration. So the secret value will not be
* resolved or viewable as plain text.
*
* Please provide at least one of `token` or `secretToken`.
* @default - If secret token is not provided, then the value provided in `token` will be used.
*/
readonly secretToken?: Secret;

/**
* Path to your Splunk Enterprise, self-service Splunk Cloud instance, or Splunk
Expand Down Expand Up @@ -121,6 +139,9 @@ export class SplunkLogDriver extends LogDriver {
constructor(private readonly props: SplunkLogDriverProps) {
super();

if (!props.token && !props.secretToken) {
throw new Error('Please provide either token or secretToken.');
}
if (props.gzipLevel) {
ensureInRange(props.gzipLevel, -1, 9);
}
Expand All @@ -130,23 +151,26 @@ export class SplunkLogDriver extends LogDriver {
* Called when the log driver is configured on a container
*/
public bind(_scope: CoreConstruct, _containerDefinition: ContainerDefinition): LogDriverConfig {
const options = stringifyOptions({
'splunk-token': this.props.token,
'splunk-url': this.props.url,
'splunk-source': this.props.source,
'splunk-sourcetype': this.props.sourceType,
'splunk-index': this.props.index,
'splunk-capath': this.props.caPath,
'splunk-caname': this.props.caName,
'splunk-insecureskipverify': this.props.insecureSkipVerify,
'splunk-format': this.props.format,
'splunk-verify-connection': this.props.verifyConnection,
'splunk-gzip': this.props.gzip,
'splunk-gzip-level': this.props.gzipLevel,
...renderCommonLogDriverOptions(this.props),
});

return {
logDriver: 'splunk',
options: stringifyOptions({
'splunk-token': this.props.token,
'splunk-url': this.props.url,
'splunk-source': this.props.source,
'splunk-sourcetype': this.props.sourceType,
'splunk-index': this.props.index,
'splunk-capath': this.props.caPath,
'splunk-caname': this.props.caName,
'splunk-insecureskipverify': this.props.insecureSkipVerify,
'splunk-format': this.props.format,
'splunk-verify-connection': this.props.verifyConnection,
'splunk-gzip': this.props.gzip,
'splunk-gzip-level': this.props.gzipLevel,
...renderCommonLogDriverOptions(this.props),
}),
options,
secretOptions: this.props.secretToken && renderLogDriverSecretOptions({ 'splunk-token': this.props.secretToken }, _containerDefinition.taskDefinition),
};
}
}
110 changes: 110 additions & 0 deletions packages/@aws-cdk/aws-ecs/test/splunk-log-driver.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, haveResourceLike } from '@aws-cdk/assert-internal';
import * as cdk from '@aws-cdk/core';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import * as ssm from '@aws-cdk/aws-ssm';
import { nodeunitShim, Test } from 'nodeunit-shim';
import * as ecs from '../lib';

Expand Down Expand Up @@ -103,4 +105,112 @@ nodeunitShim({

test.done();
},

'create a splunk log driver using secret splunk token from secrets manager'(test: Test) {
const secret = new secretsmanager.Secret(stack, 'Secret');
// WHEN
td.addContainer('Container', {
image,
logging: ecs.LogDrivers.splunk({
secretToken: ecs.Secret.fromSecretsManager(secret),
url: 'my-splunk-url',
}),
memoryLimitMiB: 128,
});

// THEN
expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [
{
LogConfiguration: {
LogDriver: 'splunk',
Options: {
'splunk-url': 'my-splunk-url',
},
SecretOptions: [
{
Name: 'splunk-token',
ValueFrom: {
Ref: 'SecretA720EF05',
},
},
],
},
},
],
}));

test.done();
},

'create a splunk log driver using secret splunk token from systems manager parameter store'(test: Test) {
const parameter = ssm.StringParameter.fromSecureStringParameterAttributes(stack, 'Parameter', {
parameterName: '/token',
version: 1,
});
// WHEN
td.addContainer('Container', {
image,
logging: ecs.LogDrivers.splunk({
secretToken: ecs.Secret.fromSsmParameter(parameter),
url: 'my-splunk-url',
}),
memoryLimitMiB: 128,
});

// THEN
expect(stack).to(haveResourceLike('AWS::ECS::TaskDefinition', {
ContainerDefinitions: [
{
LogConfiguration: {
LogDriver: 'splunk',
Options: {
'splunk-url': 'my-splunk-url',
},
SecretOptions: [
{
Name: 'splunk-token',
ValueFrom: {
'Fn::Join': [
'',
[
'arn:',
{
Ref: 'AWS::Partition',
},
':ssm:',
{
Ref: 'AWS::Region',
},
':',
{
Ref: 'AWS::AccountId',
},
':parameter/token',
],
],
},
},
],
},
},
],
}));

test.done();
},

'throws when neither token nor secret token are provided'(test: Test) {
test.throws(() => {
td.addContainer('Container', {
image,
logging: ecs.LogDrivers.splunk({
url: 'my-splunk-url',
}),
memoryLimitMiB: 128,
});
}, 'Please provide either token or secretToken.');

test.done();
},
});

0 comments on commit 7b12d8d

Please sign in to comment.