-
Notifications
You must be signed in to change notification settings - Fork 455
[CDRIVER-4454] Automatic Azure KMS Credentials #1097
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
Changes from all commits
b9fface
5b20fc8
e758329
3d055b7
2ac4fad
c5a8f57
d14b796
b0b31c0
3a5cbe3
73c207b
b81d47e
ac3b6ab
494adb5
a1abbf7
1e245c9
dce0a4e
57c37fc
b89decd
9b4f52c
ba2807d
cad3810
c5ecf83
bfcc84a
c502346
93a125b
afe21a5
9af22d5
fc2d5b5
39ae7dc
39854a6
b93e72d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/** | ||
* Copyright 2022 MongoDB, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include "./mcd-azure.h" | ||
|
||
#include "mongoc-util-private.h" | ||
|
||
#define AZURE_API_VERSION "2018-02-01" | ||
|
||
static const char *const DEFAULT_METADATA_PATH = | ||
"/metadata/identity/oauth2/" | ||
"token?api-version=" AZURE_API_VERSION | ||
"&resource=https%3A%2F%2Fvault.azure.net"; | ||
|
||
void | ||
mcd_azure_imds_request_init (mcd_azure_imds_request *req) | ||
eramongodb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
BSON_ASSERT_PARAM (req); | ||
_mongoc_http_request_init (&req->req); | ||
// The HTTP host of the IMDS server | ||
req->req.host = "169.254.169.254"; | ||
// No body | ||
req->req.body = ""; | ||
// We GET | ||
req->req.method = "GET"; | ||
// 'Metadata: true' is required | ||
req->req.extra_headers = "Metadata: true\r\n" | ||
"Accept: application/json\r\n"; | ||
// The default path is suitable. In the future, we may want to add query | ||
// parameters to disambiguate a managed identity. | ||
req->req.path = bson_strdup (DEFAULT_METADATA_PATH); | ||
} | ||
|
||
void | ||
mcd_azure_imds_request_destroy (mcd_azure_imds_request *req) | ||
eramongodb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
BSON_ASSERT_PARAM (req); | ||
bson_free ((void *) req->req.path); | ||
*req = (mcd_azure_imds_request){0}; | ||
} | ||
|
||
bool | ||
mcd_azure_access_token_try_init_from_json_str (mcd_azure_access_token *out, | ||
const char *json, | ||
eramongodb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int len, | ||
bson_error_t *error) | ||
{ | ||
BSON_ASSERT_PARAM (out); | ||
BSON_ASSERT_PARAM (json); | ||
bool okay = false; | ||
|
||
if (len < 0) { | ||
// Detect from a null-terminated string | ||
len = strlen (json); | ||
} | ||
|
||
// Zero the output | ||
*out = (mcd_azure_access_token){0}; | ||
|
||
// Parse the JSON data | ||
bson_t bson; | ||
if (!bson_init_from_json (&bson, json, len, error)) { | ||
return false; | ||
} | ||
|
||
bson_iter_t iter; | ||
// access_token | ||
bool found = bson_iter_init_find (&iter, &bson, "access_token"); | ||
const char *const access_token = | ||
!found ? NULL : bson_iter_utf8 (&iter, NULL); | ||
// resource | ||
found = bson_iter_init_find (&iter, &bson, "resource"); | ||
const char *const resource = !found ? NULL : bson_iter_utf8 (&iter, NULL); | ||
// token_type | ||
found = bson_iter_init_find (&iter, &bson, "token_type"); | ||
const char *const token_type = !found ? NULL : bson_iter_utf8 (&iter, NULL); | ||
// expires_in | ||
found = bson_iter_init_find (&iter, &bson, "expires_in"); | ||
uint32_t expires_in_len = 0; | ||
const char *const expires_in_str = | ||
!found ? NULL : bson_iter_utf8 (&iter, &expires_in_len); | ||
|
||
if (!(access_token && resource && token_type && expires_in_str)) { | ||
bson_set_error ( | ||
error, | ||
MONGOC_ERROR_PROTOCOL_ERROR, | ||
64, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the error codes in each domain can have a different meaning. Two domains can have the same error code. Consider adding an error code to |
||
"One or more required JSON properties are missing/invalid: data: %.*s", | ||
len, | ||
json); | ||
} else { | ||
// Set the output, duplicate each string | ||
*out = (mcd_azure_access_token){ | ||
.access_token = bson_strdup (access_token), | ||
.resource = bson_strdup (resource), | ||
.token_type = bson_strdup (token_type), | ||
}; | ||
// "expires_in" encodes the number of seconds since the issue time for | ||
// which the token will be valid. strtoll() will saturate on range errors | ||
// and return zero on parse errors. | ||
char *parse_end; | ||
long long s = strtoll (expires_in_str, &parse_end, 0); | ||
if (parse_end != expires_in_str + expires_in_len) { | ||
// Did not parse the entire string. Bad | ||
bson_set_error ( | ||
error, | ||
MONGOC_ERROR_PROTOCOL, | ||
65, | ||
"Invalid 'expires_in' string \"%.*s\" from IMDS server", | ||
expires_in_len, | ||
expires_in_str); | ||
} else { | ||
out->expires_in = mcd_seconds (s); | ||
okay = true; | ||
} | ||
} | ||
|
||
bson_destroy (&bson); | ||
return okay; | ||
} | ||
|
||
|
||
void | ||
mcd_azure_access_token_destroy (mcd_azure_access_token *c) | ||
{ | ||
bson_free (c->access_token); | ||
bson_free (c->resource); | ||
bson_free (c->token_type); | ||
c->access_token = NULL; | ||
c->resource = NULL; | ||
c->token_type = NULL; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/** | ||
* Copyright 2022 MongoDB, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#ifndef MCD_AZURE_H_INCLUDED | ||
#define MCD_AZURE_H_INCLUDED | ||
|
||
#include "mongoc-prelude.h" | ||
|
||
#include <mongoc/mongoc.h> | ||
|
||
#include <mongoc/mongoc-http-private.h> | ||
|
||
#include <mcd-time.h> | ||
|
||
/** | ||
* @brief An Azure OAuth2 access token obtained from the Azure API | ||
*/ | ||
typedef struct mcd_azure_access_token { | ||
/// The access token string | ||
char *access_token; | ||
/// The resource of the token (the Azure resource for which it is valid) | ||
char *resource; | ||
/// The HTTP type of the token | ||
char *token_type; | ||
/// The duration after which it will the token will expires. This is relative | ||
/// to the "issue time" of the token. | ||
mcd_duration expires_in; | ||
} mcd_azure_access_token; | ||
|
||
/** | ||
* @brief Try to parse an Azure access token from an IMDS metadata JSON response | ||
* | ||
* @param out The token to initialize. Should be uninitialized. Must later be | ||
* destroyed by the caller. | ||
* @param json The JSON string body | ||
* @param len The length of 'body' | ||
* @param error An output parameter for errors | ||
* @retval true If 'out' was successfully initialized to a token. | ||
* @retval false Otherwise | ||
* | ||
* @note The 'out' token must later be given to @ref | ||
* mcd_azure_access_token_destroy | ||
*/ | ||
bool | ||
mcd_azure_access_token_try_init_from_json_str (mcd_azure_access_token *out, | ||
const char *json, | ||
int len, | ||
bson_error_t *error) | ||
BSON_GNUC_WARN_UNUSED_RESULT; | ||
|
||
/** | ||
* @brief Destroy and zero-fill an access token object | ||
* | ||
* @param token The access token to destroy | ||
*/ | ||
void | ||
mcd_azure_access_token_destroy (mcd_azure_access_token *token); | ||
|
||
/** | ||
* @brief An Azure IMDS HTTP request | ||
*/ | ||
typedef struct mcd_azure_imds_request { | ||
eramongodb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The underlying HTTP request object to be sent | ||
mongoc_http_request_t req; | ||
} mcd_azure_imds_request; | ||
|
||
/** | ||
* @brief Initialize a new IMDS HTTP request | ||
* | ||
* @param out The object to initialize | ||
* | ||
* @note the request must later be destroyed with mcd_azure_imds_request_destroy | ||
*/ | ||
void | ||
mcd_azure_imds_request_init (mcd_azure_imds_request *out); | ||
|
||
/** | ||
* @brief Destroy an IMDS request created with mcd_azure_imds_request_init() | ||
* | ||
* @param req | ||
*/ | ||
void | ||
mcd_azure_imds_request_destroy (mcd_azure_imds_request *req); | ||
|
||
#endif // MCD_AZURE_H_INCLUDED |
Uh oh!
There was an error while loading. Please reload this page.