From a254f1651981f11802a3ea394d5aab419c22df40 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Wed, 18 Jun 2025 11:24:40 -0600 Subject: [PATCH 1/3] Fix issue loading SigV4 config --- .../src/plugins/authentication/subgraph.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apollo-router/src/plugins/authentication/subgraph.rs b/apollo-router/src/plugins/authentication/subgraph.rs index 56dd2a5943..e1613d52f5 100644 --- a/apollo-router/src/plugins/authentication/subgraph.rs +++ b/apollo-router/src/plugins/authentication/subgraph.rs @@ -16,7 +16,9 @@ use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_async::time::SystemTimeSource; use aws_smithy_http_client::tls::Provider; use aws_smithy_http_client::tls::rustls_provider::CryptoMode; +use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion; use aws_smithy_runtime_api::client::identity::Identity; +use aws_types::SdkConfig; use aws_types::region::Region; use aws_types::sdk_config::SharedCredentialsProvider; use http::HeaderMap; @@ -111,6 +113,18 @@ impl AWSSigV4Config { let role_provider_builder = self.assume_role().map(|assume_role_provider| { let rp = aws_config::sts::AssumeRoleProvider::builder(assume_role_provider.role_arn.clone()) + .configure( + &SdkConfig::builder() + .http_client( + aws_smithy_http_client::Builder::new() + .tls_provider(Provider::Rustls(CryptoMode::Ring)) + .build_https(), + ) + .sleep_impl(TokioSleep::new()) + .time_source(SystemTimeSource::new()) + .behavior_version(BehaviorVersion::latest()) + .build(), + ) .session_name(assume_role_provider.session_name.clone()) .region(region.clone()); if let Some(external_id) = &assume_role_provider.external_id { From 1c2c38ea2f95a870f393b2913132415807b92cec Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Wed, 18 Jun 2025 15:08:10 -0600 Subject: [PATCH 2/3] Add a CI-only integration test for SigV4 using Connectors --- apollo-router/tests/integration/connectors.rs | 115 +++++++++++------ .../fixtures/connectors_sigv4.graphql | 116 ++++++++++++++++++ .../fixtures/connectors_sigv4.router.yaml | 12 ++ 3 files changed, 205 insertions(+), 38 deletions(-) create mode 100644 apollo-router/tests/integration/fixtures/connectors_sigv4.graphql create mode 100644 apollo-router/tests/integration/fixtures/connectors_sigv4.router.yaml diff --git a/apollo-router/tests/integration/connectors.rs b/apollo-router/tests/integration/connectors.rs index e63c1de078..a8e51b7e9f 100644 --- a/apollo-router/tests/integration/connectors.rs +++ b/apollo-router/tests/integration/connectors.rs @@ -36,8 +36,8 @@ mod apq { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `apq` indirectly targets a connector-enabled subgraph, which is not supported"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `apq` indirectly targets a connector-enabled subgraph, which is not supported"#) + .await; Ok(()) } @@ -76,8 +76,8 @@ mod apq { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `apq` is explicitly configured for connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `apq` is explicitly configured for connector-enabled subgraph"#) + .await; Ok(()) } @@ -126,9 +126,12 @@ mod apq { mod authentication { use std::path::PathBuf; + use serde_json::Value; + use serde_json::json; use tower::BoxError; use crate::integration::IntegrationTest; + use crate::integration::common::Query; #[tokio::test(flavor = "multi_thread")] async fn incompatible_warnings_on_all() -> Result<(), BoxError> { @@ -212,8 +215,8 @@ mod authentication { router.start().await; router - .wait_for_log_message(r#""subgraphs":"connectors","message":"plugin `authentication` is enabled for connector-enabled subgraphs"#) - .await; + .wait_for_log_message(r#""subgraphs":"connectors","message":"plugin `authentication` is enabled for connector-enabled subgraphs"#) + .await; Ok(()) } @@ -266,8 +269,8 @@ mod authentication { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","sources":"jsonPlaceholder","message":"plugin `authentication` is enabled for a connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","sources":"jsonPlaceholder","message":"plugin `authentication` is enabled for a connector-enabled subgraph"#) + .await; Ok(()) } @@ -320,11 +323,47 @@ mod authentication { router.start().await; router - .assert_log_not_contains(r#""subgraph":"connectors","sources":"jsonPlaceholder","message":"plugin `authentication` is enabled for a connector-enabled subgraph"#) - .await; + .assert_log_not_contains(r#""subgraph":"connectors","sources":"jsonPlaceholder","message":"plugin `authentication` is enabled for a connector-enabled subgraph"#) + .await; Ok(()) } + + #[tokio::test(flavor = "multi_thread")] + #[cfg_attr(not(feature = "ci"), ignore)] + async fn test_aws_sig_v4_signing() { + let mut router = IntegrationTest::builder() + .config(include_str!("fixtures/connectors_sigv4.router.yaml")) + .supergraph(PathBuf::from( + "tests/integration/fixtures/connectors_sigv4.graphql", + )) + .build() + .await; + + router.start().await; + router.assert_started().await; + + let (_, response) = router + .execute_query( + Query::builder() + .body(json! ({"query": "query { instances }"})) + .build(), + ) + .await; + let body: Value = response.json().await.unwrap(); + router.graceful_shutdown().await; + let body = body.as_object().expect("Response body should be object"); + let errors = body.get("errors"); + assert!(errors.is_none(), "query generated errors: {errors:?}"); + let me = body + .get("data") + .expect("Response body should have data") + .as_object() + .expect("Data should be object") + .get("instances") + .expect("Data should have instances"); + assert!(me.is_null()); + } } mod batching { @@ -367,8 +406,8 @@ mod batching { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `batching` indirectly targets a connector-enabled subgraph, which is not supported"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `batching` indirectly targets a connector-enabled subgraph, which is not supported"#) + .await; Ok(()) } @@ -409,8 +448,8 @@ mod batching { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `batching` is explicitly configured for connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `batching` is explicitly configured for connector-enabled subgraph"#) + .await; Ok(()) } @@ -497,8 +536,8 @@ mod coprocessor { router.start().await; router - .wait_for_log_message(r#""subgraphs":"connectors","message":"coprocessors which hook into `subgraph_request` or `subgraph_response`"#) - .await; + .wait_for_log_message(r#""subgraphs":"connectors","message":"coprocessors which hook into `subgraph_request` or `subgraph_response`"#) + .await; Ok(()) } @@ -534,8 +573,8 @@ mod coprocessor { router.start().await; router - .assert_log_not_contains(r#""subgraphs":"connectors","message":"coprocessors which hook into `subgraph_request` or `subgraph_response`"#) - .await; + .assert_log_not_contains(r#""subgraphs":"connectors","message":"coprocessors which hook into `subgraph_request` or `subgraph_response`"#) + .await; Ok(()) } @@ -580,8 +619,8 @@ mod entity_cache { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `preview_entity_cache` indirectly targets a connector-enabled subgraph, which is not supported"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `preview_entity_cache` indirectly targets a connector-enabled subgraph, which is not supported"#) + .await; Ok(()) } @@ -621,8 +660,8 @@ mod entity_cache { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `preview_entity_cache` is explicitly configured for connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `preview_entity_cache` is explicitly configured for connector-enabled subgraph"#) + .await; Ok(()) } @@ -712,8 +751,8 @@ mod headers { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `headers` indirectly targets a connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `headers` indirectly targets a connector-enabled subgraph"#) + .await; Ok(()) } @@ -753,8 +792,8 @@ mod headers { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `headers` is explicitly configured for connector-enabled subgraph"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `headers` is explicitly configured for connector-enabled subgraph"#) + .await; Ok(()) } @@ -796,8 +835,8 @@ mod rhai { router.start().await; router - .wait_for_log_message(r#""subgraphs":"connectors","message":"rhai scripts which hook into `subgraph_request` or `subgraph_response`"#) - .await; + .wait_for_log_message(r#""subgraphs":"connectors","message":"rhai scripts which hook into `subgraph_request` or `subgraph_response`"#) + .await; Ok(()) } @@ -843,8 +882,8 @@ mod telemetry { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `telemetry` is indirectly configured to send errors to Apollo studio for a connector-enabled subgraph, which is only supported when `preview_extended_error_metrics` is enabled"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `telemetry` is indirectly configured to send errors to Apollo studio for a connector-enabled subgraph, which is only supported when `preview_extended_error_metrics` is enabled"#) + .await; Ok(()) } @@ -885,8 +924,8 @@ mod telemetry { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `telemetry` is explicitly configured to send errors to Apollo studio for connector-enabled subgraph, which is only supported when `preview_extended_error_metrics` is enabled"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"plugin `telemetry` is explicitly configured to send errors to Apollo studio for connector-enabled subgraph, which is only supported when `preview_extended_error_metrics` is enabled"#) + .await; Ok(()) } @@ -1016,8 +1055,8 @@ mod tls { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"The `tls` plugin is explicitly configured for a subgraph containing connectors, which is not supported. Instead, configure the connector sources directly using `tls.connector.sources..`."#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"The `tls` plugin is explicitly configured for a subgraph containing connectors, which is not supported. Instead, configure the connector sources directly using `tls.connector.sources..`."#) + .await; Ok(()) } @@ -1061,8 +1100,8 @@ mod traffic_shaping { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"The `traffic_shaping` plugin is explicitly configured for a subgraph containing connectors, which is not supported. Instead, configure the connector sources directly using `traffic_shaping.connector.sources..`."#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"The `traffic_shaping` plugin is explicitly configured for a subgraph containing connectors, which is not supported. Instead, configure the connector sources directly using `traffic_shaping.connector.sources..`."#) + .await; Ok(()) } @@ -1104,8 +1143,8 @@ mod url_override { router.start().await; router - .wait_for_log_message(r#""subgraph":"connectors","message":"overriding a subgraph URL for a connectors-enabled subgraph is not supported"#) - .await; + .wait_for_log_message(r#""subgraph":"connectors","message":"overriding a subgraph URL for a connectors-enabled subgraph is not supported"#) + .await; Ok(()) } diff --git a/apollo-router/tests/integration/fixtures/connectors_sigv4.graphql b/apollo-router/tests/integration/fixtures/connectors_sigv4.graphql new file mode 100644 index 0000000000..6b258d6fba --- /dev/null +++ b/apollo-router/tests/integration/fixtures/connectors_sigv4.graphql @@ -0,0 +1,116 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION) + @link(url: "https://specs.apollo.dev/connect/v0.2", for: EXECUTION) + @join__directive( + graphs: [SUBGRAPH] + name: "link" + args: { + url: "https://specs.apollo.dev/connect/v0.2" + import: ["@connect", "@source"] + } + ) + @join__directive( + graphs: [SUBGRAPH] + name: "source" + args: { + name: "ec2" + http: { + baseURL: "https://ec2.eu-north-1.api.aws" + headers: [{ name: "Accept", value: "application/json" }] + } + } + ) { + query: Query +} + +directive @join__directive( + graphs: [join__Graph!] + name: String! + args: join__DirectiveArguments +) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean + overrideLabel: String + contextArguments: [join__ContextArgument!] +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +input join__ContextArgument { + name: String! + type: String! + context: String! + selection: join__FieldValue! +} + +scalar join__DirectiveArguments + +scalar join__FieldSet + +scalar join__FieldValue + +enum join__Graph { + SUBGRAPH @join__graph(name: "subgraph", url: "http://ignore") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Query @join__type(graph: SUBGRAPH) { + instances: String + @join__directive( + graphs: [SUBGRAPH] + name: "connect" + args: { + source: "ec2" + http: { GET: "?Action=DescribeInstances&DryRun=1" } + selection: "$" + } + ) +} diff --git a/apollo-router/tests/integration/fixtures/connectors_sigv4.router.yaml b/apollo-router/tests/integration/fixtures/connectors_sigv4.router.yaml new file mode 100644 index 0000000000..c8a998f37c --- /dev/null +++ b/apollo-router/tests/integration/fixtures/connectors_sigv4.router.yaml @@ -0,0 +1,12 @@ +authentication: + connector: + sources: + subgraph.ec2: + aws_sig_v4: + default_chain: + profile_name: "default" + region: "eu-north-1" + service_name: "ec2" + assume_role: + role_arn: ${env.AWS_ROLE_ARN} + session_name: "connector" \ No newline at end of file From c834c56260867509a9fb20bb400d8d3fc138aa94 Mon Sep 17 00:00:00 2001 From: Dylan Anthony Date: Wed, 18 Jun 2025 15:44:41 -0600 Subject: [PATCH 3/3] Add changeset --- .changesets/fix_mantel_headband_iced_move.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changesets/fix_mantel_headband_iced_move.md diff --git a/.changesets/fix_mantel_headband_iced_move.md b/.changesets/fix_mantel_headband_iced_move.md new file mode 100644 index 0000000000..66289dfbc2 --- /dev/null +++ b/.changesets/fix_mantel_headband_iced_move.md @@ -0,0 +1,5 @@ +### Fix issue loading SigV4 config ([PR #7726](https://github.com/apollographql/router/pull/7726)) + +Fixed an issue introduced in Router 2.3.0 where some SigV4 configurations would fail to start. + +By [@dylan-apollo](https://github.com/dylan-apollo) in https://github.com/apollographql/router/pull/7726