Skip to content

Commit

Permalink
Automatically grant functions read access to a given aws secret (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
thedavl authored May 17, 2023
1 parent 4173cc4 commit e420bf2
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 17 deletions.
8 changes: 4 additions & 4 deletions common/datadogSharedLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

import log from "loglevel";
import { DefaultDatadogProps } from "./constants";
import { DatadogProps, DatadogStrictProps } from "./interfaces";
import { IDatadogProps, DatadogStrictProps } from "./interfaces";

export function validateProps(props: DatadogProps) {
export function validateProps(props: IDatadogProps) {
log.debug("Validating props...");

checkForMultipleApiKeys(props);
Expand Down Expand Up @@ -49,7 +49,7 @@ export function validateProps(props: DatadogProps) {
}
}

export function checkForMultipleApiKeys(props: DatadogProps) {
export function checkForMultipleApiKeys(props: IDatadogProps) {
let multipleApiKeysMessage;
if (props.apiKey !== undefined && props.apiKmsKey !== undefined && props.apiKeySecretArn !== undefined) {
multipleApiKeysMessage = "`apiKey`, `apiKmsKey`, and `apiKeySecretArn`";
Expand All @@ -66,7 +66,7 @@ export function checkForMultipleApiKeys(props: DatadogProps) {
}
}

export function handleSettingPropDefaults(props: DatadogProps): DatadogStrictProps {
export function handleSettingPropDefaults(props: IDatadogProps): DatadogStrictProps {
let addLayers = props.addLayers;
let enableDatadogTracing = props.enableDatadogTracing;
let enableMergeXrayTraces = props.enableMergeXrayTraces;
Expand Down
4 changes: 2 additions & 2 deletions common/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import log from "loglevel";
import { DatadogProps, DatadogStrictProps, ILambdaFunction } from "./interfaces";
import { IDatadogProps, DatadogStrictProps, ILambdaFunction } from "./interfaces";

export const ENABLE_DD_TRACING_ENV_VAR = "DD_TRACE_ENABLED";
export const ENABLE_XRAY_TRACE_MERGING_ENV_VAR = "DD_MERGE_XRAY_TRACES";
Expand Down Expand Up @@ -107,7 +107,7 @@ export function applyEnvVariables(lambdas: ILambdaFunction[], baseProps: Datadog
});
}

export function setDDEnvVariables(lambdas: ILambdaFunction[], props: DatadogProps) {
export function setDDEnvVariables(lambdas: ILambdaFunction[], props: IDatadogProps) {
lambdas.forEach((lam) => {
if (props.extensionLayerVersion) {
if (props.env) {
Expand Down
4 changes: 2 additions & 2 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Copyright 2021 Datadog, Inc.
*/

export interface DatadogProps {
export interface IDatadogProps {
readonly pythonLayerVersion?: number;
readonly nodeLayerVersion?: number;
readonly javaLayerVersion?: number;
Expand All @@ -16,7 +16,7 @@ export interface DatadogProps {
readonly flushMetricsToLogs?: boolean;
readonly site?: string;
readonly apiKey?: string;
readonly apiKeySecretArn?: string;
apiKeySecretArn?: string;
readonly apiKmsKey?: string;
readonly enableDatadogTracing?: boolean;
readonly enableMergeXrayTraces?: boolean;
Expand Down
8 changes: 4 additions & 4 deletions v1/src/datadog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
addForwarderToLogGroups,
applyEnvVariables,
applyLayers,
DatadogProps,
IDatadogProps,
DatadogStrictProps,
handleSettingPropDefaults,
redirectHandlers,
Expand All @@ -32,9 +32,9 @@ const versionJson = require("../version.json");

export class Datadog extends cdk.Construct {
scope: cdk.Construct;
props: DatadogProps;
props: IDatadogProps;
transport: Transport;
constructor(scope: cdk.Construct, id: string, props: DatadogProps) {
constructor(scope: cdk.Construct, id: string, props: IDatadogProps) {
if (process.env.DD_CONSTRUCT_DEBUG_LOGS?.toLowerCase() == "true") {
log.setLevel("debug");
}
Expand Down Expand Up @@ -132,7 +132,7 @@ export function addCdkConstructVersionTag(lambdaFunctions: lambda.Function[]) {
});
}

function setTags(lambdaFunctions: lambda.Function[], props: DatadogProps) {
function setTags(lambdaFunctions: lambda.Function[], props: IDatadogProps) {
log.debug(`Adding datadog tags`);
lambdaFunctions.forEach((functionName) => {
if (props.forwarderArn) {
Expand Down
28 changes: 23 additions & 5 deletions v2/src/datadog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Tags } from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as lambdaNodejs from "aws-cdk-lib/aws-lambda-nodejs";
import * as logs from "aws-cdk-lib/aws-logs";
import { ISecret } from "aws-cdk-lib/aws-secretsmanager";
import { Construct } from "constructs";
import log from "loglevel";
import { Transport } from "./common/transport";
Expand All @@ -22,7 +23,7 @@ import {
applyEnvVariables,
validateProps,
TagKeys,
DatadogProps,
IDatadogProps,
DatadogStrictProps,
handleSettingPropDefaults,
setGitEnvironmentVariables,
Expand All @@ -31,17 +32,22 @@ import {

const versionJson = require("../version.json");

export class Datadog extends Construct {
type IDatadogPropsV2 = IDatadogProps & { apiKeySecret?: ISecret };

class Datadog extends Construct {
scope: Construct;
props: DatadogProps;
props: IDatadogPropsV2;
transport: Transport;
constructor(scope: Construct, id: string, props: DatadogProps) {
constructor(scope: Construct, id: string, props: IDatadogPropsV2) {
if (process.env.DD_CONSTRUCT_DEBUG_LOGS?.toLowerCase() == "true") {
log.setLevel("debug");
}
super(scope, id);
this.scope = scope;
this.props = props;
if (this.props.apiKeySecret !== undefined) {
this.props.apiKeySecretArn = this.props.apiKeySecret.secretArn;
}
validateProps(this.props);
this.transport = new Transport(
this.props.flushMetricsToLogs,
Expand All @@ -61,6 +67,10 @@ export class Datadog extends Construct {
const baseProps: DatadogStrictProps = handleSettingPropDefaults(this.props);

if (this.props !== undefined && lambdaFunctions.length > 0) {
if (this.props.apiKeySecret !== undefined) {
grantReadLambdas(this.props.apiKeySecret, lambdaFunctions);
}

const region = `${lambdaFunctions[0].env.region}`;
log.debug(`Using region: ${region}`);
if (baseProps.addLayers) {
Expand Down Expand Up @@ -145,7 +155,7 @@ export function addCdkConstructVersionTag(lambdaFunctions: lambda.Function[]) {
});
}

function setTags(lambdaFunctions: lambda.Function[], props: DatadogProps) {
function setTags(lambdaFunctions: lambda.Function[], props: IDatadogProps) {
log.debug(`Adding datadog tags`);
lambdaFunctions.forEach((functionName) => {
if (props.forwarderArn) {
Expand All @@ -170,3 +180,11 @@ function setTags(lambdaFunctions: lambda.Function[], props: DatadogProps) {
}
});
}

function grantReadLambdas(secret: ISecret, lambdaFunctions: lambda.Function[]) {
lambdaFunctions.forEach((functionName) => {
secret.grantRead(functionName);
});
}

export { Datadog, IDatadogPropsV2 };
90 changes: 90 additions & 0 deletions v2/test/datadog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Template } from "aws-cdk-lib/assertions";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { LogGroup } from "aws-cdk-lib/aws-logs";
import { addCdkConstructVersionTag, checkForMultipleApiKeys, Datadog, DD_HANDLER_ENV_VAR } from "../src/index";
const { ISecret } = require("aws-cdk-lib/aws-secretsmanager");
const versionJson = require("../version.json");
const EXTENSION_LAYER_VERSION = 5;
const NODE_LAYER_VERSION = 1;
Expand Down Expand Up @@ -79,6 +80,43 @@ describe("validateProps", () => {
expect(thrownError?.message).toEqual(undefined);
});

it("doesn't throw an error if an apiKeySecret is set", () => {
const app = new App();
const stack = new Stack(app, "stack");
const hello = new lambda.Function(stack, "HelloHandler", {
runtime: lambda.Runtime.NODEJS_12_X,
code: lambda.Code.fromInline("test"),
handler: "hello.handler",
});

const secret: typeof ISecret = {
secretArn: "dummy-arn",
grantRead() {
return;
},
};

let threwError = false;
let thrownError: Error | undefined;
try {
const datadogCdk = new Datadog(stack, "Datadog", {
nodeLayerVersion: NODE_LAYER_VERSION,
extensionLayerVersion: EXTENSION_LAYER_VERSION,
apiKeySecret: secret,
enableDatadogTracing: false,
flushMetricsToLogs: false,
});
datadogCdk.addLambdaFunctions([hello]);
} catch (e) {
threwError = true;
if (e instanceof Error) {
thrownError = e;
}
}
expect(threwError).toBe(false);
expect(thrownError?.message).toEqual(undefined);
});

it("throws error if flushMetricsToLogs is false and both API key and KMS API key are undefined", () => {
const app = new App();
const stack = new Stack(app, "stack", {
Expand Down Expand Up @@ -420,6 +458,58 @@ describe("setTags", () => {
});
});

describe("apiKeySecret", () => {
it("sets apiKeySecretArn", () => {
const app = new App();
const stack = new Stack(app, "stack");
const hello = new lambda.Function(stack, "HelloHandler", {
runtime: lambda.Runtime.NODEJS_12_X,
code: lambda.Code.fromInline("test"),
handler: "hello.handler",
});
const secret: typeof ISecret = {
secretArn: "dummy-arn",
grantRead() {
return;
},
};
const datadogCdk = new Datadog(stack, "Datadog", {
nodeLayerVersion: NODE_LAYER_VERSION,
extensionLayerVersion: EXTENSION_LAYER_VERSION,
apiKeySecret: secret,
enableDatadogTracing: false,
flushMetricsToLogs: false,
});
datadogCdk.addLambdaFunctions([hello]);
expect(datadogCdk.props.apiKeySecretArn).toEqual("dummy-arn");
});
it("overrides apiKeySecretArn", () => {
const app = new App();
const stack = new Stack(app, "stack");
const hello = new lambda.Function(stack, "HelloHandler", {
runtime: lambda.Runtime.NODEJS_12_X,
code: lambda.Code.fromInline("test"),
handler: "hello.handler",
});
const secret: typeof ISecret = {
secretArn: "dummy-arn-from-isecret",
grantRead() {
return;
},
};
const datadogCdk = new Datadog(stack, "Datadog", {
nodeLayerVersion: NODE_LAYER_VERSION,
extensionLayerVersion: EXTENSION_LAYER_VERSION,
apiKeySecret: secret,
apiKeySecretArn: "dummy-arn",
enableDatadogTracing: false,
flushMetricsToLogs: false,
});
datadogCdk.addLambdaFunctions([hello]);
expect(datadogCdk.props.apiKeySecretArn).toEqual("dummy-arn-from-isecret");
});
});

describe("redirectHandler", () => {
it("doesn't redirect handler when explicitly set to `false`", () => {
const app = new App();
Expand Down

0 comments on commit e420bf2

Please sign in to comment.