add experiment

pull/35851/head
Mark D. Roth 1 year ago
parent 3296d34ea6
commit c470bf5e61
  1. 1
      BUILD
  2. 10
      bazel/experiments.bzl
  3. 11
      src/core/client_channel/http_proxy_mapper.cc
  4. 12
      src/core/lib/experiments/experiments.cc
  5. 11
      src/core/lib/experiments/experiments.h
  6. 5
      src/core/lib/experiments/experiments.yaml
  7. 2
      src/core/lib/experiments/rollouts.yaml
  8. 20
      src/core/lib/security/credentials/jwt/json_token.cc
  9. 44
      src/core/lib/security/credentials/jwt/jwt_verifier.cc
  10. 2
      test/core/security/BUILD

@ -3384,6 +3384,7 @@ grpc_cc_library(
"//src/core:arena_promise", "//src/core:arena_promise",
"//src/core:closure", "//src/core:closure",
"//src/core:error", "//src/core:error",
"//src/core:experiments",
"//src/core:gpr_manual_constructor", "//src/core:gpr_manual_constructor",
"//src/core:httpcli_ssl_credentials", "//src/core:httpcli_ssl_credentials",
"//src/core:iomgr_fwd", "//src/core:iomgr_fwd",

@ -17,6 +17,7 @@
"""Dictionary of tags to experiments so we know when to test different experiments.""" """Dictionary of tags to experiments so we know when to test different experiments."""
EXPERIMENT_ENABLES = { EXPERIMENT_ENABLES = {
"absl_base64": "absl_base64",
"call_status_override_on_cancellation": "call_status_override_on_cancellation", "call_status_override_on_cancellation": "call_status_override_on_cancellation",
"call_v3": "call_v3", "call_v3": "call_v3",
"canary_client_privacy": "canary_client_privacy", "canary_client_privacy": "canary_client_privacy",
@ -124,6 +125,9 @@ EXPERIMENTS = {
"round_robin_delegate_to_pick_first", "round_robin_delegate_to_pick_first",
"wrr_delegate_to_pick_first", "wrr_delegate_to_pick_first",
], ],
"credential_token_tests": [
"absl_base64",
],
"event_engine_listener_test": [ "event_engine_listener_test": [
"event_engine_listener", "event_engine_listener",
], ],
@ -201,6 +205,9 @@ EXPERIMENTS = {
"round_robin_delegate_to_pick_first", "round_robin_delegate_to_pick_first",
"wrr_delegate_to_pick_first", "wrr_delegate_to_pick_first",
], ],
"credential_token_tests": [
"absl_base64",
],
"flow_control_test": [ "flow_control_test": [
"write_size_cap", "write_size_cap",
"write_size_policy", "write_size_policy",
@ -288,6 +295,9 @@ EXPERIMENTS = {
"round_robin_delegate_to_pick_first", "round_robin_delegate_to_pick_first",
"wrr_delegate_to_pick_first", "wrr_delegate_to_pick_first",
], ],
"credential_token_tests": [
"absl_base64",
],
"event_engine_listener_test": [ "event_engine_listener_test": [
"event_engine_listener", "event_engine_listener",
], ],

@ -46,11 +46,13 @@
#include "src/core/lib/address_utils/parse_address.h" #include "src/core/lib/address_utils/parse_address.h"
#include "src/core/lib/address_utils/sockaddr_utils.h" #include "src/core/lib/address_utils/sockaddr_utils.h"
#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/env.h" #include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/slice/b64.h"
#include "src/core/lib/transport/http_connect_handshaker.h" #include "src/core/lib/transport/http_connect_handshaker.h"
#include "src/core/lib/uri/uri_parser.h" #include "src/core/lib/uri/uri_parser.h"
@ -258,7 +260,14 @@ absl::optional<std::string> HttpProxyMapper::MapName(
MaybeAddDefaultPort(absl::StripPrefix(uri->path(), "/"))); MaybeAddDefaultPort(absl::StripPrefix(uri->path(), "/")));
if (user_cred.has_value()) { if (user_cred.has_value()) {
// Use base64 encoding for user credentials as stated in RFC 7617 // Use base64 encoding for user credentials as stated in RFC 7617
std::string encoded_user_cred = absl::Base64Escape(*user_cred); std::string encoded_user_cred;
if (IsAbslBase64Enabled()) {
encoded_user_cred = absl::Base64Escape(*user_cred);
} else {
UniquePtr<char> tmp(
grpc_base64_encode(user_cred->data(), user_cred->length(), 0, 0));
encoded_user_cred = tmp.get();
}
*args = args->Set( *args = args->Set(
GRPC_ARG_HTTP_CONNECT_HEADERS, GRPC_ARG_HTTP_CONNECT_HEADERS,
absl::StrCat("Proxy-Authorization:Basic ", encoded_user_cred)); absl::StrCat("Proxy-Authorization:Basic ", encoded_user_cred));

@ -24,6 +24,8 @@
#if defined(GRPC_CFSTREAM) #if defined(GRPC_CFSTREAM)
namespace { namespace {
const char* const description_absl_base64 = "Use abseil base64 functions.";
const char* const additional_constraints_absl_base64 = "{}";
const char* const description_call_status_override_on_cancellation = const char* const description_call_status_override_on_cancellation =
"Avoid overriding call status of successfully finished calls if it races " "Avoid overriding call status of successfully finished calls if it races "
"with cancellation."; "with cancellation.";
@ -193,6 +195,8 @@ const bool kDefaultForDebugOnly = true;
namespace grpc_core { namespace grpc_core {
const ExperimentMetadata g_experiment_metadata[] = { const ExperimentMetadata g_experiment_metadata[] = {
{"absl_base64", description_absl_base64, additional_constraints_absl_base64,
nullptr, 0, true, true},
{"call_status_override_on_cancellation", {"call_status_override_on_cancellation",
description_call_status_override_on_cancellation, description_call_status_override_on_cancellation,
additional_constraints_call_status_override_on_cancellation, nullptr, 0, additional_constraints_call_status_override_on_cancellation, nullptr, 0,
@ -297,6 +301,8 @@ const ExperimentMetadata g_experiment_metadata[] = {
#elif defined(GPR_WINDOWS) #elif defined(GPR_WINDOWS)
namespace { namespace {
const char* const description_absl_base64 = "Use abseil base64 functions.";
const char* const additional_constraints_absl_base64 = "{}";
const char* const description_call_status_override_on_cancellation = const char* const description_call_status_override_on_cancellation =
"Avoid overriding call status of successfully finished calls if it races " "Avoid overriding call status of successfully finished calls if it races "
"with cancellation."; "with cancellation.";
@ -466,6 +472,8 @@ const bool kDefaultForDebugOnly = true;
namespace grpc_core { namespace grpc_core {
const ExperimentMetadata g_experiment_metadata[] = { const ExperimentMetadata g_experiment_metadata[] = {
{"absl_base64", description_absl_base64, additional_constraints_absl_base64,
nullptr, 0, true, true},
{"call_status_override_on_cancellation", {"call_status_override_on_cancellation",
description_call_status_override_on_cancellation, description_call_status_override_on_cancellation,
additional_constraints_call_status_override_on_cancellation, nullptr, 0, additional_constraints_call_status_override_on_cancellation, nullptr, 0,
@ -570,6 +578,8 @@ const ExperimentMetadata g_experiment_metadata[] = {
#else #else
namespace { namespace {
const char* const description_absl_base64 = "Use abseil base64 functions.";
const char* const additional_constraints_absl_base64 = "{}";
const char* const description_call_status_override_on_cancellation = const char* const description_call_status_override_on_cancellation =
"Avoid overriding call status of successfully finished calls if it races " "Avoid overriding call status of successfully finished calls if it races "
"with cancellation."; "with cancellation.";
@ -739,6 +749,8 @@ const bool kDefaultForDebugOnly = true;
namespace grpc_core { namespace grpc_core {
const ExperimentMetadata g_experiment_metadata[] = { const ExperimentMetadata g_experiment_metadata[] = {
{"absl_base64", description_absl_base64, additional_constraints_absl_base64,
nullptr, 0, true, true},
{"call_status_override_on_cancellation", {"call_status_override_on_cancellation",
description_call_status_override_on_cancellation, description_call_status_override_on_cancellation,
additional_constraints_call_status_override_on_cancellation, nullptr, 0, additional_constraints_call_status_override_on_cancellation, nullptr, 0,

@ -57,6 +57,8 @@ namespace grpc_core {
#ifdef GRPC_EXPERIMENTS_ARE_FINAL #ifdef GRPC_EXPERIMENTS_ARE_FINAL
#if defined(GRPC_CFSTREAM) #if defined(GRPC_CFSTREAM)
#define GRPC_EXPERIMENT_IS_INCLUDED_ABSL_BASE64
inline bool IsAbslBase64Enabled() { return true; }
#ifndef NDEBUG #ifndef NDEBUG
#define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION #define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION
#endif #endif
@ -119,6 +121,8 @@ inline bool IsWriteSizeCapEnabled() { return true; }
inline bool IsWrrDelegateToPickFirstEnabled() { return true; } inline bool IsWrrDelegateToPickFirstEnabled() { return true; }
#elif defined(GPR_WINDOWS) #elif defined(GPR_WINDOWS)
#define GRPC_EXPERIMENT_IS_INCLUDED_ABSL_BASE64
inline bool IsAbslBase64Enabled() { return true; }
#ifndef NDEBUG #ifndef NDEBUG
#define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION #define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION
#endif #endif
@ -182,6 +186,8 @@ inline bool IsWriteSizeCapEnabled() { return true; }
inline bool IsWrrDelegateToPickFirstEnabled() { return true; } inline bool IsWrrDelegateToPickFirstEnabled() { return true; }
#else #else
#define GRPC_EXPERIMENT_IS_INCLUDED_ABSL_BASE64
inline bool IsAbslBase64Enabled() { return true; }
#ifndef NDEBUG #ifndef NDEBUG
#define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION #define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION
#endif #endif
@ -247,6 +253,7 @@ inline bool IsWrrDelegateToPickFirstEnabled() { return true; }
#else #else
enum ExperimentIds { enum ExperimentIds {
kExperimentIdAbslBase64,
kExperimentIdCallStatusOverrideOnCancellation, kExperimentIdCallStatusOverrideOnCancellation,
kExperimentIdCallV3, kExperimentIdCallV3,
kExperimentIdCanaryClientPrivacy, kExperimentIdCanaryClientPrivacy,
@ -289,6 +296,10 @@ enum ExperimentIds {
kExperimentIdWrrDelegateToPickFirst, kExperimentIdWrrDelegateToPickFirst,
kNumExperiments kNumExperiments
}; };
#define GRPC_EXPERIMENT_IS_INCLUDED_ABSL_BASE64
inline bool IsAbslBase64Enabled() {
return IsExperimentEnabled(kExperimentIdAbslBase64);
}
#define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION #define GRPC_EXPERIMENT_IS_INCLUDED_CALL_STATUS_OVERRIDE_ON_CANCELLATION
inline bool IsCallStatusOverrideOnCancellationEnabled() { inline bool IsCallStatusOverrideOnCancellationEnabled() {
return IsExperimentEnabled(kExperimentIdCallStatusOverrideOnCancellation); return IsExperimentEnabled(kExperimentIdCallStatusOverrideOnCancellation);

@ -40,6 +40,11 @@
# This file only defines the experiments. Refer to rollouts.yaml for the rollout # This file only defines the experiments. Refer to rollouts.yaml for the rollout
# state of each experiment. # state of each experiment.
- name: absl_base64
description: Use abseil base64 functions.
expiry: 2024/06/01
owner: roth@google.com
test_tags: ["credential_token_tests"]
- name: call_status_override_on_cancellation - name: call_status_override_on_cancellation
description: description:
Avoid overriding call status of successfully finished calls if it races with Avoid overriding call status of successfully finished calls if it races with

@ -40,6 +40,8 @@
# #
# Supported platforms: ios, windows, posix # Supported platforms: ios, windows, posix
- name: absl_base64
default: true
- name: call_status_override_on_cancellation - name: call_status_override_on_cancellation
default: debug default: debug
- name: call_v3 - name: call_v3

@ -42,10 +42,12 @@
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/error.h"
#include "src/core/lib/json/json_reader.h" #include "src/core/lib/json/json_reader.h"
#include "src/core/lib/json/json_writer.h" #include "src/core/lib/json/json_writer.h"
#include "src/core/lib/security/util/json_util.h" #include "src/core/lib/security/util/json_util.h"
#include "src/core/lib/slice/b64.h"
using grpc_core::Json; using grpc_core::Json;
@ -181,6 +183,9 @@ static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
{"kid", Json::FromString(key_id)}, {"kid", Json::FromString(key_id)},
}); });
std::string json_str = grpc_core::JsonDump(json); std::string json_str = grpc_core::JsonDump(json);
if (!grpc_core::IsAbslBase64Enabled()) {
return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
}
return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str()); return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str());
} }
@ -209,6 +214,9 @@ static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
std::string json_str = std::string json_str =
grpc_core::JsonDump(Json::FromObject(std::move(object))); grpc_core::JsonDump(Json::FromObject(std::move(object)));
if (!grpc_core::IsAbslBase64Enabled()) {
return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
}
return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str()); return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str());
} }
@ -281,10 +289,14 @@ char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed."); gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed.");
goto end; goto end;
} }
result = if (!grpc_core::IsAbslBase64Enabled()) {
gpr_strdup(absl::WebSafeBase64Escape( result = grpc_base64_encode(sig, sig_len, 1, 0);
absl::string_view(reinterpret_cast<char*>(sig), sig_len)) } else {
.c_str()); result =
gpr_strdup(absl::WebSafeBase64Escape(
absl::string_view(reinterpret_cast<char*>(sig), sig_len))
.c_str());
}
end: end:
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if OPENSSL_VERSION_NUMBER < 0x30000000L

@ -53,6 +53,7 @@
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/gprpp/manual_constructor.h"
#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/gprpp/memory.h"
@ -67,6 +68,7 @@
#include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/json/json_reader.h" #include "src/core/lib/json/json_reader.h"
#include "src/core/lib/security/credentials/credentials.h" // IWYU pragma: keep #include "src/core/lib/security/credentials/credentials.h" // IWYU pragma: keep
#include "src/core/lib/slice/b64.h"
#include "src/core/lib/slice/slice.h" #include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/uri/uri_parser.h" #include "src/core/lib/uri/uri_parser.h"
@ -112,9 +114,19 @@ static const EVP_MD* evp_md_from_alg(const char* alg) {
static Json parse_json_part_from_jwt(const char* str, size_t len) { static Json parse_json_part_from_jwt(const char* str, size_t len) {
std::string string; std::string string;
if (!absl::WebSafeBase64Unescape(absl::string_view(str, len), &string)) { if (!grpc_core::IsAbslBase64Enabled()) {
gpr_log(GPR_ERROR, "Invalid base64."); grpc_slice slice = grpc_base64_decode_with_len(str, len, 1);
return Json(); // JSON null if (GRPC_SLICE_IS_EMPTY(slice)) {
gpr_log(GPR_ERROR, "Invalid base64.");
return Json(); // JSON null
}
string = std::string(grpc_core::StringViewFromSlice(slice));
grpc_core::CSliceUnref(slice);
} else {
if (!absl::WebSafeBase64Unescape(absl::string_view(str, len), &string)) {
gpr_log(GPR_ERROR, "Invalid base64.");
return Json(); // JSON null
}
} }
auto json = grpc_core::JsonParse(string); auto json = grpc_core::JsonParse(string);
if (!json.ok()) { if (!json.ok()) {
@ -479,6 +491,18 @@ end:
static BIGNUM* bignum_from_base64(const char* b64) { static BIGNUM* bignum_from_base64(const char* b64) {
if (b64 == nullptr) return nullptr; if (b64 == nullptr) return nullptr;
if (!grpc_core::IsAbslBase64Enabled()) {
grpc_slice bin = grpc_base64_decode(b64, 1);
if (GRPC_SLICE_IS_EMPTY(bin)) {
gpr_log(GPR_ERROR, "Invalid base64 for big num.");
return nullptr;
}
BIGNUM* result =
BN_bin2bn(GRPC_SLICE_START_PTR(bin),
TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), nullptr);
grpc_core::CSliceUnref(bin);
return result;
}
std::string string; std::string string;
if (!absl::WebSafeBase64Unescape(b64, &string)) { if (!absl::WebSafeBase64Unescape(b64, &string)) {
gpr_log(GPR_ERROR, "Invalid base64 for big num."); gpr_log(GPR_ERROR, "Invalid base64 for big num.");
@ -920,10 +944,10 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
const char* dot = nullptr; const char* dot = nullptr;
jose_header* header = nullptr; jose_header* header = nullptr;
grpc_jwt_claims* claims = nullptr; grpc_jwt_claims* claims = nullptr;
grpc_slice signature;
size_t signed_jwt_len; size_t signed_jwt_len;
const char* cur = jwt; const char* cur = jwt;
Json json; Json json;
std::string signature;
GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr && GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr &&
cb != nullptr); cb != nullptr);
@ -945,11 +969,17 @@ void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
signed_jwt_len = static_cast<size_t>(dot - jwt); signed_jwt_len = static_cast<size_t>(dot - jwt);
cur = dot + 1; cur = dot + 1;
if (!absl::WebSafeBase64Unescape(cur, &signature)) goto error; if (!grpc_core::IsAbslBase64Enabled()) {
signature = grpc_base64_decode(cur, 1);
if (GRPC_SLICE_IS_EMPTY(signature)) goto error;
} else {
std::string signature_str;
if (!absl::WebSafeBase64Unescape(cur, &signature_str)) goto error;
signature = grpc_slice_from_cpp_string(std::move(signature_str));
}
retrieve_key_and_verify( retrieve_key_and_verify(
verifier_cb_ctx_create(verifier, pollset, header, claims, audience, verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
grpc_slice_from_cpp_string(std::move(signature)), signature, jwt, signed_jwt_len, user_data, cb));
jwt, signed_jwt_len, user_data, cb));
return; return;
error: error:

@ -140,6 +140,7 @@ grpc_cc_test(
language = "C++", language = "C++",
uses_event_engine = False, uses_event_engine = False,
uses_polling = False, uses_polling = False,
tags = ["credential_token_tests"],
deps = [ deps = [
"//:gpr", "//:gpr",
"//:grpc", "//:grpc",
@ -156,6 +157,7 @@ grpc_cc_test(
language = "C++", language = "C++",
uses_event_engine = False, uses_event_engine = False,
uses_polling = False, uses_polling = False,
tags = ["credential_token_tests"],
deps = [ deps = [
"//:gpr", "//:gpr",
"//:grpc", "//:grpc",

Loading…
Cancel
Save