Skip to content

Commit 579407e

Browse files
authored
Use FIPs endpoint for KMS when in Govcloud region (#635)
1 parent ccf67c7 commit 579407e

File tree

2 files changed

+49
-6
lines changed

2 files changed

+49
-6
lines changed

src/metrics/kms-service.spec.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe("KMSService", () => {
100100
});
101101

102102
const kmsService = new KMSService();
103-
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"));
103+
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"), {});
104104
expect(Buffer.from(result).toString("ascii")).toEqual(EXPECTED_RESULT);
105105
fakeKmsCall.done();
106106
});
@@ -116,8 +116,34 @@ describe("KMSService", () => {
116116
});
117117

118118
const kmsService = new KMSService();
119-
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"));
119+
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"), {});
120120
expect(Buffer.from(result).toString("ascii")).toEqual(EXPECTED_RESULT);
121121
fakeKmsCall.done();
122122
});
123+
124+
it("configures FIPS endpoint for GovCloud regions", async () => {
125+
try {
126+
process.env.AWS_REGION = "us-gov-west-1";
127+
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+
}));
135+
136+
// Create service and call decrypt
137+
const kmsService = new KMSService();
138+
await kmsService.decrypt(ENCRYPTED_KEY);
139+
140+
// Verify FIPS endpoint was used
141+
expect(mockKmsConstructor).toHaveBeenCalledWith({
142+
endpoint: "https://kms-fips.us-gov-west-1.amazonaws.com",
143+
});
144+
} finally {
145+
process.env.AWS_REGION = "us-east-1";
146+
jest.restoreAllMocks();
147+
}
148+
});
123149
});

src/metrics/kms-service.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// In order to avoid the layer adding the 40mb aws-sdk to a deployment, (which is always available
22
// in the Lambda environment anyway), we use require to import the SDK.
33

4+
import { logDebug } from "../utils";
5+
46
export class KMSService {
57
private encryptionContext;
68

@@ -12,6 +14,19 @@ export class KMSService {
1214
const buffer = Buffer.from(ciphertext, "base64");
1315
let kms;
1416

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+
}
22+
let kmsClientParams = {};
23+
if (isGovRegion) {
24+
// Endpoints: https://docs.aws.amazon.com/general/latest/gr/kms.html
25+
kmsClientParams = {
26+
endpoint: `https://kms-fips.${region}.amazonaws.com`,
27+
};
28+
}
29+
1530
// Explicitly try/catch this require to appease esbuild and ts compiler
1631
// otherwise users would need to mark this as `external`
1732
// see https://github.com/DataDog/datadog-lambda-js/pull/409
@@ -20,11 +35,12 @@ export class KMSService {
2035
} catch (err) {
2136
if ((err as any).code === "MODULE_NOT_FOUND") {
2237
// Node 18
23-
return this.decryptV3(buffer);
38+
return this.decryptV3(buffer, kmsClientParams);
2439
}
2540
}
2641
try {
27-
const kmsClient = new kms();
42+
// Configure KMS client to use FIPS endpoint
43+
const kmsClient = new kms(kmsClientParams);
2844

2945
// When the API key is encrypted using the AWS console, the function name is added as an encryption context.
3046
// When the API key is encrypted using the AWS CLI, no encryption context is added.
@@ -50,7 +66,7 @@ export class KMSService {
5066
}
5167

5268
// Node 18 or AWS SDK V3
53-
public async decryptV3(buffer: Buffer): Promise<string> {
69+
public async decryptV3(buffer: Buffer, kmsClientParams: any): Promise<string> {
5470
// tslint:disable-next-line: variable-name one-variable-per-declaration
5571
let KMSClient, DecryptCommand;
5672
// Explicitly try/catch this require to appease esbuild and ts compiler
@@ -61,7 +77,8 @@ export class KMSService {
6177
} catch (e) {
6278
throw Error("Can't load AWS SDK v2 or v3 to decrypt KMS key, custom metrics may not be sent");
6379
}
64-
const kmsClient = new KMSClient();
80+
81+
const kmsClient = new KMSClient(kmsClientParams);
6582
let result;
6683
try {
6784
const decryptCommand = new DecryptCommand({ CiphertextBlob: buffer });

0 commit comments

Comments
 (0)