8000 Use FIPs endpoint for KMS when in Govcloud region by nhulston · Pull Request #635 · DataDog/datadog-lambda-js · GitHub
[go: up one dir, main page]

Skip to content

Use FIPs endpoint for KMS when in Govcloud region #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
8000
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/metrics/kms-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe("KMSService", () => {
});

const kmsService = new KMSService();
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"));
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"), {});
expect(Buffer.from(result).toString("ascii")).toEqual(EXPECTED_RESULT);
fakeKmsCall.done();
});
Expand All @@ -116,8 +116,34 @@ describe("KMSService", () => {
});

const kmsService = new KMSService();
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"));
const result = await kmsService.decryptV3(Buffer.from(ENCRYPTED_KEY, "base64"), {});
expect(Buffer.from(result).toString("ascii")).toEqual(EXPECTED_RESULT);
fakeKmsCall.done();
});

it("configures FIPS endpoint for GovCloud regions", async () => {
try {
process.env.AWS_REGION = "us-gov-west-1";

const mockKmsConstructor = jest.fn();
jest.mock("aws-sdk/clients/kms", () => mockKmsConstructor);
mockKmsConstructor.mockImplementation(() => ({
decrypt: () => ({
promise: () => Promise.resolve({ Plaintext: Buffer.from(EXPECTED_RESULT) }),
}),
}));

// Create service and call decrypt
const kmsService = new KMSService();
await kmsService.decrypt(ENCRYPTED_KEY);

// Verify FIPS endpoint was used
expect(mockKmsConstructor).toHaveBeenCalledWith({
endpoint: "https://kms-fips.us-gov-west-1.amazonaws.com",
});
} finally {
process.env.AWS_REGION = "us-east-1";
jest.restoreAllMocks();
}
});
});
25 changes: 21 additions & 4 deletions src/metrics/kms-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// In order to avoid the layer adding the 40mb aws-sdk to a deployment, (which is always available
// in the Lambda environment anyway), we use require to import the SDK.

import { logDebug } from "../utils";

export class KMSService {
private encryptionContext;

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

const region = process.env.AWS_REGION;
const isGovRegion = region !== undefined && region.startsWith("us-gov-");
if (isGovRegion) {
logDebug("Govcloud region detected. Using FIPs endpoints for secrets management.");
}
let kmsClientParams = {};
if (isGovRegion) {
// Endpoints: https://docs.aws.amazon.com/general/latest/gr/kms.html
kmsClientParams = {
endpoint: `https://kms-fips.${region}.amazonaws.com`,
};
}

// Explicitly try/catch this require to appease esbuild and ts compiler
// otherwise users would need to mark this as `external`
// see https://github.com/DataDog/datadog-lambda-js/pull/409
Expand All @@ -20,11 +35,12 @@ export class KMSService {
} catch (err) {
if ((err as any).code === "MODULE_NOT_FOUND") {
// Node 18
return this.decryptV3(buffer);
return this.decryptV3(buffer, kmsClientParams);
}
}
try {
const kmsClient = new kms();
// Configure KMS client to use FIPS endpoint
const kmsClient = new kms(kmsClientParams);

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

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

const kmsClient = new KMSClient(kmsClientParams);
let result;
try {
const decryptCommand = new DecryptCommand({ CiphertextBlob: buffer });
Expand Down
Loading
0