-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(NODE-5393): Migrate AWS signature v4 logic into driver #4824
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
Open
PavelSafronov
wants to merge
25
commits into
mongodb:main
Choose a base branch
from
PavelSafronov:NODE-5393
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+408
−137
Open
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
7cc1156
sigv4 implementation
PavelSafronov 811d453
fix issues and add a test, and a simple way to run it
PavelSafronov a0ba1ec
added unit tests for the signing logic, added comments about how to c…
PavelSafronov 449d677
Merge branch 'main' into NODE-5393
PavelSafronov a44f3b4
added test for undefined credentials
PavelSafronov 221044d
pr feedback:
PavelSafronov 72ab61d
removed extraneous integ test and moved its logic into an existing aw…
PavelSafronov 037bcf8
minor fixes
PavelSafronov fe3c90b
use webcrypto for new code
PavelSafronov 021f9de
use ByteUtils.toHex
PavelSafronov d7966a3
use ByteUtils.encodeUTF8Into
PavelSafronov 3a2a0ee
pr feedback
PavelSafronov 9178f66
Update src/cmap/auth/aws4.ts
PavelSafronov 5a8380f
pr feedback
PavelSafronov a3c06e4
minor fix
PavelSafronov 26fecf5
Merge branch 'main' into NODE-5393
PavelSafronov a625dc5
Merge branch 'main' into NODE-5393
PavelSafronov 2e69f64
removing unnecessary bit of code
PavelSafronov 31f49e7
Merge branch 'main' into NODE-5393
PavelSafronov 59f3e26
update aws4 test
PavelSafronov 41a18ab
Merge branch 'main' into NODE-5393
PavelSafronov 178b90a
pr feedback
PavelSafronov 4e88199
add aws4 as dev dependency and verify our code generates the same sig…
PavelSafronov a4d722a
make aws4 a dev dependency
PavelSafronov 6fffef6
Merge branch 'main' into NODE-5393
PavelSafronov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,5 @@ cd $DRIVERS_TOOLS/.evergreen/auth_aws | |
|
|
||
| cd $BEFORE | ||
|
|
||
| npm install --no-save aws4 | ||
|
|
||
| # revert to show test output | ||
| set -x | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| cd $DRIVERS_TOOLS/.evergreen/auth_aws | ||
|
|
||
| . ./activate-authawsvenv.sh | ||
|
|
||
| # Test with permanent credentials | ||
| . aws_setup.sh env-creds | ||
| unset MONGODB_URI | ||
| echo "AWS_SESSION_TOKEN is set to '${AWS_SESSION_TOKEN-NOT SET}'" | ||
| npm run check:test -- --grep "AwsSigV4" | ||
|
|
||
| # Test with session credentials | ||
| . aws_setup.sh session-creds | ||
| unset MONGODB_URI | ||
| echo "AWS_SESSION_TOKEN is set to '${AWS_SESSION_TOKEN-NOT SET}'" | ||
| npm run check:test -- --grep "AwsSigV4" | ||
|
|
||
| # Test with missing credentials | ||
| unset MONGODB_URI | ||
| unset AWS_ACCESS_KEY_ID | ||
| unset AWS_SECRET_ACCESS_KEY | ||
| unset AWS_SESSION_TOKEN | ||
| npm run check:test -- --grep "AwsSigV4" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import * as crypto from 'node:crypto'; | ||
|
|
||
| export type Options = { | ||
| path: '/'; | ||
| body: string; | ||
| host: string; | ||
| method: 'POST'; | ||
| headers: { | ||
| 'Content-Type': 'application/x-www-form-urlencoded'; | ||
| 'Content-Length': number; | ||
| 'X-MongoDB-Server-Nonce': string; | ||
| 'X-MongoDB-GS2-CB-Flag': 'n'; | ||
| }; | ||
| service: string; | ||
| region: string; | ||
| date?: Date; | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| export type AwsSessionCredentials = { | ||
| accessKeyId: string; | ||
| secretAccessKey: string; | ||
| sessionToken: string; | ||
| }; | ||
|
|
||
| export type AwsLongtermCredentials = { | ||
| accessKeyId: string; | ||
| secretAccessKey: string; | ||
| }; | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| export type SignedHeaders = { | ||
| headers: { | ||
| Authorization: string; | ||
| 'X-Amz-Date': string; | ||
| }; | ||
| }; | ||
|
|
||
| export interface AWS4 { | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /** | ||
| * Created these inline types to better assert future usage of this API | ||
| * @param options - options for request | ||
| * @param credentials - AWS credential details, sessionToken should be omitted entirely if its false-y | ||
| */ | ||
| sign( | ||
| this: void, | ||
| options: Options, | ||
| credentials: AwsSessionCredentials | AwsLongtermCredentials | undefined | ||
| ): SignedHeaders; | ||
| } | ||
|
|
||
| const getHash = (str: string): string => { | ||
| return crypto.createHash('sha256').update(str, 'utf8').digest('hex'); | ||
| }; | ||
| const getHmacArray = (key: string | Uint8Array, str: string): Uint8Array => { | ||
addaleax marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return crypto.createHmac('sha256', key).update(str, 'utf8').digest(); | ||
| }; | ||
| const getHmacString = (key: Uint8Array, str: string): string => { | ||
| return crypto.createHmac('sha256', key).update(str, 'utf8').digest('hex'); | ||
| }; | ||
addaleax marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const getEnvCredentials = () => { | ||
| const env = process.env; | ||
| return { | ||
| accessKeyId: env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY, | ||
| secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY, | ||
| sessionToken: env.AWS_SESSION_TOKEN | ||
| }; | ||
| }; | ||
|
|
||
| const convertHeaderValue = (value: string | number) => { | ||
| return value.toString().trim().replace(/\s+/g, ' '); | ||
| }; | ||
|
|
||
| export function aws4Sign( | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this: void, | ||
| options: Options, | ||
| credentials: AwsSessionCredentials | AwsLongtermCredentials | undefined | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ): SignedHeaders { | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const method = options.method; | ||
| const canonicalUri = options.path; | ||
| const canonicalQuerystring = ''; | ||
| const creds = credentials || getEnvCredentials(); | ||
|
|
||
| const date = options.date || new Date(); | ||
| const requestDateTime = date.toISOString().replace(/[:-]|\.\d{3}/g, ''); | ||
| const requestDate = requestDateTime.substring(0, 8); | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const headers: string[] = [ | ||
baileympearson marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| `content-length:${convertHeaderValue(options.headers['Content-Length'])}`, | ||
| `content-type:${convertHeaderValue(options.headers['Content-Type'])}`, | ||
| `host:${convertHeaderValue(options.host)}`, | ||
| `x-amz-date:${convertHeaderValue(requestDateTime)}`, | ||
| `x-mongodb-gs2-cb-flag:${convertHeaderValue(options.headers['X-MongoDB-GS2-CB-Flag'])}`, | ||
| `x-mongodb-server-nonce:${convertHeaderValue(options.headers['X-MongoDB-Server-Nonce'])}` | ||
| ]; | ||
| if ('sessionToken' in creds && creds.sessionToken) { | ||
| headers.push(`x-amz-security-token:${convertHeaderValue(creds.sessionToken)}`); | ||
| } | ||
| const canonicalHeaders = headers.sort().join('\n'); | ||
| const canonicalHeaderNames = headers.map(header => header.split(':', 2)[0].toLowerCase()); | ||
| const signedHeaders = canonicalHeaderNames.sort().join(';'); | ||
|
|
||
| const hashedPayload = getHash(options.body || ''); | ||
|
|
||
| const canonicalRequest = [ | ||
| method, | ||
| canonicalUri, | ||
| canonicalQuerystring, | ||
| canonicalHeaders + '\n', | ||
| signedHeaders, | ||
| hashedPayload | ||
| ].join('\n'); | ||
|
|
||
| const canonicalRequestHash = getHash(canonicalRequest); | ||
| const credentialScope = `${requestDate}/${options.region}/${options.service}/aws4_request`; | ||
|
|
||
| const stringToSign = [ | ||
| 'AWS4-HMAC-SHA256', | ||
| requestDateTime, | ||
| credentialScope, | ||
| canonicalRequestHash | ||
| ].join('\n'); | ||
|
|
||
| const dateKey = getHmacArray('AWS4' + creds.secretAccessKey, requestDate); | ||
| const dateRegionKey = getHmacArray(dateKey, options.region); | ||
| const dateRegionServiceKey = getHmacArray(dateRegionKey, options.service); | ||
| const signingKey = getHmacArray(dateRegionServiceKey, 'aws4_request'); | ||
| const signature = getHmacString(signingKey, stringToSign); | ||
|
|
||
| const authorizationHeader = [ | ||
| 'AWS4-HMAC-SHA256 Credential=' + creds.accessKeyId + '/' + credentialScope, | ||
| 'SignedHeaders=' + signedHeaders, | ||
| 'Signature=' + signature | ||
| ].join(', '); | ||
|
|
||
| return { | ||
| headers: { | ||
| Authorization: authorizationHeader, | ||
| 'X-Amz-Date': requestDateTime | ||
| } | ||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.