Skip to content

Commit eb74c9c

Browse files
Extract API Key from Secrets Manager when using the metrics API client (#609)
get API key secret from secrets manager
1 parent c726f30 commit eb74c9c

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { SpanOptions, TracerWrapper } from "./trace/tracer-wrapper";
3131
export { DatadogTraceHeaders as TraceHeaders } from "./trace/context/extractor";
3232
export const apiKeyEnvVar = "DD_API_KEY";
3333
export const apiKeyKMSEnvVar = "DD_KMS_API_KEY";
34+
export const apiKeySecretARNEnvVar = "DD_API_KEY_SECRET_ARN";
3435
export const captureLambdaPayloadEnvVar = "DD_CAPTURE_LAMBDA_PAYLOAD";
3536
export const captureLambdaPayloadMaxDepthEnvVar = "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH";
3637
export const traceManagedServicesEnvVar = "DD_TRACE_MANAGED_SERVICES";
@@ -76,6 +77,7 @@ export type Config = MetricsConfig & TraceConfig & GlobalConfig;
7677
export const defaultConfig: Config = {
7778
apiKey: "",
7879
apiKeyKMS: "",
80+
apiKeySecretARN: "",
7981
autoPatchHTTP: true,
8082
captureLambdaPayload: false,
8183
captureLambdaPayloadMaxDepth: 10,
@@ -344,6 +346,10 @@ function getConfig(userConfig?: Partial<Config>): Config {
344346
config.apiKeyKMS = getEnvValue(apiKeyKMSEnvVar, "");
345347
}
346348

349+
if (config.apiKeySecretARN === "") {
350+
config.apiKeySecretARN = getEnvValue(apiKeySecretARNEnvVar, "");
351+
}
352+
347353
if (userConfig === undefined || userConfig.injectLogContext === undefined) {
348354
const result = getEnvValue(logInjectionEnvVar, "true").toLowerCase();
349355
config.injectLogContext = result === "true";

src/metrics/listener.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ import StatsDClient from "hot-shots";
99
import { Context } from "aws-lambda";
1010
jest.mock("hot-shots");
1111

12+
jest.mock("aws-sdk/clients/secretsmanager", () => {
13+
return jest.fn().mockImplementation(() => ({
14+
getSecretValue: jest.fn().mockReturnValue({
15+
promise: jest.fn().mockResolvedValue({
16+
SecretString: "api-key-secret",
17+
}),
18+
}),
19+
}));
20+
});
21+
1222
const siteURL = "example.com";
1323

1424
class MockKMS {
@@ -34,6 +44,7 @@ describe("MetricsListener", () => {
3444
const listener = new MetricsListener(kms as any, {
3545
apiKey: "api-key",
3646
apiKeyKMS: "kms-api-key-encrypted",
47+
apiKeySecretARN: "api-key-secret-arn",
3748
enhancedMetrics: false,
3849
logForwarding: false,
3950
shouldRetryMetrics: false,
@@ -54,6 +65,7 @@ describe("MetricsListener", () => {
5465
const listener = new MetricsListener(kms as any, {
5566
apiKey: "",
5667
apiKeyKMS: "kms-api-key-encrypted",
68+
apiKeySecretARN: "api-key-secret-arn",
5769
enhancedMetrics: false,
5870
logForwarding: false,
5971
shouldRetryMetrics: false,
@@ -67,11 +79,35 @@ describe("MetricsListener", () => {
6779

6880
expect(nock.isDone()).toBeTruthy();
6981
});
82+
83+
it("extracts the API Key from the secret manager to send a metric", async () => {
84+
nock("https://api.example.com").post("/api/v1/distribution_points?api_key=api-key-secret").reply(200, {});
85+
86+
const kms = new MockKMS("kms-api-key-decrypted");
87+
const listener = new MetricsListener(kms as any, {
88+
apiKey: "",
89+
apiKeyKMS: "",
90+
apiKeySecretARN: "api-key-secret-arn",
91+
enhancedMetrics: false,
92+
logForwarding: false,
93+
shouldRetryMetrics: false,
94+
localTesting: false,
95+
siteURL,
96+
});
97+
98+
await listener.onStartInvocation({});
99+
listener.sendDistributionMetricWithDate("my-metric", 10, new Date(), false, "tag:a", "tag:b");
100+
await listener.onCompleteInvocation();
101+
102+
expect(nock.isDone()).toBeTruthy();
103+
});
104+
70105
it("doesn't throw an error if it can't get a valid apiKey", async () => {
71106
const kms = new MockKMS("kms-api-key-decrypted", new Error("The error"));
72107
const listener = new MetricsListener(kms as any, {
73108
apiKey: "",
74109
apiKeyKMS: "kms-api-key-encrypted",
110+
apiKeySecretARN: "api-key-secret-arn",
75111
enhancedMetrics: false,
76112
logForwarding: false,
77113
shouldRetryMetrics: false,
@@ -91,6 +127,7 @@ describe("MetricsListener", () => {
91127
const listener = new MetricsListener(kms as any, {
92128
apiKey: "api-key",
93129
apiKeyKMS: "kms-api-key-encrypted",
130+
apiKeySecretARN: "api-key-secret-arn",
94131
enhancedMetrics: false,
95132
logForwarding: true,
96133
shouldRetryMetrics: false,
@@ -124,6 +161,7 @@ describe("MetricsListener", () => {
124161
const listener = new MetricsListener(kms as any, {
125162
apiKey: "api-key",
126163
apiKeyKMS: "",
164+
apiKeySecretARN: "api-key-secret-arn",
127165
enhancedMetrics: false,
128166
logForwarding: true,
129167
shouldRetryMetrics: false,
@@ -159,6 +197,7 @@ describe("MetricsListener", () => {
159197
const listener = new MetricsListener(kms as any, {
160198
apiKey: "api-key",
161199
apiKeyKMS: "",
200+
apiKeySecretARN: "api-key-secret-arn",
162201
enhancedMetrics: false,
163202
logForwarding: false,
164203
shouldRetryMetrics: false,
@@ -198,6 +237,7 @@ describe("MetricsListener", () => {
198237
const listener = new MetricsListener(kms as any, {
199238
apiKey: "api-key",
200239
apiKeyKMS: "",
240+
apiKeySecretARN: "api-key-secret-arn",
201241
enhancedMetrics: false,
202242
logForwarding: false,
203243
shouldRetryMetrics: false,
@@ -219,6 +259,7 @@ describe("MetricsListener", () => {
219259
const listener = new MetricsListener(kms as any, {
220260
apiKey: "api-key",
221261
apiKeyKMS: "kms-api-key-encrypted",
262+
apiKeySecretARN: "api-key-secret-arn",
222263
enhancedMetrics: false,
223264
logForwarding: true,
224265
shouldRetryMetrics: false,

src/metrics/listener.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export interface MetricsConfig {
2828
* be decrypted before any metrics are sent.
2929
*/
3030
apiKeyKMS: string;
31+
/**
32+
* An api key stored in secrets manager used to talk to the Datadog API.
33+
*/
34+
apiKeySecretARN: string;
3135
/**
3236
* The site of the Datadog URL to send to. This should either be 'datadoghq.com', (default),
3337
* or 'datadoghq.eu', for customers in the eu.
@@ -215,6 +219,17 @@ export class MetricsListener {
215219
logError("couldn't decrypt kms api key", error as Error);
216220
}
217221
}
222+
223+
if (config.apiKeySecretARN !== "") {
224+
try {
225+
const { default: secretsClient } = await import("aws-sdk/clients/secretsmanager");
226+
const secretsManager = new secretsClient();
227+
const secret = await secretsManager.getSecretValue({ SecretId: config.apiKeySecretARN }).promise();
228+
return secret?.SecretString ?? "";
229+
} catch (error) {
230+
logError("couldn't get secrets manager api key", error as Error);
231+
}
232+
}
218233
return "";
219234
}
220235

0 commit comments

Comments
 (0)