Skip to content

Commit dab10b2

Browse files
authored
[breaking] FIPS compliant metrics + secrets management (#649)
* determine fips mode based on region+override env var * update kms-service.ts * update listener.ts * fix test * move region parsing tests to fips.spec.ts * nit
1 parent 45d33ad commit dab10b2

File tree

6 files changed

+88
-53
lines changed

6 files changed

+88
-53
lines changed

src/metrics/kms-service.spec.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,22 +122,27 @@ describe("KMSService", () => {
122122
});
123123

124124
it("configures FIPS endpoint for GovCloud regions", async () => {
125-
try {
126-
process.env.AWS_REGION = "us-gov-west-1";
125+
jest.resetModules();
126+
127+
jest.mock("../utils/fips", () => ({
128+
AWS_REGION: "us-gov-west-1",
129+
FIPS_MODE_ENABLED: true,
130+
}));
127131

128-
const mockKmsConstructor = jest.fn();
129-
jest.mock("aws-sdk/clients/kms", () => mockKmsConstructor);
130-
mockKmsConstructor.mockImplementation(() => ({
131-
decrypt: () => ({
132-
promise: () => Promise.resolve({ Plaintext: Buffer.from(EXPECTED_RESULT) }),
133-
}),
134-
}));
132+
const mockKmsConstructor = jest.fn().mockImplementation(() => ({
133+
decrypt: () => ({
134+
promise: () => Promise.resolve({ Plaintext: Buffer.from(EXPECTED_RESULT) }),
135+
}),
136+
}));
137+
jest.mock("aws-sdk/clients/kms", () => mockKmsConstructor);
135138

136-
// Create service and call decrypt
139+
// tslint:disable-next-line:no-shadowed-variable
140+
const { KMSService } = require("./kms-service");
141+
142+
try {
137143
const kmsService = new KMSService();
138144
await kmsService.decrypt(ENCRYPTED_KEY);
139145

140-
// Verify FIPS endpoint was used
141146
expect(mockKmsConstructor).toHaveBeenCalledWith({
142147
endpoint: "https://kms-fips.us-gov-west-1.amazonaws.com",
143148
});

src/metrics/kms-service.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// in the Lambda environment anyway), we use require to import the SDK.
33

44
import { logDebug } from "../utils";
5+
import { AWS_REGION, FIPS_MODE_ENABLED } from "../utils/fips";
56

67
export class KMSService {
78
private encryptionContext;
@@ -14,16 +15,12 @@ export class KMSService {
1415
const buffer = Buffer.from(ciphertext, "base64");
1516
let kms;
1617

17-
const region = process.env.AWS_REGION;
18-
const isGovRegion = region !== undefined && region.startsWith("us-gov-");
19-
if (isGovRegion) {
20-
logDebug("Govcloud region detected. Using FIPs endpoints for secrets management.");
21-
}
2218
let kmsClientParams = {};
23-
if (isGovRegion) {
19+
if (FIPS_MODE_ENABLED) {
20+
logDebug("FIPS mode is enabled, Using FIPS endpoints for secrets management.");
2421
// Endpoints: https://docs.aws.amazon.com/general/latest/gr/kms.html
2522
kmsClientParams = {
26-
endpoint: `https://kms-fips.${region}.amazonaws.com`,
23+
endpoint: `https://kms-fips.${AWS_REGION}.amazonaws.com`,
2724
};
2825
}
2926

src/metrics/listener.spec.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -123,41 +123,11 @@ describe("MetricsListener", () => {
123123
await expect(listener.onCompleteInvocation()).resolves.toEqual(undefined);
124124
});
125125

126-
it("configures FIPS endpoint for GovCloud regions", async () => {
127-
try {
128-
process.env.AWS_REGION = "us-gov-west-1";
129-
const secretsManagerModule = require("@aws-sdk/client-secrets-manager");
130-
const secretsManagerSpy = jest.spyOn(secretsManagerModule, "SecretsManager");
131-
132-
const kms = new MockKMS("kms-api-key-decrypted");
133-
const listener = new MetricsListener(kms as any, {
134-
apiKey: "",
135-
apiKeyKMS: "",
136-
apiKeySecretARN: "arn:aws:secretsmanager:us-gov-west-1:1234567890:secret:key-name-123ABC",
137-
enhancedMetrics: false,
138-
logForwarding: false,
139-
shouldRetryMetrics: false,
140-
localTesting: false,
141-
siteURL,
142-
});
143-
144-
await listener.onStartInvocation({});
145-
await listener.onCompleteInvocation();
146-
147-
expect(secretsManagerSpy).toHaveBeenCalledWith({
148-
useFipsEndpoint: true,
149-
region: "us-gov-west-1",
150-
});
151-
152-
secretsManagerSpy.mockRestore();
153-
} finally {
154-
process.env.AWS_REGION = "us-east-1";
155-
}
156-
});
157-
158126
it("uses correct secrets region", async () => {
159127
try {
160128
process.env.AWS_REGION = "us-east-1";
129+
130+
// tslint:disable-next-line:no-shadowed-variable
161131
const secretsManagerModule = require("@aws-sdk/client-secrets-manager");
162132
const secretsManagerSpy = jest.spyOn(secretsManagerModule, "SecretsManager");
163133

src/metrics/listener.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Distribution } from "./model";
77
import { Context } from "aws-lambda";
88
import { getEnhancedMetricTags } from "./enhanced-metrics";
99
import { LambdaDogStatsD } from "./dogstatsd";
10+
import { FIPS_MODE_ENABLED } from "../utils/fips";
1011

1112
const METRICS_BATCH_SEND_INTERVAL = 10000; // 10 seconds
1213
const HISTORICAL_METRICS_THRESHOLD_HOURS = 4 * 60 * 60 * 1000; // 4 hours
@@ -154,6 +155,13 @@ export class MetricsListener {
154155
}
155156

156157
// Otherwise, send directly to DD API (not FIPs compliant!)
158+
if (FIPS_MODE_ENABLED) {
159+
logDebug(
160+
"FIPS mode is enabled, so sending metrics via the Datadog API is not possible. (1) Install the extension, (2) enable log forwarding, or (3) disable FIPS mode.",
161+
);
162+
return;
163+
}
164+
157165
// Add global tags to metrics sent to the API
158166
if (this.globalTags !== undefined && this.globalTags.length > 0) {
159167
tags = [...tags, ...this.globalTags];
@@ -210,10 +218,8 @@ export class MetricsListener {
210218
try {
211219
const { SecretsManager } = await import("@aws-sdk/client-secrets-manager");
212220
const secretRegion = config.apiKeySecretARN.split(":")[3];
213-
const lambdaRegion = process.env.AWS_REGION;
214-
const isGovRegion = lambdaRegion !== undefined && lambdaRegion.startsWith("us-gov-");
215221
const secretsManager = new SecretsManager({
216-
useFipsEndpoint: isGovRegion,
222+
useFipsEndpoint: FIPS_MODE_ENABLED,
217223
region: secretRegion,
218224
});
219225
const secret = await secretsManager.getSecretValue({ SecretId: config.apiKeySecretARN });

src/utils/fips.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
describe("fips.ts", () => {
2+
const ORIGINAL_ENV = process.env;
3+
4+
beforeEach(() => {
5+
jest.resetModules();
6+
process.env = { ...ORIGINAL_ENV };
7+
});
8+
9+
afterAll(() => {
10+
process.env = ORIGINAL_ENV;
11+
});
12+
13+
it("enables FIPS mode in GovCloud by default", () => {
14+
process.env.AWS_REGION = "us-gov-west-1";
15+
delete process.env.DD_LAMBDA_FIPS_MODE;
16+
17+
const { FIPS_MODE_ENABLED } = require("./fips");
18+
expect(FIPS_MODE_ENABLED).toBe(true);
19+
});
20+
21+
it("disables FIPS mode in standard region by default", () => {
22+
process.env.AWS_REGION = "us-east-1";
23+
delete process.env.DD_LAMBDA_FIPS_MODE;
24+
25+
const { FIPS_MODE_ENABLED } = require("./fips");
26+
expect(FIPS_MODE_ENABLED).toBe(false);
27+
});
28+
29+
it("enables FIPS mode when env var is set to true in a standard region", () => {
30+
process.env.AWS_REGION = "us-east-1";
31+
process.env.DD_LAMBDA_FIPS_MODE = "true";
32+
33+
const { FIPS_MODE_ENABLED } = require("./fips");
34+
expect(FIPS_MODE_ENABLED).toBe(true);
35+
});
36+
37+
it("disables FIPS mode when DD_LAMBDA_FIPS_MODE=false in GovCloud region", () => {
38+
process.env.AWS_REGION = "us-gov-east-1";
39+
process.env.DD_LAMBDA_FIPS_MODE = "false";
40+
41+
const { FIPS_MODE_ENABLED } = require("./fips");
42+
expect(FIPS_MODE_ENABLED).toBe(false);
43+
});
44+
});

src/utils/fips.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { logDebug } from "./log";
2+
3+
export const AWS_REGION = process.env.AWS_REGION ?? "";
4+
const isGovRegion = AWS_REGION.startsWith("us-gov-");
5+
6+
// Determine FIPS mode default (enabled in Gov regions) and override via env var
7+
const defaultFips = isGovRegion ? "true" : "false";
8+
const rawFipsEnv = process.env.DD_LAMBDA_FIPS_MODE ?? defaultFips;
9+
export const FIPS_MODE_ENABLED = rawFipsEnv.toLowerCase() === "true";
10+
11+
if (isGovRegion || FIPS_MODE_ENABLED) {
12+
logDebug(`Node Lambda Layer FIPS mode is ${FIPS_MODE_ENABLED ? "enabled" : "not enabled"}.`);
13+
}

0 commit comments

Comments
 (0)