aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Yihua Zhang <yihuaz@google.com>2018-12-05 10:27:01 -0800
committerGravatar Yihua Zhang <yihuaz@google.com>2018-12-05 10:27:01 -0800
commit750e80ea1c12315ce987ed7b5c38595f698cf355 (patch)
treeb11a030e1c1a6372a1f399cfc505464921e467df
parent9bd5b3778abbb2564544a69d9c99d3c87a3354eb (diff)
bring back original network test for metadata server detection
-rw-r--r--src/core/lib/security/credentials/google_default/google_default_credentials.cc134
-rw-r--r--test/core/security/credentials_test.cc76
2 files changed, 188 insertions, 22 deletions
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
index fcab252959..7474380c05 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc
@@ -49,9 +49,11 @@
/* -- Default credentials. -- */
-static int g_compute_engine_detection_done = 0;
-static int g_need_compute_engine_creds = 0;
+static int g_metadata_server_detection_done = 0;
+static int g_metadata_server_available = 0;
+static int g_is_on_gce = 0;
static gpr_mu g_state_mu;
+static gpr_mu* g_polling_mu;
static gpr_once g_once = GPR_ONCE_INIT;
static grpc_core::internal::grpc_gce_tenancy_checker g_gce_tenancy_checker =
grpc_alts_is_running_on_gcp;
@@ -89,15 +91,20 @@ static grpc_security_status google_default_create_security_connector(
bool use_alts =
is_grpclb_load_balancer || is_backend_from_grpclb_load_balancer;
grpc_security_status status = GRPC_SECURITY_ERROR;
+ /* Return failure if ALTS is selected but not running on GCE. */
+ if (use_alts && !g_is_on_gce) {
+ goto end;
+ }
status = use_alts ? c->alts_creds->vtable->create_security_connector(
c->alts_creds, call_creds, target, args, sc, new_args)
: c->ssl_creds->vtable->create_security_connector(
c->ssl_creds, call_creds, target, args, sc, new_args);
- /* grpclb-specific channel args are removed from the channel args set
- * to ensure backends and fallback adresses will have the same set of channel
- * args. By doing that, it guarantees the connections to backends will not be
- * torn down and re-connected when switching in and out of fallback mode.
- */
+/* grpclb-specific channel args are removed from the channel args set
+ * to ensure backends and fallback adresses will have the same set of channel
+ * args. By doing that, it guarantees the connections to backends will not be
+ * torn down and re-connected when switching in and out of fallback mode.
+ */
+end:
if (use_alts) {
static const char* args_to_remove[] = {
GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER,
@@ -113,6 +120,93 @@ static grpc_channel_credentials_vtable google_default_credentials_vtable = {
google_default_credentials_destruct,
google_default_create_security_connector, nullptr};
+static void on_metadata_server_detection_http_response(void* user_data,
+ grpc_error* error) {
+ compute_engine_detector* detector =
+ static_cast<compute_engine_detector*>(user_data);
+ if (error == GRPC_ERROR_NONE && detector->response.status == 200 &&
+ detector->response.hdr_count > 0) {
+ /* Internet providers can return a generic response to all requests, so
+ it is necessary to check that metadata header is present also. */
+ size_t i;
+ for (i = 0; i < detector->response.hdr_count; i++) {
+ grpc_http_header* header = &detector->response.hdrs[i];
+ if (strcmp(header->key, "Metadata-Flavor") == 0 &&
+ strcmp(header->value, "Google") == 0) {
+ detector->success = 1;
+ break;
+ }
+ }
+ }
+ gpr_mu_lock(g_polling_mu);
+ detector->is_done = 1;
+ GRPC_LOG_IF_ERROR(
+ "Pollset kick",
+ grpc_pollset_kick(grpc_polling_entity_pollset(&detector->pollent),
+ nullptr));
+ gpr_mu_unlock(g_polling_mu);
+}
+
+static void destroy_pollset(void* p, grpc_error* e) {
+ grpc_pollset_destroy(static_cast<grpc_pollset*>(p));
+}
+
+static int is_metadata_server_reachable() {
+ compute_engine_detector detector;
+ grpc_httpcli_request request;
+ grpc_httpcli_context context;
+ grpc_closure destroy_closure;
+ /* The http call is local. If it takes more than one sec, it is for sure not
+ on compute engine. */
+ grpc_millis max_detection_delay = GPR_MS_PER_SEC;
+ grpc_pollset* pollset =
+ static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
+ grpc_pollset_init(pollset, &g_polling_mu);
+ detector.pollent = grpc_polling_entity_create_from_pollset(pollset);
+ detector.is_done = 0;
+ detector.success = 0;
+ memset(&detector.response, 0, sizeof(detector.response));
+ memset(&request, 0, sizeof(grpc_httpcli_request));
+ request.host = (char*)GRPC_COMPUTE_ENGINE_DETECTION_HOST;
+ request.http.path = (char*)"/";
+ grpc_httpcli_context_init(&context);
+ grpc_resource_quota* resource_quota =
+ grpc_resource_quota_create("google_default_credentials");
+ grpc_httpcli_get(
+ &context, &detector.pollent, resource_quota, &request,
+ grpc_core::ExecCtx::Get()->Now() + max_detection_delay,
+ GRPC_CLOSURE_CREATE(on_metadata_server_detection_http_response, &detector,
+ grpc_schedule_on_exec_ctx),
+ &detector.response);
+ grpc_resource_quota_unref_internal(resource_quota);
+ grpc_core::ExecCtx::Get()->Flush();
+ /* Block until we get the response. This is not ideal but this should only be
+ called once for the lifetime of the process by the default credentials. */
+ gpr_mu_lock(g_polling_mu);
+ while (!detector.is_done) {
+ grpc_pollset_worker* worker = nullptr;
+ if (!GRPC_LOG_IF_ERROR(
+ "pollset_work",
+ grpc_pollset_work(grpc_polling_entity_pollset(&detector.pollent),
+ &worker, GRPC_MILLIS_INF_FUTURE))) {
+ detector.is_done = 1;
+ detector.success = 0;
+ }
+ }
+ gpr_mu_unlock(g_polling_mu);
+ grpc_httpcli_context_destroy(&context);
+ GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset,
+ grpc_polling_entity_pollset(&detector.pollent),
+ grpc_schedule_on_exec_ctx);
+ grpc_pollset_shutdown(grpc_polling_entity_pollset(&detector.pollent),
+ &destroy_closure);
+ g_polling_mu = nullptr;
+ grpc_core::ExecCtx::Get()->Flush();
+ gpr_free(grpc_polling_entity_pollset(&detector.pollent));
+ grpc_http_response_destroy(&detector.response);
+ return detector.success;
+}
+
/* Takes ownership of creds_path if not NULL. */
static grpc_error* create_default_creds_from_path(
char* creds_path, grpc_call_credentials** creds) {
@@ -182,7 +276,6 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Failed to create Google credentials");
grpc_error* err;
- int need_compute_engine_creds = 0;
grpc_core::ExecCtx exec_ctx;
GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
@@ -202,16 +295,25 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
error = grpc_error_add_child(error, err);
gpr_mu_lock(&g_state_mu);
- /* At last try to see if we're on compute engine (do the detection only once
- since it requires a network test). */
- if (!g_compute_engine_detection_done) {
- g_need_compute_engine_creds = g_gce_tenancy_checker();
- g_compute_engine_detection_done = 1;
+
+ /* Try a platform-provided hint for GCE. */
+ if (!g_metadata_server_detection_done) {
+ g_is_on_gce = g_gce_tenancy_checker();
+ g_metadata_server_detection_done = g_is_on_gce;
+ g_metadata_server_available = g_is_on_gce;
+ }
+ /* TODO: Add a platform-provided hint for GAE. */
+
+ /* Do a network test for metadata server. */
+ if (!g_metadata_server_detection_done) {
+ bool detected = is_metadata_server_reachable();
+ /* Do not cache detecion result if netowrk test returns false. */
+ g_metadata_server_detection_done = detected;
+ g_metadata_server_available = detected;
}
- need_compute_engine_creds = g_need_compute_engine_creds;
gpr_mu_unlock(&g_state_mu);
- if (need_compute_engine_creds) {
+ if (g_metadata_server_available) {
call_creds = grpc_google_compute_engine_credentials_create(nullptr);
if (call_creds == nullptr) {
error = grpc_error_add_child(
@@ -259,7 +361,7 @@ void grpc_flush_cached_google_default_credentials(void) {
grpc_core::ExecCtx exec_ctx;
gpr_once_init(&g_once, init_default_credentials);
gpr_mu_lock(&g_state_mu);
- g_compute_engine_detection_done = 0;
+ g_metadata_server_detection_done = 0;
gpr_mu_unlock(&g_state_mu);
}
diff --git a/test/core/security/credentials_test.cc b/test/core/security/credentials_test.cc
index b3e3c3c741..a7a6050ec0 100644
--- a/test/core/security/credentials_test.cc
+++ b/test/core/security/credentials_test.cc
@@ -919,6 +919,22 @@ static void test_google_default_creds_refresh_token(void) {
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
}
+static int default_creds_metadata_server_detection_httpcli_get_success_override(
+ const grpc_httpcli_request* request, grpc_millis deadline,
+ grpc_closure* on_done, grpc_httpcli_response* response) {
+ *response = http_response(200, "");
+ grpc_http_header* headers =
+ static_cast<grpc_http_header*>(gpr_malloc(sizeof(*headers) * 1));
+ headers[0].key = gpr_strdup("Metadata-Flavor");
+ headers[0].value = gpr_strdup("Google");
+ response->hdr_count = 1;
+ response->hdrs = headers;
+ GPR_ASSERT(strcmp(request->http.path, "/") == 0);
+ GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
+ GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
+ return 1;
+}
+
static char* null_well_known_creds_path_getter(void) { return nullptr; }
static bool test_gce_tenancy_checker(void) {
@@ -963,26 +979,73 @@ static void test_google_default_creds_gce(void) {
grpc_override_well_known_credentials_path_getter(nullptr);
}
-static void test_no_google_default_creds(void) {
+static void test_google_default_creds_non_gce(void) {
+ grpc_core::ExecCtx exec_ctx;
+ expected_md emd[] = {
+ {"authorization", "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"}};
+ request_metadata_state* state =
+ make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
+ grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
+ nullptr, nullptr};
grpc_flush_cached_google_default_credentials();
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
grpc_override_well_known_credentials_path_getter(
null_well_known_creds_path_getter);
-
set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
g_test_gce_tenancy_checker_called = false;
g_test_is_on_gce = false;
+ /* Simulate a successful detection of metadata server. */
+ grpc_httpcli_set_override(
+ default_creds_metadata_server_detection_httpcli_get_success_override,
+ httpcli_post_should_not_be_called);
+ grpc_composite_channel_credentials* creds =
+ reinterpret_cast<grpc_composite_channel_credentials*>(
+ grpc_google_default_credentials_create());
+ /* Verify that the default creds actually embeds a GCE creds. */
+ GPR_ASSERT(creds != nullptr);
+ GPR_ASSERT(creds->call_creds != nullptr);
+ grpc_httpcli_set_override(compute_engine_httpcli_get_success_override,
+ httpcli_post_should_not_be_called);
+ run_request_metadata_test(creds->call_creds, auth_md_ctx, state);
+ grpc_core::ExecCtx::Get()->Flush();
+ GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
+ /* Cleanup. */
+ grpc_channel_credentials_unref(&creds->base);
+ grpc_httpcli_set_override(nullptr, nullptr);
+ grpc_override_well_known_credentials_path_getter(nullptr);
+}
+
+static int default_creds_gce_detection_httpcli_get_failure_override(
+ const grpc_httpcli_request* request, grpc_millis deadline,
+ grpc_closure* on_done, grpc_httpcli_response* response) {
+ /* No magic header. */
+ GPR_ASSERT(strcmp(request->http.path, "/") == 0);
+ GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
+ *response = http_response(200, "");
+ GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
+ return 1;
+}
+static void test_no_google_default_creds(void) {
+ grpc_flush_cached_google_default_credentials();
+ gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
+ grpc_override_well_known_credentials_path_getter(
+ null_well_known_creds_path_getter);
+ set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
+ g_test_gce_tenancy_checker_called = false;
+ g_test_is_on_gce = false;
+ grpc_httpcli_set_override(
+ default_creds_gce_detection_httpcli_get_failure_override,
+ httpcli_post_should_not_be_called);
/* Simulate a successful detection of GCE. */
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
-
- /* Try a second one. GCE detection should not occur anymore. */
+ /* Try a second one. GCE detection should occur again. */
g_test_gce_tenancy_checker_called = false;
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
- GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
-
+ GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
/* Cleanup. */
grpc_override_well_known_credentials_path_getter(nullptr);
+ grpc_httpcli_set_override(nullptr, nullptr);
}
typedef enum {
@@ -1233,6 +1296,7 @@ int main(int argc, char** argv) {
test_google_default_creds_auth_key();
test_google_default_creds_refresh_token();
test_google_default_creds_gce();
+ test_google_default_creds_non_gce();
test_no_google_default_creds();
test_metadata_plugin_success();
test_metadata_plugin_failure();