From cce97d263a27a223f9e9142ba2cbcbf92fb140a9 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Tue, 8 Aug 2023 09:55:22 -0400 Subject: [PATCH] mobile: Adds authentication token support to the xDS EngineBuilder APIs (#28523) This change enables passing in an authentication token to the gRPC headers for authentication with an xDS management server. The invoker specifies the header key (e.g. `x-goog-api-key`) and value (the authentication token itself). This change also updates the GCP Traffic Director integration test to use API keys (`x-goog-api-key`) for authentication instead of JWT tokens. Signed-off-by: Ali Beyad --- mobile/library/cc/engine_builder.cc | 13 +++++- mobile/library/cc/engine_builder.h | 18 ++++++++ mobile/library/common/jni/jni_interface.cc | 12 +++-- .../engine/EnvoyConfiguration.java | 21 ++++++--- .../envoymobile/engine/JniLibrary.java | 8 ++-- .../impl/NativeCronvoyEngineBuilderImpl.java | 3 +- .../envoyproxy/envoymobile/EngineBuilder.kt | 22 ++++++++++ .../library/objective-c/EnvoyConfiguration.h | 4 ++ .../library/objective-c/EnvoyConfiguration.mm | 8 ++++ mobile/library/swift/EngineBuilder.swift | 28 ++++++++++++ mobile/test/cc/unit/envoy_config_test.cc | 40 ++++++++++++++--- .../engine/EnvoyConfigurationTest.kt | 4 ++ .../envoymobile/EngineBuilderTest.kt | 3 ++ .../gcp_traffic_director_integration_test.cc | 44 +++---------------- mobile/test/swift/EngineBuilderTests.swift | 16 +++++++ 15 files changed, 188 insertions(+), 56 deletions(-) diff --git a/mobile/library/cc/engine_builder.cc b/mobile/library/cc/engine_builder.cc index 1b7f90531837..5aa018150023 100644 --- a/mobile/library/cc/engine_builder.cc +++ b/mobile/library/cc/engine_builder.cc @@ -45,6 +45,12 @@ namespace Platform { XdsBuilder::XdsBuilder(std::string xds_server_address, const int xds_server_port) : xds_server_address_(std::move(xds_server_address)), xds_server_port_(xds_server_port) {} +XdsBuilder& XdsBuilder::setAuthenticationToken(std::string token_header, std::string token) { + authentication_token_header_ = std::move(token_header); + authentication_token_ = std::move(token); + return *this; +} + XdsBuilder& XdsBuilder::setJwtAuthenticationToken(std::string token, const int token_lifetime_in_seconds) { jwt_token_ = std::move(token); @@ -94,7 +100,12 @@ void XdsBuilder::build(envoy::config::bootstrap::v3::Bootstrap* bootstrap) const ->mutable_root_certs() ->set_inline_string(ssl_root_certs_); } - if (!jwt_token_.empty()) { + + if (!authentication_token_header_.empty() && !authentication_token_.empty()) { + auto* auth_token_metadata = grpc_service.add_initial_metadata(); + auth_token_metadata->set_key(authentication_token_header_); + auth_token_metadata->set_value(authentication_token_); + } else if (!jwt_token_.empty()) { auto& jwt = *grpc_service.mutable_google_grpc() ->add_call_credentials() ->mutable_service_account_jwt_access(); diff --git a/mobile/library/cc/engine_builder.h b/mobile/library/cc/engine_builder.h index 23582b770857..794101b02abf 100644 --- a/mobile/library/cc/engine_builder.h +++ b/mobile/library/cc/engine_builder.h @@ -49,11 +49,27 @@ class XdsBuilder final { // requests. XdsBuilder(std::string xds_server_address, const int xds_server_port); + // Sets the authentication token in the gRPC headers used to authenticate to the xDS management + // server. + // + // For example, if using API keys to authenticate to Traffic Director on GCP (see + // https://cloud.google.com/docs/authentication/api-keys for details), invoke: + // builder.setAuthenticationToken("x-goog-api-key", api_key_token) + // + // If this method is called, then don't call setJwtAuthenticationToken. + // + // `token_header`: the header name for which the the `token` will be set as a value. + // `token`: the authentication token. + XdsBuilder& setAuthenticationToken(std::string token_header, std::string token); + // Sets JWT as the authentication method to the xDS management server, using the given token. // + // If setAuthenticationToken is called, then invocations of this method will be ignored. + // // `token`: the JWT token used to authenticate the client to the xDS management server. // `token_lifetime_in_seconds`: the lifetime of the JWT token, in seconds. If none // (or 0) is specified, then DefaultJwtTokenLifetimeSeconds is used. + // TODO(abeyad): Deprecate and remove this. XdsBuilder& setJwtAuthenticationToken(std::string token, int token_lifetime_in_seconds = DefaultJwtTokenLifetimeSeconds); @@ -107,6 +123,8 @@ class XdsBuilder final { std::string xds_server_address_; int xds_server_port_; + std::string authentication_token_header_; + std::string authentication_token_; std::string jwt_token_; int jwt_token_lifetime_in_seconds_ = DefaultJwtTokenLifetimeSeconds; std::string ssl_root_certs_; diff --git a/mobile/library/common/jni/jni_interface.cc b/mobile/library/common/jni/jni_interface.cc index bc5c3316030f..471fb1a59b1c 100644 --- a/mobile/library/common/jni/jni_interface.cc +++ b/mobile/library/common/jni/jni_interface.cc @@ -1287,9 +1287,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr jboolean trust_chain_verification, jobjectArray filter_chain, jobjectArray stat_sinks, jboolean enable_platform_certificates_validation, jobjectArray runtime_guards, jstring rtds_resource_name, jlong rtds_timeout_seconds, jstring xds_address, jlong xds_port, - jstring xds_jwt_token, jlong xds_jwt_token_lifetime, jstring xds_root_certs, jstring xds_sni, - jstring node_id, jstring node_region, jstring node_zone, jstring node_sub_zone, - jstring cds_resources_locator, jlong cds_timeout_seconds, jboolean enable_cds) { + jstring xds_auth_header, jstring xds_auth_token, jstring xds_jwt_token, + jlong xds_jwt_token_lifetime, jstring xds_root_certs, jstring xds_sni, jstring node_id, + jstring node_region, jstring node_zone, jstring node_sub_zone, jstring cds_resources_locator, + jlong cds_timeout_seconds, jboolean enable_cds) { Envoy::Platform::EngineBuilder builder; configureBuilder( @@ -1308,6 +1309,11 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr std::string native_xds_address = getCppString(env, xds_address); if (!native_xds_address.empty()) { Envoy::Platform::XdsBuilder xds_builder(std::move(native_xds_address), xds_port); + std::string native_xds_auth_header = getCppString(env, xds_auth_header); + if (!native_xds_auth_header.empty()) { + xds_builder.setAuthenticationToken(std::move(native_xds_auth_header), + getCppString(env, xds_auth_token)); + } std::string native_jwt_token = getCppString(env, xds_jwt_token); if (!native_jwt_token.empty()) { xds_builder.setJwtAuthenticationToken(std::move(native_jwt_token), xds_jwt_token_lifetime); diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java index 689238af7ee7..005946f76ceb 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java @@ -64,6 +64,8 @@ public enum TrustChainVerification { public final Integer rtdsTimeoutSeconds; public final String xdsAddress; public final Integer xdsPort; + public final String xdsAuthHeader; + public final String xdsAuthToken; public final String xdsJwtToken; public final Integer xdsJwtTokenLifetime; public final String xdsRootCerts; @@ -134,6 +136,11 @@ public enum TrustChainVerification { * @param rtdsTimeoutSeconds the timeout for RTDS fetches. * @param xdsAddress the address for the xDS management server. * @param xdsPort the port for the xDS server. + * @param xdsAuthHeader the HTTP header to use for sending the + * authentication token to the xDS server. + * @param xdsAuthToken the token to send as the authentication + * header value to authenticate with the + * xDS server. * @param xdsJwtToken the JWT token to use for authenticating * with the xDS server. * @param xdsTokenLifetime the lifetime of the JWT token. @@ -168,9 +175,10 @@ public EnvoyConfiguration( Map keyValueStores, List statSinks, Map runtimeGuards, boolean enablePlatformCertificatesValidation, String rtdsResourceName, Integer rtdsTimeoutSeconds, String xdsAddress, Integer xdsPort, - String xdsJwtToken, Integer xdsJwtTokenLifetime, String xdsRootCerts, String xdsSni, - String nodeId, String nodeRegion, String nodeZone, String nodeSubZone, - String cdsResourcesLocator, Integer cdsTimeoutSeconds, boolean enableCds) { + String xdsAuthHeader, String xdsAuthToken, String xdsJwtToken, Integer xdsJwtTokenLifetime, + String xdsRootCerts, String xdsSni, String nodeId, String nodeRegion, String nodeZone, + String nodeSubZone, String cdsResourcesLocator, Integer cdsTimeoutSeconds, + boolean enableCds) { JniLibrary.load(); this.grpcStatsDomain = grpcStatsDomain; this.connectTimeoutSeconds = connectTimeoutSeconds; @@ -224,6 +232,8 @@ public EnvoyConfiguration( this.rtdsTimeoutSeconds = rtdsTimeoutSeconds; this.xdsAddress = xdsAddress; this.xdsPort = xdsPort; + this.xdsAuthHeader = xdsAuthHeader; + this.xdsAuthToken = xdsAuthToken; this.xdsJwtToken = xdsJwtToken; this.xdsJwtTokenLifetime = xdsJwtTokenLifetime; this.xdsRootCerts = xdsRootCerts; @@ -258,8 +268,9 @@ public long createBootstrap() { streamIdleTimeoutSeconds, perTryIdleTimeoutSeconds, appVersion, appId, enforceTrustChainVerification, filter_chain, stats_sinks, enablePlatformCertificatesValidation, runtime_guards, rtdsResourceName, rtdsTimeoutSeconds, - xdsAddress, xdsPort, xdsJwtToken, xdsJwtTokenLifetime, xdsRootCerts, xdsSni, nodeId, - nodeRegion, nodeZone, nodeSubZone, cdsResourcesLocator, cdsTimeoutSeconds, enableCds); + xdsAddress, xdsPort, xdsAuthHeader, xdsAuthToken, xdsJwtToken, xdsJwtTokenLifetime, + xdsRootCerts, xdsSni, nodeId, nodeRegion, nodeZone, nodeSubZone, cdsResourcesLocator, + cdsTimeoutSeconds, enableCds); } static class ConfigurationException extends RuntimeException { diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java index eb35517a7c73..ac570ffee3f2 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/JniLibrary.java @@ -310,8 +310,8 @@ public static native long createBootstrap( long perTryIdleTimeoutSeconds, String appVersion, String appId, boolean trustChainVerification, byte[][] filterChain, byte[][] statSinks, boolean enablePlatformCertificatesValidation, byte[][] runtimeGuards, String rtdsResourceName, - long rtdsTimeoutSeconds, String xdsAddress, long xdsPort, String xdsJwtToken, - long xdsJwtTokenLifetime, String xdsRootCerts, String xdsSni, String nodeId, - String nodeRegion, String nodeZone, String nodeSubZone, String cdsResourcesLocator, - long cdsTimeoutSeconds, boolean enableCds); + long rtdsTimeoutSeconds, String xdsAddress, long xdsPort, String xdsAuthenticationHeader, + String xdsAuthenticationToken, String xdsJwtToken, long xdsJwtTokenLifetime, + String xdsRootCerts, String xdsSni, String nodeId, String nodeRegion, String nodeZone, + String nodeSubZone, String cdsResourcesLocator, long cdsTimeoutSeconds, boolean enableCds); } diff --git a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java index 2593c0812fb1..08d3f09762dc 100644 --- a/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java +++ b/mobile/library/java/org/chromium/net/impl/NativeCronvoyEngineBuilderImpl.java @@ -132,7 +132,8 @@ mEnableDrainPostDnsRefresh, quicEnabled(), mEnableGzipDecompression, brotliEnabl mTrustChainVerification, nativeFilterChain, platformFilterChain, stringAccessors, keyValueStores, statSinks, runtimeGuards, mEnablePlatformCertificatesValidation, /*rtdsResourceName=*/"", /*rtdsTimeoutSeconds=*/0, /*xdsAddress=*/"", - /*xdsPort=*/0, /*xdsJwtToken=*/"", /*xdsJwtTokenLifetime=*/0, /*xdsSslRootCerts=*/"", + /*xdsPort=*/0, /*xdsAuthenticationHeader=*/"", /*xdsAuthenticationToken=*/"", + /*xdsJwtToken=*/"", /*xdsJwtTokenLifetime=*/0, /*xdsSslRootCerts=*/"", /*xdsSni=*/"", mNodeId, mNodeRegion, mNodeZone, mNodeSubZone, /*cdsResourcesLocator=*/"", /*cdsTimeoutSeconds=*/0, /*enableCds=*/false); } diff --git a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt index b5ced04a1646..50c8dc2be4fb 100644 --- a/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt +++ b/mobile/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt @@ -43,6 +43,8 @@ open class XdsBuilder ( private const val DEFAULT_XDS_TIMEOUT_IN_SECONDS: Int = 5 } + internal var authHeader: String? = null + internal var authToken: String? = null internal var jwtToken: String? = null internal var jwtTokenLifetimeInSeconds: Int = DEFAULT_JWT_TOKEN_LIFETIME_IN_SECONDS internal var sslRootCerts: String? = null @@ -53,6 +55,24 @@ open class XdsBuilder ( internal var cdsResourcesLocator: String? = null internal var cdsTimeoutInSeconds: Int = DEFAULT_XDS_TIMEOUT_IN_SECONDS + /** + * Sets the authentication HTTP header and token value for authenticating with the xDS + * management server. + * + * @param header The HTTP authentication header. + * @param token The authentication token to be sent in the header. + * + * @return this builder. + */ + fun setAuthenticationToken( + header: String, + token: String + ): XdsBuilder { + this.authHeader = header + this.authToken = token + return this + } + /** * Sets JWT as the authentication method to the xDS management server, using the given token. * @@ -731,6 +751,8 @@ open class EngineBuilder( xdsBuilder?.rtdsTimeoutInSeconds ?: 0, xdsBuilder?.xdsServerAddress, xdsBuilder?.xdsServerPort ?: 0, + xdsBuilder?.authHeader, + xdsBuilder?.authToken, xdsBuilder?.jwtToken, xdsBuilder?.jwtTokenLifetimeInSeconds ?: 0, xdsBuilder?.sslRootCerts, diff --git a/mobile/library/objective-c/EnvoyConfiguration.h b/mobile/library/objective-c/EnvoyConfiguration.h index 4ecb82f0f76b..cd40c177aad3 100644 --- a/mobile/library/objective-c/EnvoyConfiguration.h +++ b/mobile/library/objective-c/EnvoyConfiguration.h @@ -49,6 +49,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) NSString *nodeSubZone; @property (nonatomic, strong, nullable) NSString *xdsServerAddress; @property (nonatomic, assign) UInt32 xdsServerPort; +@property (nonatomic, strong, nullable) NSString *xdsAuthHeader; +@property (nonatomic, strong, nullable) NSString *xdsAuthToken; @property (nonatomic, strong, nullable) NSString *xdsJwtToken; @property (nonatomic, assign) UInt32 xdsJwtTokenLifetimeSeconds; @property (nonatomic, strong, nullable) NSString *xdsSslRootCerts; @@ -109,6 +111,8 @@ NS_ASSUME_NONNULL_BEGIN nodeSubZone:(nullable NSString *)nodeSubZone xdsServerAddress:(nullable NSString *)xdsServerAddress xdsServerPort:(UInt32)xdsServerPort + xdsAuthHeader:(nullable NSString *)xdsAuthHeader + xdsAuthToken:(nullable NSString *)xdsAuthToken xdsJwtToken:(nullable NSString *)xdsJwtToken xdsJwtTokenLifetimeSeconds:(UInt32)xdsJwtTokenLifetimeSeconds xdsSslRootCerts:(nullable NSString *)xdsSslRootCerts diff --git a/mobile/library/objective-c/EnvoyConfiguration.mm b/mobile/library/objective-c/EnvoyConfiguration.mm index b1a70b867609..60aca2302f0f 100644 --- a/mobile/library/objective-c/EnvoyConfiguration.mm +++ b/mobile/library/objective-c/EnvoyConfiguration.mm @@ -113,6 +113,8 @@ - (instancetype)initWithGrpcStatsDomain:(nullable NSString *)grpcStatsDomain nodeSubZone:(nullable NSString *)nodeSubZone xdsServerAddress:(nullable NSString *)xdsServerAddress xdsServerPort:(UInt32)xdsServerPort + xdsAuthHeader:(nullable NSString *)xdsAuthHeader + xdsAuthToken:(nullable NSString *)xdsAuthToken xdsJwtToken:(nullable NSString *)xdsJwtToken xdsJwtTokenLifetimeSeconds:(UInt32)xdsJwtTokenLifetimeSeconds xdsSslRootCerts:(nullable NSString *)xdsSslRootCerts @@ -166,6 +168,8 @@ - (instancetype)initWithGrpcStatsDomain:(nullable NSString *)grpcStatsDomain self.nodeSubZone = nodeSubZone; self.xdsServerAddress = xdsServerAddress; self.xdsServerPort = xdsServerPort; + self.xdsAuthHeader = xdsAuthHeader; + self.xdsAuthToken = xdsAuthToken; self.xdsJwtToken = xdsJwtToken; self.xdsJwtTokenLifetimeSeconds = xdsJwtTokenLifetimeSeconds; self.xdsSslRootCerts = xdsSslRootCerts; @@ -262,6 +266,10 @@ - (instancetype)initWithGrpcStatsDomain:(nullable NSString *)grpcStatsDomain #ifdef ENVOY_GOOGLE_GRPC if (self.xdsServerAddress != nil) { Envoy::Platform::XdsBuilder xdsBuilder([self.xdsServerAddress toCXXString], self.xdsServerPort); + if (self.xdsAuthHeader != nil) { + xdsBuilder.setAuthenticationToken([self.xdsAuthHeader toCXXString], + [self.xdsAuthToken toCXXString]); + } if (self.xdsJwtToken != nil) { xdsBuilder.setJwtAuthenticationToken([self.xdsJwtToken toCXXString], self.xdsJwtTokenLifetimeSeconds); diff --git a/mobile/library/swift/EngineBuilder.swift b/mobile/library/swift/EngineBuilder.swift index 40331f8752ad..aa7631191577 100644 --- a/mobile/library/swift/EngineBuilder.swift +++ b/mobile/library/swift/EngineBuilder.swift @@ -19,6 +19,8 @@ open class XdsBuilder: NSObject { let xdsServerAddress: String let xdsServerPort: UInt32 + var authHeader: String? + var authToken: String? var jwtToken: String? var jwtTokenLifetimeInSeconds: UInt32 = XdsBuilder.defaultJwtTokenLifetimeInSeconds var sslRootCerts: String? @@ -38,6 +40,22 @@ open class XdsBuilder: NSObject { self.xdsServerPort = xdsServerPort } + /// Sets the authentication HTTP header and token value for authentication with the xDS + /// management server. + /// + /// - parameter header: The HTTP authentication header. + /// - parameter token: The authentication token to be sent in the header. + /// + /// - returns: This builder. + @discardableResult + public func setAuthenticationToken( + header: String, + token: String) -> Self { + self.authHeader = header + self.authToken = token + return self + } + /// Sets JWT as the authentication method to the xDS management server, using the given token. /// /// - parameter token: The JWT token used to authenticate the client to the xDS @@ -754,6 +772,8 @@ open class EngineBuilder: NSObject { func makeConfig() -> EnvoyConfiguration { var xdsServerAddress: String? var xdsServerPort: UInt32 = 0 + var xdsAuthHeader: String? + var xdsAuthToken: String? var xdsJwtToken: String? var xdsJwtTokenLifetimeSeconds: UInt32 = 0 var xdsSslRootCerts: String? @@ -767,6 +787,8 @@ open class EngineBuilder: NSObject { #if ENVOY_GOOGLE_GRPC xdsServerAddress = self.xdsBuilder?.xdsServerAddress xdsServerPort = self.xdsBuilder?.xdsServerPort ?? 0 + xdsAuthHeader = self.xdsBuilder?.authHeader + xdsAuthToken = self.xdsBuilder?.authToken xdsJwtToken = self.xdsBuilder?.jwtToken xdsJwtTokenLifetimeSeconds = self.xdsBuilder?.jwtTokenLifetimeInSeconds ?? 0 xdsSslRootCerts = self.xdsBuilder?.sslRootCerts @@ -818,6 +840,8 @@ open class EngineBuilder: NSObject { nodeSubZone: self.nodeSubZone, xdsServerAddress: xdsServerAddress, xdsServerPort: xdsServerPort, + xdsAuthHeader: xdsAuthHeader, + xdsAuthToken: xdsAuthToken, xdsJwtToken: xdsJwtToken, xdsJwtTokenLifetimeSeconds: xdsJwtTokenLifetimeSeconds, xdsSslRootCerts: xdsSslRootCerts, @@ -919,6 +943,10 @@ private extension EngineBuilder { if let xdsBuilder = self.xdsBuilder { var cxxXdsBuilder = Envoy.Platform.XdsBuilder(xdsBuilder.xdsServerAddress.toCXX(), Int32(xdsBuilder.xdsServerPort)) + if let xdsAuthHeader = xdsBuilder.authHeader { + cxxXdsBuilder.setAuthenticationToken(xdsAuthHeader.toCXX(), + xdsBuilder.authToken?.toCXX() ?? "".toCXX()) + } if let xdsJwtToken = xdsBuilder.jwtToken { cxxXdsBuilder.setJwtAuthenticationToken(xdsJwtToken.toCXX(), Int32(xdsBuilder.jwtTokenLifetimeInSeconds)) diff --git a/mobile/test/cc/unit/envoy_config_test.cc b/mobile/test/cc/unit/envoy_config_test.cc index 7b6d21fe4520..ea79c894b943 100644 --- a/mobile/test/cc/unit/envoy_config_test.cc +++ b/mobile/test/cc/unit/envoy_config_test.cc @@ -281,11 +281,10 @@ TEST(TestConfig, XdsConfig) { IsEmpty()); EXPECT_THAT(ads_config.grpc_services(0).google_grpc().call_credentials(), SizeIs(0)); - // With security credentials. + // With authentication credentials. xds_builder = XdsBuilder(/*xds_server_address=*/"fake-td.googleapis.com", /*xds_server_port=*/12345); - xds_builder.setJwtAuthenticationToken(/*token=*/"my_jwt_token", - /*token_lifetime_in_seconds=*/500); + xds_builder.setAuthenticationToken(/*header=*/"x-goog-api-key", /*token=*/"A1B2C3"); xds_builder.setSslRootCerts(/*root_certs=*/"my_root_cert"); xds_builder.setSni(/*sni=*/"fake-td.googleapis.com"); engine_builder.setXds(std::move(xds_builder)); @@ -302,19 +301,50 @@ TEST(TestConfig, XdsConfig) { .root_certs() .inline_string(), "my_root_cert"); + EXPECT_EQ(ads_config_with_tokens.grpc_services(0).initial_metadata(0).key(), "x-goog-api-key"); + EXPECT_EQ(ads_config_with_tokens.grpc_services(0).initial_metadata(0).value(), "A1B2C3"); EXPECT_EQ(ads_config_with_tokens.grpc_services(0) + .google_grpc() + .channel_args() + .args() + .at("grpc.default_authority") + .string_value(), + "fake-td.googleapis.com"); + + // With JWT security credentials. + xds_builder = + XdsBuilder(/*xds_server_address=*/"fake-td.googleapis.com", /*xds_server_port=*/12345); + xds_builder.setJwtAuthenticationToken(/*token=*/"my_jwt_token", + /*token_lifetime_in_seconds=*/500); + xds_builder.setSslRootCerts(/*root_certs=*/"my_root_cert"); + xds_builder.setSni(/*sni=*/"fake-td.googleapis.com"); + engine_builder.setXds(std::move(xds_builder)); + bootstrap = engine_builder.generateBootstrap(); + auto& ads_config_with_jwt_tokens = bootstrap->dynamic_resources().ads_config(); + EXPECT_EQ(ads_config_with_jwt_tokens.api_type(), envoy::config::core::v3::ApiConfigSource::GRPC); + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0).google_grpc().target_uri(), + "fake-td.googleapis.com:12345"); + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0).google_grpc().stat_prefix(), "ads"); + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0) + .google_grpc() + .channel_credentials() + .ssl_credentials() + .root_certs() + .inline_string(), + "my_root_cert"); + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0) .google_grpc() .call_credentials(0) .service_account_jwt_access() .json_key(), "my_jwt_token"); - EXPECT_EQ(ads_config_with_tokens.grpc_services(0) + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0) .google_grpc() .call_credentials(0) .service_account_jwt_access() .token_lifetime_seconds(), 500); - EXPECT_EQ(ads_config_with_tokens.grpc_services(0) + EXPECT_EQ(ads_config_with_jwt_tokens.grpc_services(0) .google_grpc() .channel_args() .args() diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt index cc6e23031af0..e1d92b66d7b9 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt @@ -102,6 +102,8 @@ class EnvoyConfigurationTest { rtdsTimeoutSeconds: Int = 0, xdsAddress: String = "", xdsPort: Int = 0, + xdsAuthHeader: String = "", + xdsAuthToken: String = "", xdsJwtToken: String = "", xdsJwtTokenLifetimeSeconds: Int = 0, xdsSslRootCerts: String = "", @@ -152,6 +154,8 @@ class EnvoyConfigurationTest { rtdsTimeoutSeconds, xdsAddress, xdsPort, + xdsAuthHeader, + xdsAuthToken, xdsJwtToken, xdsJwtTokenLifetimeSeconds, xdsSslRootCerts, diff --git a/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt b/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt index 6d9ba62a2655..1703b08690d7 100644 --- a/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt +++ b/mobile/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt @@ -207,6 +207,7 @@ class EngineBuilderTest { @Test fun `specifying xDS works`() { var xdsBuilder = XdsBuilder("fake_test_address", 0) + xdsBuilder.setAuthenticationToken("x-goog-api-key", "A1B2C3") xdsBuilder.setJwtAuthenticationToken("my_jwt_token") xdsBuilder.setSslRootCerts("my_root_certs") xdsBuilder.setSni("fake_test_address"); @@ -218,6 +219,8 @@ class EngineBuilderTest { val engine = engineBuilder.build() as EngineImpl assertThat(engine.envoyConfiguration.xdsAddress).isEqualTo("fake_test_address") + assertThat(engine.envoyConfiguration.xdsAuthHeader).isEqualTo("x-goog-api-key") + assertThat(engine.envoyConfiguration.xdsAuthToken).isEqualTo("A1B2C3") assertThat(engine.envoyConfiguration.xdsJwtToken).isEqualTo("my_jwt_token") assertThat(engine.envoyConfiguration.xdsRootCerts).isEqualTo("my_root_certs") assertThat(engine.envoyConfiguration.xdsSni).isEqualTo("fake_test_address") diff --git a/mobile/test/non_hermetic/gcp_traffic_director_integration_test.cc b/mobile/test/non_hermetic/gcp_traffic_director_integration_test.cc index 182320d68a71..1214fada04d4 100644 --- a/mobile/test/non_hermetic/gcp_traffic_director_integration_test.cc +++ b/mobile/test/non_hermetic/gcp_traffic_director_integration_test.cc @@ -36,44 +36,10 @@ using ::Envoy::Grpc::SotwOrDelta; using ::Envoy::Network::Address::IpVersion; // The One-Platform API endpoint for Traffic Director. -constexpr char TD_API_ENDPOINT[] = "trafficdirector.googleapis.com"; -// The name of the project in Google Cloud Console; copied from the project_id -// field in the generated JWT token. -constexpr char PROJECT_NAME[] = "td-testing-gfq"; +constexpr char TD_API_ENDPOINT[] = "staging-trafficdirectorconsumermesh.sandbox.googleapis.com"; // The project number of the project, found on the main page of the project in // Google Cloud Console. -constexpr char PROJECT_ID[] = "798832730858"; -// Copied from the "client_id" field in the generated JWT token. -constexpr char CLIENT_ID[] = "102524055118681734203"; -// Copied from the "private_key_id" field in the generated JWT token. -constexpr char PRIVATE_KEY_ID[] = "e07f02d49044a533cf4342d138eacecc6acdb6ed"; - -// Using a JWT token to authenticate to Traffic Director. -std::string jwtToken() { - const std::string email = - absl::Substitute("$0-compute@developer.gserviceaccount.com", PROJECT_ID); - const std::string cert_url = absl::Substitute("https://www.googleapis.com/robot/v1/metadata/x509/" - "$0-compute%40developer.gserviceaccount.com", - PROJECT_ID); - - const char* private_key = std::getenv("GCP_JWT_PRIVATE_KEY"); - RELEASE_ASSERT(private_key != nullptr, "GCP_JWT_PRIVATE_KEY environment variable not set."); - - return absl::Substitute( - R"json({ - "private_key": "$0", - "private_key_id": "$1", - "project_id": "$2", - "client_email": "$3", - "client_id": "$4", - "client_x509_cert_url": "$5", - "type": "service_account", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs" - })json", - private_key, PRIVATE_KEY_ID, PROJECT_NAME, email, CLIENT_ID, cert_url); -} +constexpr char PROJECT_ID[] = "947171374466"; // Tests that Envoy Mobile can connect to Traffic Director (an xDS management server offered by GCP) // via a test GCP project, and can pull down xDS config for the given project. @@ -97,10 +63,14 @@ class GcpTrafficDirectorIntegrationTest std::string root_certs(TestEnvironment::readFileToStringForTest( TestEnvironment::runfilesPath("test/config/integration/certs/google_root_certs.pem"))); + // API key for the `bct-staging-td-consumer-mesh` GCP test project. + const char* api_key = std::getenv("GCP_TEST_PROJECT_API_KEY"); + RELEASE_ASSERT(api_key != nullptr, "GCP_TEST_PROJECT_API_KEY environment variable not set."); + // TODO(abeyad): switch to using API key authentication instead of a JWT token. Platform::XdsBuilder xds_builder(/*xds_server_address=*/std::string(TD_API_ENDPOINT), /*xds_server_port=*/443); - xds_builder.setJwtAuthenticationToken(jwtToken(), Platform::DefaultJwtTokenLifetimeSeconds); + xds_builder.setAuthenticationToken("x-goog-api-key", std::string(api_key)); xds_builder.setSslRootCerts(std::move(root_certs)); xds_builder.addClusterDiscoveryService(); builder_.addLogLevel(Platform::LogLevel::trace) diff --git a/mobile/test/swift/EngineBuilderTests.swift b/mobile/test/swift/EngineBuilderTests.swift index c534183ff20d..c2713374592b 100644 --- a/mobile/test/swift/EngineBuilderTests.swift +++ b/mobile/test/swift/EngineBuilderTests.swift @@ -393,6 +393,22 @@ final class EngineBuilderTests: XCTestCase { } func testAddingXdsSecurityConfigurationWhenRunningEnvoy() { + let xdsBuilder = XdsBuilder(xdsServerAddress: "FAKE_SWIFT_ADDRESS", xdsServerPort: 0) + .setAuthenticationToken(header: "x-goog-api-key", token: "A1B2C3") + .setSslRootCerts(rootCerts: "fake_ssl_root_certs") + .setSni(sni: "fake_sni_address") + .addRuntimeDiscoveryService(resourceName: "some_rtds_resource", timeoutInSeconds: 14325) + let bootstrapDebugDescription = EngineBuilder() + .addEngineType(MockEnvoyEngine.self) + .setXds(xdsBuilder) + .bootstrapDebugDescription() + XCTAssertTrue(bootstrapDebugDescription.contains("x-goog-api-key")) + XCTAssertTrue(bootstrapDebugDescription.contains("A1B2C3")) + XCTAssertTrue(bootstrapDebugDescription.contains("fake_ssl_root_certs")) + XCTAssertTrue(bootstrapDebugDescription.contains("fake_sni_address")) + } + + func testAddingXdsJwtSecurityConfigurationWhenRunningEnvoy() { let xdsBuilder = XdsBuilder(xdsServerAddress: "FAKE_SWIFT_ADDRESS", xdsServerPort: 0) .setJwtAuthenticationToken(token: "fake_jwt_token", tokenLifetimeInSeconds: 12345) .setSslRootCerts(rootCerts: "fake_ssl_root_certs")