From 3fcb78de7c6444cd1f7fe4cca6533bcc6bf8a085 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 20 Sep 2023 17:02:13 +0200 Subject: [PATCH] TLS client authentication (#3794) Fix #3414 The router now supports TLS client authentication when connecting to subgraphs. It can be configured as follows: ```yaml tls: subgraph: all: client_authentication: certificate_chain: ${file./path/to/certificate_chain.pem} key: ${file./path/to/key.pem} # if configuring for a specific subgraph: subgraphs: # subgraph name products: client_authentication: certificate_chain: ${file./path/to/certificate_chain.pem} key: ${file./path/to/key.pem} ``` This also adds tests for subgraph TLS connections: * self signed server certificates * root certificate authority override * client authentication --- .changesets/feat_geal_subgraph_mtls.md | 21 + apollo-router/src/configuration/metrics.rs | 10 + apollo-router/src/configuration/mod.rs | 38 +- ...etrics__test__metrics@tls.router.yaml.snap | 10 + ...nfiguration__tests__schema_generation.snap | 55 +- .../testdata/metrics/tls.router.yaml | 106 ++++ apollo-router/src/router_factory.rs | 31 +- .../src/services/subgraph_service.rs | 546 ++++++++++++++++-- apollo-router/src/services/testdata/CA/ca.crt | 31 + apollo-router/src/services/testdata/CA/ca.key | 52 ++ .../src/services/testdata/client.crt | 33 ++ .../src/services/testdata/client.csr | 26 + .../src/services/testdata/client.ext | 7 + .../src/services/testdata/client.key | 52 ++ .../src/services/testdata/server.crt | 33 ++ .../src/services/testdata/server.csr | 26 + .../src/services/testdata/server.ext | 6 + .../src/services/testdata/server.key | 52 ++ .../services/testdata/server_self_signed.crt | 32 + .../services/testdata/server_self_signed.csr | 26 + apollo-router/src/services/testdata/tls.md | 82 +++ docs/source/configuration/overview.mdx | 22 +- 22 files changed, 1216 insertions(+), 81 deletions(-) create mode 100644 .changesets/feat_geal_subgraph_mtls.md create mode 100644 apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@tls.router.yaml.snap create mode 100644 apollo-router/src/configuration/testdata/metrics/tls.router.yaml create mode 100644 apollo-router/src/services/testdata/CA/ca.crt create mode 100644 apollo-router/src/services/testdata/CA/ca.key create mode 100644 apollo-router/src/services/testdata/client.crt create mode 100644 apollo-router/src/services/testdata/client.csr create mode 100644 apollo-router/src/services/testdata/client.ext create mode 100644 apollo-router/src/services/testdata/client.key create mode 100644 apollo-router/src/services/testdata/server.crt create mode 100644 apollo-router/src/services/testdata/server.csr create mode 100644 apollo-router/src/services/testdata/server.ext create mode 100644 apollo-router/src/services/testdata/server.key create mode 100644 apollo-router/src/services/testdata/server_self_signed.crt create mode 100644 apollo-router/src/services/testdata/server_self_signed.csr create mode 100644 apollo-router/src/services/testdata/tls.md diff --git a/.changesets/feat_geal_subgraph_mtls.md b/.changesets/feat_geal_subgraph_mtls.md new file mode 100644 index 0000000000..a377ab52bf --- /dev/null +++ b/.changesets/feat_geal_subgraph_mtls.md @@ -0,0 +1,21 @@ +### TLS client authentication for subgraph requests ([Issue #3414](https://github.com/apollographql/router/issues/3414)) + +The router now supports TLS client authentication when connecting to subgraphs. It can be configured as follows: + +```yaml +tls: + subgraph: + all: + client_authentication: + certificate_chain: ${file./path/to/certificate_chain.pem} + key: ${file./path/to/key.pem} + # if configuring for a specific subgraph: + subgraphs: + # subgraph name + products: + client_authentication: + certificate_chain: ${file./path/to/certificate_chain.pem} + key: ${file./path/to/key.pem} +``` + +By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/3794 \ No newline at end of file diff --git a/apollo-router/src/configuration/metrics.rs b/apollo-router/src/configuration/metrics.rs index 1bd5db048a..20dd595598 100644 --- a/apollo-router/src/configuration/metrics.rs +++ b/apollo-router/src/configuration/metrics.rs @@ -260,6 +260,16 @@ impl Metrics { opt.subgraph, "$.subgraph..enabled[?(@ == true)]" ); + log_usage_metrics!( + value.apollo.router.config.tls, + "$.tls", + opt.router.tls.server, + "$.supergraph", + opt.router.tls.subgraph.ca_override, + "$[?(@.subgraph..certificate_authorities)]", + opt.router.tls.subgraph.client_authentication, + "$.subgraph..client_authentication" + ); log_usage_metrics!( value.apollo.router.config.traffic_shaping, "$.traffic_shaping", diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 4aaa11c1c9..02c1694051 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -929,13 +929,11 @@ where .map_err(serde::de::Error::custom) .and_then(|mut certs| { if certs.len() > 1 { - Err(serde::de::Error::custom( - "expected exactly one server certificate", - )) + Err(serde::de::Error::custom("expected exactly one certificate")) } else { - certs.pop().ok_or(serde::de::Error::custom( - "expected exactly one server certificate", - )) + certs + .pop() + .ok_or(serde::de::Error::custom("expected exactly one certificate")) } }) } @@ -955,16 +953,16 @@ where { let data = String::deserialize(deserializer)?; - load_keys(&data).map_err(serde::de::Error::custom) + load_key(&data).map_err(serde::de::Error::custom) } -fn load_certs(data: &str) -> io::Result> { +pub(crate) fn load_certs(data: &str) -> io::Result> { certs(&mut BufReader::new(data.as_bytes())) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) .map(|mut certs| certs.drain(..).map(Certificate).collect()) } -fn load_keys(data: &str) -> io::Result { +pub(crate) fn load_key(data: &str) -> io::Result { let mut reader = BufReader::new(data.as_bytes()); let mut key_iterator = iter::from_fn(|| read_one(&mut reader).transpose()); @@ -1008,14 +1006,20 @@ fn load_keys(data: &str) -> io::Result { pub(crate) struct TlsSubgraph { /// list of certificate authorities in PEM format pub(crate) certificate_authorities: Option, + /// client certificate authentication + pub(crate) client_authentication: Option, } #[buildstructor::buildstructor] impl TlsSubgraph { #[builder] - pub(crate) fn new(certificate_authorities: Option) -> Self { + pub(crate) fn new( + certificate_authorities: Option, + client_authentication: Option, + ) -> Self { Self { certificate_authorities, + client_authentication, } } } @@ -1026,6 +1030,20 @@ impl Default for TlsSubgraph { } } +/// TLS client authentication +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub(crate) struct TlsClientAuth { + /// list of certificates in PEM format + #[serde(deserialize_with = "deserialize_certificate_chain", skip_serializing)] + #[schemars(with = "String")] + pub(crate) certificate_chain: Vec, + /// key in PEM format + #[serde(deserialize_with = "deserialize_key", skip_serializing)] + #[schemars(with = "String")] + pub(crate) key: PrivateKey, +} + /// Configuration options pertaining to the sandbox page. #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@tls.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@tls.router.yaml.snap new file mode 100644 index 0000000000..5e9309469e --- /dev/null +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@tls.router.yaml.snap @@ -0,0 +1,10 @@ +--- +source: apollo-router/src/configuration/metrics.rs +expression: "&metrics.metrics" +--- +value.apollo.router.config.tls: + - 1 + - opt__router__tls__server__: true + opt__router__tls__subgraph__ca_override__: true + opt__router__tls__subgraph__client_authentication__: true + diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index df03391cf4..428ef466d4 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -5384,7 +5384,8 @@ expression: "&schema" "supergraph": null, "subgraph": { "all": { - "certificate_authorities": null + "certificate_authorities": null, + "client_authentication": null }, "subgraphs": {} } @@ -5395,7 +5396,8 @@ expression: "&schema" "description": "Configuration options pertaining to the subgraph server component.", "default": { "all": { - "certificate_authorities": null + "certificate_authorities": null, + "client_authentication": null }, "subgraphs": {} }, @@ -5404,7 +5406,8 @@ expression: "&schema" "all": { "description": "options applying to all subgraphs", "default": { - "certificate_authorities": null + "certificate_authorities": null, + "client_authentication": null }, "type": "object", "properties": { @@ -5413,6 +5416,29 @@ expression: "&schema" "default": null, "type": "string", "nullable": true + }, + "client_authentication": { + "description": "client certificate authentication", + "default": null, + "type": "object", + "required": [ + "certificate_chain", + "key" + ], + "properties": { + "certificate_chain": { + "description": "list of certificates in PEM format", + "writeOnly": true, + "type": "string" + }, + "key": { + "description": "key in PEM format", + "writeOnly": true, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true } }, "additionalProperties": false @@ -5430,6 +5456,29 @@ expression: "&schema" "default": null, "type": "string", "nullable": true + }, + "client_authentication": { + "description": "client certificate authentication", + "default": null, + "type": "object", + "required": [ + "certificate_chain", + "key" + ], + "properties": { + "certificate_chain": { + "description": "list of certificates in PEM format", + "writeOnly": true, + "type": "string" + }, + "key": { + "description": "key in PEM format", + "writeOnly": true, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true } }, "additionalProperties": false diff --git a/apollo-router/src/configuration/testdata/metrics/tls.router.yaml b/apollo-router/src/configuration/testdata/metrics/tls.router.yaml new file mode 100644 index 0000000000..00d1d3f63d --- /dev/null +++ b/apollo-router/src/configuration/testdata/metrics/tls.router.yaml @@ -0,0 +1,106 @@ +tls: + supergraph: + # write the cert content directly because the test does not do expansion + certificate: &cert | + -----BEGIN CERTIFICATE----- + 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 + -----END CERTIFICATE----- + certificate_chain: *cert + key: &key | + -----BEGIN PRIVATE KEY----- + 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 + wJ/KXhcyqjcyToe5KOxF5cOo0BLFpNwQ + -----END PRIVATE KEY----- + + subgraph: + all: + client_authentication: + certificate_chain: *cert + key: *key + # if configuring for a specific subgraph: + subgraphs: + # subgraph name + products: + certificate_authorities: *cert + client_authentication: + certificate_chain: *cert + key: *key \ No newline at end of file diff --git a/apollo-router/src/router_factory.rs b/apollo-router/src/router_factory.rs index 62dc5d888e..2ed2923d5c 100644 --- a/apollo-router/src/router_factory.rs +++ b/apollo-router/src/router_factory.rs @@ -290,32 +290,15 @@ pub(crate) async fn create_subgraph_services( let mut subgraph_services = IndexMap::new(); for (name, _) in schema.subgraphs() { - let subgraph_root_store = configuration - .tls - .subgraph - .subgraphs - .get(name) - .as_ref() - .and_then(|subgraph| subgraph.create_certificate_store()) - .transpose()? - .or_else(|| tls_root_store.clone()); - let subgraph_service = shaping.subgraph_service_internal( name, - SubgraphService::new( + SubgraphService::from_config( name, - configuration - .apq - .subgraph - .subgraphs - .get(name) - .map(|apq| apq.enabled) - .unwrap_or(configuration.apq.subgraph.all.enabled), - subgraph_root_store, + configuration, + &tls_root_store, shaping.enable_subgraph_http2(name), subscription_plugin_conf.clone(), - configuration.notify.clone(), - ), + )?, ); subgraph_services.insert(name.clone(), subgraph_service); } @@ -360,14 +343,16 @@ impl YamlRouterFactory { } impl TlsSubgraph { - fn create_certificate_store(&self) -> Option> { + pub(crate) fn create_certificate_store( + &self, + ) -> Option> { self.certificate_authorities .as_deref() .map(create_certificate_store) } } -fn create_certificate_store( +pub(crate) fn create_certificate_store( certificate_authorities: &str, ) -> Result { let mut store = RootCertStore::empty(); diff --git a/apollo-router/src/services/subgraph_service.rs b/apollo-router/src/services/subgraph_service.rs index 2d0044d488..9333bc16aa 100644 --- a/apollo-router/src/services/subgraph_service.rs +++ b/apollo-router/src/services/subgraph_service.rs @@ -37,6 +37,7 @@ use mediatype::names::JSON; use mediatype::MediaType; use mime::APPLICATION_JSON; use opentelemetry::global; +use rustls::ClientConfig; use rustls::RootCertStore; use schemars::JsonSchema; use serde::Serialize; @@ -75,6 +76,7 @@ use crate::query_planner::OperationKind; use crate::services::layers::apq; use crate::services::SubgraphRequest; use crate::services::SubgraphResponse; +use crate::Configuration; use crate::Context; use crate::Notify; @@ -151,28 +153,87 @@ pub(crate) struct SubgraphService { } impl SubgraphService { + pub(crate) fn from_config( + service: impl Into, + configuration: &Configuration, + tls_root_store: &Option, + enable_http2: bool, + subscription_config: Option, + ) -> Result { + let name: String = service.into(); + let tls_cert_store = configuration + .tls + .subgraph + .subgraphs + .get(&name) + .as_ref() + .and_then(|subgraph| subgraph.create_certificate_store()) + .transpose()? + .or_else(|| tls_root_store.clone()); + let enable_apq = configuration + .apq + .subgraph + .subgraphs + .get(&name) + .map(|apq| apq.enabled) + .unwrap_or(configuration.apq.subgraph.all.enabled); + let client_cert_config = configuration + .tls + .subgraph + .subgraphs + .get(&name) + .as_ref() + .and_then(|tls| tls.client_authentication.as_ref()) + .or(configuration + .tls + .subgraph + .all + .client_authentication + .as_ref()); + + let tls_builder = rustls::ClientConfig::builder().with_safe_defaults(); + let tls_client_config = match (tls_cert_store, client_cert_config) { + (None, None) => tls_builder.with_native_roots().with_no_client_auth(), + (Some(store), None) => tls_builder + .with_root_certificates(store) + .with_no_client_auth(), + (None, Some(client_auth_config)) => { + tls_builder.with_native_roots().with_client_auth_cert( + client_auth_config.certificate_chain.clone(), + client_auth_config.key.clone(), + )? + } + (Some(store), Some(client_auth_config)) => tls_builder + .with_root_certificates(store) + .with_client_auth_cert( + client_auth_config.certificate_chain.clone(), + client_auth_config.key.clone(), + )?, + }; + + Ok(SubgraphService::new( + name, + enable_apq, + enable_http2, + subscription_config, + tls_client_config, + configuration.notify.clone(), + )) + } + pub(crate) fn new( service: impl Into, enable_apq: bool, - tls_cert_store: Option, enable_http2: bool, subscription_config: Option, + tls_config: ClientConfig, notify: Notify, ) -> Self { let mut http_connector = HttpConnector::new(); http_connector.set_nodelay(true); http_connector.set_keepalive(Some(std::time::Duration::from_secs(60))); http_connector.enforce_http(false); - let tls_config = match tls_cert_store { - None => rustls::ClientConfig::builder() - .with_safe_defaults() - .with_native_roots() - .with_no_client_auth(), - Some(store) => rustls::ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(store) - .with_no_client_auth(), - }; + let builder = hyper_rustls::HttpsConnectorBuilder::new() .with_tls_config(tls_config) .https_or_http() @@ -1088,6 +1149,7 @@ where #[cfg(test)] mod tests { use std::convert::Infallible; + use std::io; use std::net::SocketAddr; use std::net::TcpListener; use std::str::FromStr; @@ -1105,8 +1167,15 @@ mod tests { use http::header::HOST; use http::StatusCode; use http::Uri; + use http::Version; + use hyper::server::conn::AddrIncoming; use hyper::service::make_service_fn; use hyper::Body; + use hyper_rustls::TlsAcceptor; + use rustls::server::AllowAnyAuthenticatedClient; + use rustls::Certificate; + use rustls::PrivateKey; + use rustls::ServerConfig; use serde_json_bytes::ByteString; use serde_json_bytes::Value; use tower::service_fn; @@ -1115,6 +1184,10 @@ mod tests { use SubgraphRequest; use super::*; + use crate::configuration::load_certs; + use crate::configuration::load_key; + use crate::configuration::TlsClientAuth; + use crate::configuration::TlsSubgraph; use crate::graphql::Error; use crate::graphql::Request; use crate::graphql::Response; @@ -1770,9 +1843,12 @@ mod tests { let subgraph_service = SubgraphService::new( "testbis", true, - None, false, subscription_config().into(), + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), Notify::builder().build(), ); let (tx, _rx) = mpsc::channel(2); @@ -1819,8 +1895,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_application_graphql_response(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -1853,8 +1938,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_application_json_response(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -1887,8 +1981,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_ok_status_invalid_response(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -1926,8 +2029,17 @@ mod tests { tokio::task::spawn( emulate_subgraph_invalid_response_invalid_status_application_json(listener), ); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -1969,8 +2081,17 @@ mod tests { tokio::task::spawn( emulate_subgraph_invalid_response_invalid_status_application_graphql(listener), ); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -2013,9 +2134,12 @@ mod tests { let subgraph_service = SubgraphService::new( "test", true, - None, false, subscription_config().into(), + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), Notify::builder().build(), ); let (tx, mut rx) = mpsc::channel(2); @@ -2073,9 +2197,12 @@ mod tests { let subgraph_service = SubgraphService::new( "test", true, - None, false, subscription_config().into(), + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), Notify::builder().build(), ); let (tx, _rx) = mpsc::channel(2); @@ -2122,8 +2249,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_bad_request(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -2164,8 +2300,17 @@ mod tests { let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_bad_response_format(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -2201,8 +2346,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_compressed_response(listener)); - let subgraph_service = - SubgraphService::new("test", false, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + false, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let resp = subgraph_service @@ -2242,8 +2396,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_subgraph_unauthorized(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let response = subgraph_service @@ -2279,8 +2442,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_persisted_query_not_supported_message(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); assert!(subgraph_service.clone().apq.as_ref().load(Relaxed)); @@ -2325,8 +2497,17 @@ mod tests { tokio::task::spawn(emulate_persisted_query_not_supported_extension_code( listener, )); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); assert!(subgraph_service.clone().apq.as_ref().load(Relaxed)); @@ -2369,8 +2550,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_persisted_query_not_found_message(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let resp = subgraph_service @@ -2410,8 +2600,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_persisted_query_not_found_extension_code(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let resp = subgraph_service @@ -2451,8 +2650,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_expected_apq_enabled_configuration(listener)); - let subgraph_service = - SubgraphService::new("test", true, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + true, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let resp = subgraph_service @@ -2492,8 +2700,17 @@ mod tests { let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); let socket_addr = listener.local_addr().unwrap(); tokio::task::spawn(emulate_expected_apq_disabled_configuration(listener)); - let subgraph_service = - SubgraphService::new("test", false, None, true, None, Notify::default()); + let subgraph_service = SubgraphService::new( + "test", + false, + true, + None, + ClientConfig::builder() + .with_safe_defaults() + .with_native_roots() + .with_no_client_auth(), + Notify::default(), + ); let url = Uri::from_str(&format!("http://{socket_addr}")).unwrap(); let resp = subgraph_service @@ -2527,4 +2744,245 @@ mod tests { assert_eq!(resp.response.body(), &expected_resp); } + + async fn tls_server( + listener: tokio::net::TcpListener, + certificates: Vec, + key: PrivateKey, + body: &'static str, + ) { + let acceptor = TlsAcceptor::builder() + .with_single_cert(certificates, key) + .unwrap() + .with_all_versions_alpn() + .with_incoming(AddrIncoming::from_listener(listener).unwrap()); + let service = make_service_fn(|_| async { + Ok::<_, io::Error>(service_fn(|_req| async { + Ok::<_, io::Error>( + http::Response::builder() + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .status(StatusCode::OK) + .version(Version::HTTP_11) + .body::(body.into()) + .unwrap(), + ) + })) + }); + let server = Server::builder(acceptor).serve(service); + server.await.unwrap() + } + + #[tokio::test(flavor = "multi_thread")] + async fn tls_self_signed() { + let certificate_pem = include_str!("./testdata/server_self_signed.crt"); + let key_pem = include_str!("./testdata/server.key"); + + let certificates = load_certs(certificate_pem).unwrap(); + let key = load_key(key_pem).unwrap(); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let socket_addr = listener.local_addr().unwrap(); + tokio::task::spawn(tls_server(listener, certificates, key, r#"{"data": null}"#)); + + // we cannot parse a configuration from text, because certificates are generally + // added by file expansion and we don't have access to that here, and inserting + // the PEM data directly generates parsing issues due to end of line characters + let mut config = Configuration::default(); + config.tls.subgraph.subgraphs.insert( + "test".to_string(), + TlsSubgraph { + certificate_authorities: Some(certificate_pem.into()), + client_authentication: None, + }, + ); + let subgraph_service = + SubgraphService::from_config("test", &config, &None, false, None).unwrap(); + + let url = Uri::from_str(&format!("https://localhost:{}", socket_addr.port())).unwrap(); + let response = subgraph_service + .oneshot(SubgraphRequest { + supergraph_request: Arc::new( + http::Request::builder() + .header(HOST, "host") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + ), + subgraph_request: http::Request::builder() + .header(HOST, "rhost") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .uri(url) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + operation_kind: OperationKind::Query, + context: Context::new(), + subscription_stream: None, + connection_closed_signal: None, + }) + .await + .unwrap(); + assert_eq!(response.response.body().data, Some(Value::Null)); + } + + #[tokio::test(flavor = "multi_thread")] + async fn tls_custom_root() { + let certificate_pem = include_str!("./testdata/server.crt"); + let ca_pem = include_str!("./testdata/CA/ca.crt"); + let key_pem = include_str!("./testdata/server.key"); + + let mut certificates = load_certs(certificate_pem).unwrap(); + certificates.extend(load_certs(ca_pem).unwrap()); + let key = load_key(key_pem).unwrap(); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let socket_addr = listener.local_addr().unwrap(); + tokio::task::spawn(tls_server(listener, certificates, key, r#"{"data": null}"#)); + + // we cannot parse a configuration from text, because certificates are generally + // added by file expansion and we don't have access to that here, and inserting + // the PEM data directly generates parsing issues due to end of line characters + let mut config = Configuration::default(); + config.tls.subgraph.subgraphs.insert( + "test".to_string(), + TlsSubgraph { + certificate_authorities: Some(ca_pem.into()), + client_authentication: None, + }, + ); + let subgraph_service = + SubgraphService::from_config("test", &config, &None, false, None).unwrap(); + + let url = Uri::from_str(&format!("https://localhost:{}", socket_addr.port())).unwrap(); + let response = subgraph_service + .oneshot(SubgraphRequest { + supergraph_request: Arc::new( + http::Request::builder() + .header(HOST, "host") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + ), + subgraph_request: http::Request::builder() + .header(HOST, "rhost") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .uri(url) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + operation_kind: OperationKind::Query, + context: Context::new(), + subscription_stream: None, + connection_closed_signal: None, + }) + .await + .unwrap(); + assert_eq!(response.response.body().data, Some(Value::Null)); + } + + async fn tls_server_with_client_auth( + listener: tokio::net::TcpListener, + certificates: Vec, + key: PrivateKey, + client_root: Certificate, + body: &'static str, + ) { + let mut client_auth_roots = RootCertStore::empty(); + client_auth_roots.add(&client_root).unwrap(); + + let client_auth = AllowAnyAuthenticatedClient::new(client_auth_roots).boxed(); + + let acceptor = TlsAcceptor::builder() + .with_tls_config( + ServerConfig::builder() + .with_safe_defaults() + .with_client_cert_verifier(client_auth) + .with_single_cert(certificates, key) + .unwrap(), + ) + .with_all_versions_alpn() + .with_incoming(AddrIncoming::from_listener(listener).unwrap()); + let service = make_service_fn(|_| async { + Ok::<_, io::Error>(service_fn(|_req| async { + Ok::<_, io::Error>( + http::Response::builder() + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .status(StatusCode::OK) + .version(Version::HTTP_11) + .body::(body.into()) + .unwrap(), + ) + })) + }); + let server = Server::builder(acceptor).serve(service); + server.await.unwrap() + } + + #[tokio::test(flavor = "multi_thread")] + async fn tls_client_auth() { + let server_certificate_pem = include_str!("./testdata/server.crt"); + let ca_pem = include_str!("./testdata/CA/ca.crt"); + let server_key_pem = include_str!("./testdata/server.key"); + + let mut server_certificates = load_certs(server_certificate_pem).unwrap(); + let ca_certificate = load_certs(ca_pem).unwrap().remove(0); + server_certificates.push(ca_certificate.clone()); + let key = load_key(server_key_pem).unwrap(); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let socket_addr = listener.local_addr().unwrap(); + tokio::task::spawn(tls_server_with_client_auth( + listener, + server_certificates, + key, + ca_certificate, + r#"{"data": null}"#, + )); + + let client_certificate_pem = include_str!("./testdata/client.crt"); + let client_key_pem = include_str!("./testdata/client.key"); + + let client_certificates = load_certs(client_certificate_pem).unwrap(); + let client_key = load_key(client_key_pem).unwrap(); + + // we cannot parse a configuration from text, because certificates are generally + // added by file expansion and we don't have access to that here, and inserting + // the PEM data directly generates parsing issues due to end of line characters + let mut config = Configuration::default(); + config.tls.subgraph.subgraphs.insert( + "test".to_string(), + TlsSubgraph { + certificate_authorities: Some(ca_pem.into()), + client_authentication: Some(TlsClientAuth { + certificate_chain: client_certificates, + key: client_key, + }), + }, + ); + let subgraph_service = + SubgraphService::from_config("test", &config, &None, false, None).unwrap(); + + let url = Uri::from_str(&format!("https://localhost:{}", socket_addr.port())).unwrap(); + let response = subgraph_service + .oneshot(SubgraphRequest { + supergraph_request: Arc::new( + http::Request::builder() + .header(HOST, "host") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + ), + subgraph_request: http::Request::builder() + .header(HOST, "rhost") + .header(CONTENT_TYPE, APPLICATION_JSON.essence_str()) + .uri(url) + .body(Request::builder().query("query").build()) + .expect("expecting valid request"), + operation_kind: OperationKind::Query, + context: Context::new(), + subscription_stream: None, + connection_closed_signal: None, + }) + .await + .unwrap(); + assert_eq!(response.response.body().data, Some(Value::Null)); + } } diff --git a/apollo-router/src/services/testdata/CA/ca.crt b/apollo-router/src/services/testdata/CA/ca.crt new file mode 100644 index 0000000000..6b12c0f9ef --- /dev/null +++ b/apollo-router/src/services/testdata/CA/ca.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYTCCA0mgAwIBAgIUFhEXodpzYVGQ/3LlWZnw+ogPBTcwDQYJKoZIhvcNAQEL +BQAwPzELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFwaFFMMRcwFQYD +VQQDDA5BcG9sbG8gVGVzdCBDQTAgFw0yMzA5MDgxNDA2NDhaGA8yMDUxMDEyNDE0 +MDY0OFowPzELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFwaFFMMRcw +FQYDVQQDDA5BcG9sbG8gVGVzdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBALu8ZVZoMc89GKXtnUKwJv6A4Wm/ZMeFJLhr2TuD6ahAGq5LmNNxdTt6 +SA1OMFSgZKjXrip5tIZPRHtxajNJLziIssegf7yUmhmnwMSsQo4ZL7WfmbXuvgKD +/z3NthFwk93OYHMNlJ7hunhzW9mgka/FyqjsnyhX4HTgiWalX/ar2TQ1z1w8T9gz +WgRqWTJhWJQO9lAMw43BTMURzo+f/1y/7ooTHkGwgPtWxgquSONsjOAhtfFhoCY9 +XB5xsKSOav0cNmwF2+16j1Z5znjIsBLBbno8HODBlA6abiE1XMmo2lsRtbHvSdHP +bElZd298tppSbDTIgWDDcL7hchpHGfBFho167gKIdEcioUzCuyhGH4XYHJco8gsG +cZFX2MfeuUNp5v/qjndBUqRwMqrqvU7iQTygJRuMlU0+ULW0niFvQCiNlACzQfOV +0zh+YZnPl0cP1UfElTcyWGN0x/8HLR06qyfLIragh93kpC1wX00YUpVvtGkZc3Y8 +XYgwR5Dw+XsyvrCzba8d0w/vHKEPF4uFMYSNN1viXN3AyswJsGNnwkVgwpg37W25 +kHqHBgqLxCup6+YGRzEhrKIhdHmzJss2RafCrpjcmA/Meuaup+85lbdTcdfcNcL4 +d7zkvIeoFU/NucUguLAztS0X05rOcNsq/4uZzslNhj1UcdkEbuHtAgMBAAGjUzBR +MB0GA1UdDgQWBBSz6p5WY8/jvOA/XR/YxIk3Y6DzFjAfBgNVHSMEGDAWgBSz6p5W +Y8/jvOA/XR/YxIk3Y6DzFjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4ICAQBkegjS3E4TOqchNINpRB+ChnLCUxVP/yQCxPtouF3MR91YNjXb6dIZChWS +Hrt2nB1LBtJDJFK6LEJXgz6AJniKzBotG8mPRmmNPQrSSXKnaCDRyWZ7+0My7x5Q +XvEziLidJA9itb6+Gys1PuUHQdWAC22FVNjlKAodPgtQaQrse5DVZWqK55OLTIi4 +p2SbrfiYe9Zs+ZGwlx2UAPilV3l/2cEJPrP0pNGePeLzvgfzmBFgyah75Y7tDxJ6 +1qIc2sifz0csN4uKbqGn7MG9Y6hw6ML5jjrHVdqizeM3GQzHADAzJfYZYn5gB1Hk +Z6zfNRiN5CrmU9ECIh+EgI6N1yDni6bzrF2mHszd3ShvoztvEAFRkeL43owaLC3y +Z/2asGC+PO3iDr9P13S8n/dnrFjAzi9ociijwwPqwm1YRjmO5yjzXWoSoQ1HGwBw +E9IbhqGUBjZWGqVPkfsy3Ox0HRpXT1PK9cl560iqKy0/bTVKQkVNk2LEbMCOTEYQ +453POBgkXY+W1uktVJOv0Yk8+9Yc2dZgqhJc6Xxz9bRXmJQZlE1pAXc3VNCic/W3 +8VLj5+R82vZk3+2QFTy3ZSIMZAlO8s22kM/tqXB+nAVTsrqBiHXRvph6S/deO6AF +o1O4Fwifrf2G6ln9bs+zdMlX1xbGgwxubLwp4I96yvtaSIBTuA== +-----END CERTIFICATE----- diff --git a/apollo-router/src/services/testdata/CA/ca.key b/apollo-router/src/services/testdata/CA/ca.key new file mode 100644 index 0000000000..58742c835d --- /dev/null +++ b/apollo-router/src/services/testdata/CA/ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +WGzJrJ309AV6WL7hOxRUJFHSmPh/zN0= +-----END PRIVATE KEY----- diff --git a/apollo-router/src/services/testdata/client.crt b/apollo-router/src/services/testdata/client.crt new file mode 100644 index 0000000000..289aa64b4e --- /dev/null +++ b/apollo-router/src/services/testdata/client.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/apollo-router/src/services/testdata/client.csr b/apollo-router/src/services/testdata/client.csr new file mode 100644 index 0000000000..b452f5bb2b --- /dev/null +++ b/apollo-router/src/services/testdata/client.csr @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEfDCCAmQCAQAwNzELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFw +aFFMMQ8wDQYDVQQDDAZyb3V0ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDZFAn0/6v8Q5K8JE4UhiTAL3utNBO33tKWVxCXIy1vnM9cfJ6hrpM8/qCF +Ac8HMs0SHfMuEWRSO9k/7C10L1I6n/O7jiFOr1hbgiQO9twXP1Ic+R1HnZrXv9A0 +Ql2zXw0I7SiZc7sgUBLsBmBOvCKhpJnlPuE0p5WTutpUSk6jp8SFqEjYVBxPWO+k +SSxT3idUad9Tw6jm2WS40NGRqrHaUlnpYxDqOf2yKHHH/6PIVAdXnPPH4d9rnS/f +9OphG07WvArrwb9fvCbXF7vNSIGXa7jfMK2xJUvf+KWj6g+0QtXzf1H+5EMC5jVc +fWz0KUJ1YFJdDWU7DzmwFM5Ow7hrTtzv5HQoh8aEy62xZeiWFuWPCgttPx1WSrVs +oe0WVmaEYY3enwkts2aM36NprUH2MaMDXwyXvroPaP/qSXgPk2Oa2SR8xam1jJuV +tBt7JNSGPcZdi8niySKa2tHke2ZhdrwIzASZQZUudm6i8b+SgTsqcfuFN7EeH7kK +NCceyuwXR6JTJPSKVor91DlU48ldwlmxpKB4+gZIwc0WGzzWkrRjD6S3E9NKIhpE ++xT+KVSUu7PI+eIzDb9njZSGb0yIqr+ijQPJ18lj7ZqyF3CzW0+TT/iDNIZa6J0f +BH95A1qYI2ini9XKBPImVTg+f3LzCfQb1UWgwzAYxgk6G9S4oQIDAQABoAAwDQYJ +KoZIhvcNAQELBQADggIBAI2SS4y1+rngn3Za3VlcPfd/+g+L/NOwVC19To5vtx7h +11ZmQMh6kWC0e+GbiBbK4+KZqFo120u7e+PBdiG+nM+qlCAuGi3f41DHHASHqf3r ++bw7/u3N4ZuzadVVNX4zamLlZ1NpWUqwPXi59MOdjYhAtz92nbNR9swKGzxVg4rk +U0g1FB0hXY7RGbkdURFGPDKCUHw4wAWK4KcTFUXmAHAX0fdGfBJe1ebFCS7bNmKr +C9NrwKV96swzWINsnpqk8i1M37KN7YgXyJPE7G4Tj+8hn+dc6E7Y60LJDwkTnx4v +yQycWnY6LP8MMT9quFPuaaadzAfQu4lRKc05TtxDIZf+2Pipj+h7TDJY2nbcDsY0 +b45gh7T0IrNkDdszfCSZbjYmVDg2Nxuy9i6hKMGngVllL+wJ8rjmXC5HRsCvgSyY +804z8OuK72xDD5x4zQEMJ40Tr+1wyqT5jWiO4iyj0faavdP9TrNZaLYeF/nfwNt5 +pr1A9TqcCLIc1Zid/i2/++/2jPT+0Fc7mpmSvxfc+CGv74d1Cb5pp/U/8uvukTQV +X/4WkWo/+KGg/WYgYBla7ysjursankZ0WYX5MCNbCJ6xSq9z6Y6U+ZK+SsC49S1B +G/H6UUnPGkA5k7EFJMFVAamBHVlAxOLzvx/poawxeIqEKJYZ2OewhKJNhw7tKrpp +-----END CERTIFICATE REQUEST----- diff --git a/apollo-router/src/services/testdata/client.ext b/apollo-router/src/services/testdata/client.ext new file mode 100644 index 0000000000..bc73c04527 --- /dev/null +++ b/apollo-router/src/services/testdata/client.ext @@ -0,0 +1,7 @@ +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection diff --git a/apollo-router/src/services/testdata/client.key b/apollo-router/src/services/testdata/client.key new file mode 100644 index 0000000000..bc6468071b --- /dev/null +++ b/apollo-router/src/services/testdata/client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +kqoAIqDF9xFf9gDYSdBhPAEBS6zKJQ== +-----END PRIVATE KEY----- diff --git a/apollo-router/src/services/testdata/server.crt b/apollo-router/src/services/testdata/server.crt new file mode 100644 index 0000000000..e88c728b78 --- /dev/null +++ b/apollo-router/src/services/testdata/server.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFwjCCA6qgAwIBAgIUZSVRG66ZJwAcvwu2rroe8C5lVqkwDQYJKoZIhvcNAQEL +BQAwPzELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFwaFFMMRcwFQYD +VQQDDA5BcG9sbG8gVGVzdCBDQTAgFw0yMzA5MDgxNTI0MTJaGA8yMDUxMDEyNDE1 +MjQxMlowJjELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFwaFFMMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsJ8bwoJJ8OLL0U5lrhYUeWpY +ej+eS5yibX02yWXCpzEgmailiugChwWwJrPElaarKQUYPTAlhgmFj342TWjTu8fY +VAJO3AoHchJtkaQnyJSipagOB+nZQ936T0E6k7wdt5BfLT5ow2Enxov88YxdFdfP +9HlYXShyXTLASCnyhgxMS8OjHO2koPQ270gV5NLVlg9+v+ZLYldDVPyLzd4TTlgW +0PStedHFaw2i5PbLYzasF+cIb6Ld0BrknPfNaiOo3GGGs2hOGnTABTu87Y5bW3x+ +11G18p3wVwhAgHPqgbjQnZoGMq1VfLJjJcb2LTPmtGMWGBfUOIFmGWARvnhY/Txj +ulRrWnLc+lAfj7zcoipZMkr2+JmTq6qoE9KPxdsYEuCS2aN04KK3hyppdOMIuhyF +BHs3sWd8l5kdo1qUmunawkQrBoG2QJZgJbiOFC/ESp5Z3cGVxrIRPZ4olmP0/S5B +8DkPyLcxjwJeinQU294o+5caJb/G53umVDG9TZb5BF+SWfQOlKnCU/NLupyEP0ZA +FgtKmn2ztzcLTamCnqfF5LwkM0AiqXpavIF9EgRFuJ/D80PFRmPtxKxlPhBvgfD/ +1U1dWI2XO97Gp3a70RAdJhDkAKIQQesPFHFezb9ExT7BcLJCkmXy21oY4Xf+nudf +j0ts50yyZcmmFF0Yig0CAwEAAaOBzDCByTAdBgNVHQ4EFgQUgv2yxjcZxbXqJBAS +iv1MdiesIo4wegYDVR0jBHMwcYAUs+qeVmPP47zgP10f2MSJN2Og8xahQ6RBMD8x +CzAJBgNVBAYTAkZSMRcwFQYDVQQKDA5BcG9sbG8gR3JhcGhRTDEXMBUGA1UEAwwO +QXBvbGxvIFRlc3QgQ0GCFBYRF6Hac2FRkP9y5VmZ8PqIDwU3MAsGA1UdDwQEAwIC +/DAUBgNVHREEDTALgglsb2NhbGhvc3QwCQYDVR0SBAIwADANBgkqhkiG9w0BAQsF +AAOCAgEAEx+/UKoPbbUVA8oGPaEeyFIZdS33zD/ZL+NCNISXXKhXeNTrzlMirQVa +2OkywIU7IWtMPwDSsM+GfO1NB3Ik8dcu+zw4QPeRMUeO6K7cHGx5AJLULq6HRKRb ++0/9fLOXxQUDoxElIN8zNqAGUsGGQGbbKGjodPiGfOnatjhCJccyGHPbq8Jek//u +SbfpRlwbBKyKL59lLXs8zjCrbC2Teal3/KhWt1/H3dAksLYTWDPmVQooq3ezwzek +DZh7ENGE81/2ipLPuHo4D04GGkWemLQ0h0vLmnIHQQKUscMP1Xj8hMDi2Rj/YfB0 +bf2Kle2KXJKkAOIn5n/SZrDciVydmYZEjZP7nUeB2dDwvFq+hbI6BRt94f2tAkYB +94pLWSIG4CfGI99sRHU1WwT4F1VCUJ3pdGXl+02/mSNUCKRQnv/3OiSgo2khDL1O +FxempDb0N9qPL2vftDhdBiPBPabvwPMGW9jRj1RHpxnTR2U8iY8cHc5E3s6N3BoT +AElWnH/E6v6Nex74C/vw3l4rg6Ym5LUJ/Kjh6OecHnnAP2IBxFFXgl/cyMJOiOby +Vhc5w5ySMag/S79nEjrcQzPaLPEtBQ/WUmj7mH99lBgcsJK6SyHp3twO5fETEGbw +3m/X7mTXFhxr2MBPUH8VrIC/hJtH3ni12KmiBlYJHbDaVwm4+G8= +-----END CERTIFICATE----- diff --git a/apollo-router/src/services/testdata/server.csr b/apollo-router/src/services/testdata/server.csr new file mode 100644 index 0000000000..0e2dc2723f --- /dev/null +++ b/apollo-router/src/services/testdata/server.csr @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEazCCAlMCAQAwJjELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFw +aFFMMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsJ8bwoJJ8OLL0U5l +rhYUeWpYej+eS5yibX02yWXCpzEgmailiugChwWwJrPElaarKQUYPTAlhgmFj342 +TWjTu8fYVAJO3AoHchJtkaQnyJSipagOB+nZQ936T0E6k7wdt5BfLT5ow2Enxov8 +8YxdFdfP9HlYXShyXTLASCnyhgxMS8OjHO2koPQ270gV5NLVlg9+v+ZLYldDVPyL +zd4TTlgW0PStedHFaw2i5PbLYzasF+cIb6Ld0BrknPfNaiOo3GGGs2hOGnTABTu8 +7Y5bW3x+11G18p3wVwhAgHPqgbjQnZoGMq1VfLJjJcb2LTPmtGMWGBfUOIFmGWAR +vnhY/TxjulRrWnLc+lAfj7zcoipZMkr2+JmTq6qoE9KPxdsYEuCS2aN04KK3hypp +dOMIuhyFBHs3sWd8l5kdo1qUmunawkQrBoG2QJZgJbiOFC/ESp5Z3cGVxrIRPZ4o +lmP0/S5B8DkPyLcxjwJeinQU294o+5caJb/G53umVDG9TZb5BF+SWfQOlKnCU/NL +upyEP0ZAFgtKmn2ztzcLTamCnqfF5LwkM0AiqXpavIF9EgRFuJ/D80PFRmPtxKxl +PhBvgfD/1U1dWI2XO97Gp3a70RAdJhDkAKIQQesPFHFezb9ExT7BcLJCkmXy21oY +4Xf+nudfj0ts50yyZcmmFF0Yig0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAA +0xxj8WjL8rOM2Q/eHCTXZfAZBBTUFNsDrKCjuVUc8R7OCelOd4kjIarS/TU34+kB +UH9oVKv485pSiDJGO38YFPLxZKTvBOCO81LjcvE4oZ9G56yu1wfPGp+quiLBfCZy +j0UmdlxHmqrsSB2Ja2LKdCjThO57UROFfNP8f+gF70JpEPPsuOiUnGZGOCI04BEg +FPhpbqQ/p4F1LuGGmJIxpC0+Cl2umAIPNgTnINvTknhfgoOnM0jars3xIWENXwTQ +ojOPxbXp84+VEtKIYDzqqV+U41nsJWkIz80DucLBkgj9aQ4ysnpIvhoLFQ80jkTv +Sa0s+fLQFndMSrWEYke9Pa3012ARJea1H9oOZsrV3Krxd9Ep9vGbuLZ+bR04uIVG +LkMeg7XH6vguU9+QuitmIpb+r+D9XFcALK10OcAgTiavTzQntvdMKsO4xD0vkzWP +Yju2haP9i8/vKtGXn227OVnuHy9YK3lNLPD2hz8B4Frl9jgXHrJUQ6N2NFFsL+eE +jrCxtCGm0OGaeQRnUkrAEknr1gfGVgFs5OD6D8plHsUJTAkiPyw/xb8/LsFm1rj4 +oV0iVKFlVll1bNQL6NIa7EL4fXzro9y0wKNTIqbOz5QBEMGqCDme+Y7llhSd336W +33NMdEdMWgUkez0EaGoEoTvvSViAB5655HpRac039Q== +-----END CERTIFICATE REQUEST----- diff --git a/apollo-router/src/services/testdata/server.ext b/apollo-router/src/services/testdata/server.ext new file mode 100644 index 0000000000..c3cafab58d --- /dev/null +++ b/apollo-router/src/services/testdata/server.ext @@ -0,0 +1,6 @@ +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +#basicConstraints = CA:TRUE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign +subjectAltName = DNS:localhost +issuerAltName = issuer:copy diff --git a/apollo-router/src/services/testdata/server.key b/apollo-router/src/services/testdata/server.key new file mode 100644 index 0000000000..c24c5bd9bb --- /dev/null +++ b/apollo-router/src/services/testdata/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +4dWw/oAnLAPKVv38o9bOcPHDGSLLAhU= +-----END PRIVATE KEY----- diff --git a/apollo-router/src/services/testdata/server_self_signed.crt b/apollo-router/src/services/testdata/server_self_signed.crt new file mode 100644 index 0000000000..cd5d3b9f3c --- /dev/null +++ b/apollo-router/src/services/testdata/server_self_signed.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmTCCA4GgAwIBAgIUDJn4No8XZo+1xUEEMqpAqRi1xmUwDQYJKoZIhvcNAQEL +BQAwJjELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFwaFFMMB4XDTIz +MDkwODE1MjM1M1oXDTIzMTAwODE1MjM1M1owJjELMAkGA1UEBhMCRlIxFzAVBgNV +BAoMDkFwb2xsbyBHcmFwaFFMMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAsJ8bwoJJ8OLL0U5lrhYUeWpYej+eS5yibX02yWXCpzEgmailiugChwWwJrPE +laarKQUYPTAlhgmFj342TWjTu8fYVAJO3AoHchJtkaQnyJSipagOB+nZQ936T0E6 +k7wdt5BfLT5ow2Enxov88YxdFdfP9HlYXShyXTLASCnyhgxMS8OjHO2koPQ270gV +5NLVlg9+v+ZLYldDVPyLzd4TTlgW0PStedHFaw2i5PbLYzasF+cIb6Ld0BrknPfN +aiOo3GGGs2hOGnTABTu87Y5bW3x+11G18p3wVwhAgHPqgbjQnZoGMq1VfLJjJcb2 +LTPmtGMWGBfUOIFmGWARvnhY/TxjulRrWnLc+lAfj7zcoipZMkr2+JmTq6qoE9KP +xdsYEuCS2aN04KK3hyppdOMIuhyFBHs3sWd8l5kdo1qUmunawkQrBoG2QJZgJbiO +FC/ESp5Z3cGVxrIRPZ4olmP0/S5B8DkPyLcxjwJeinQU294o+5caJb/G53umVDG9 +TZb5BF+SWfQOlKnCU/NLupyEP0ZAFgtKmn2ztzcLTamCnqfF5LwkM0AiqXpavIF9 +EgRFuJ/D80PFRmPtxKxlPhBvgfD/1U1dWI2XO97Gp3a70RAdJhDkAKIQQesPFHFe +zb9ExT7BcLJCkmXy21oY4Xf+nudfj0ts50yyZcmmFF0Yig0CAwEAAaOBvjCBuzAd +BgNVHQ4EFgQUgv2yxjcZxbXqJBASiv1MdiesIo4wYQYDVR0jBFowWIAUgv2yxjcZ +xbXqJBASiv1MdiesIo6hKqQoMCYxCzAJBgNVBAYTAkZSMRcwFQYDVQQKDA5BcG9s +bG8gR3JhcGhRTIIUDJn4No8XZo+1xUEEMqpAqRi1xmUwCwYDVR0PBAQDAgL8MBQG +A1UdEQQNMAuCCWxvY2FsaG9zdDAUBgNVHRIEDTALgglsb2NhbGhvc3QwDQYJKoZI +hvcNAQELBQADggIBACjN3nhmJD2k6i3vSDAmfLtdzuSIbRSHPUXMyv4kRS1XaRw2 +QyRhnZN+7LDoGyzI8IkSNs14xeUtixw5Nl0kFiq9cEuTvptAUNbPi4n/UuI3tSDf +8VX/0WCoJonD5QlnQ9RPBGqbVmyzn1nDIdLyKsbUpK4igdDkj+DeLva8bO41/MMk +e4PONDwHO4dyA00OlzyYazxK72tSDAEUomgDNvYtaNAi7uxXWnAYPkuXX77JgGxS +hTBdMXPIUhcUCLotL5sIKj4UMk1BWUom9egL21W+M6A97M9+uCr72nktM29E8IGc +euuqgq5bgALRthgaecsUPu/MlnUumLK9v7+osFtIAl6fGYQMvcdX90jII0QWoAdk +uleVU8Ae18588e527clV5B+TnGjMucbxoPr4AbWR9slvdNVBB5UMHg67UsqF/Kyq +F1WK5Zr5RD5r3yEwEmVkzQops/TmNRfmm+rFqrOMwRxpb8eW0jR4qfGPcFKfoZu3 +8xif2zKl9w21xQZ6VFVpgir8QJf0uswsHdlMRABAfiBBReP4Pg+jFFbmijajdU/i +w46qc4whI5dbF+nAGdg3NsC8P+zeBTXQJEoF7E+M+eu35pOxi8xi46NKHsqfNNtG +hLT0oWvdWPRH009SPHcA1fTZ347pkOeGtz94IJjTPm+HI1sHWv7avHX3U4Pr +-----END CERTIFICATE----- diff --git a/apollo-router/src/services/testdata/server_self_signed.csr b/apollo-router/src/services/testdata/server_self_signed.csr new file mode 100644 index 0000000000..0e2dc2723f --- /dev/null +++ b/apollo-router/src/services/testdata/server_self_signed.csr @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEazCCAlMCAQAwJjELMAkGA1UEBhMCRlIxFzAVBgNVBAoMDkFwb2xsbyBHcmFw +aFFMMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsJ8bwoJJ8OLL0U5l +rhYUeWpYej+eS5yibX02yWXCpzEgmailiugChwWwJrPElaarKQUYPTAlhgmFj342 +TWjTu8fYVAJO3AoHchJtkaQnyJSipagOB+nZQ936T0E6k7wdt5BfLT5ow2Enxov8 +8YxdFdfP9HlYXShyXTLASCnyhgxMS8OjHO2koPQ270gV5NLVlg9+v+ZLYldDVPyL +zd4TTlgW0PStedHFaw2i5PbLYzasF+cIb6Ld0BrknPfNaiOo3GGGs2hOGnTABTu8 +7Y5bW3x+11G18p3wVwhAgHPqgbjQnZoGMq1VfLJjJcb2LTPmtGMWGBfUOIFmGWAR +vnhY/TxjulRrWnLc+lAfj7zcoipZMkr2+JmTq6qoE9KPxdsYEuCS2aN04KK3hypp +dOMIuhyFBHs3sWd8l5kdo1qUmunawkQrBoG2QJZgJbiOFC/ESp5Z3cGVxrIRPZ4o +lmP0/S5B8DkPyLcxjwJeinQU294o+5caJb/G53umVDG9TZb5BF+SWfQOlKnCU/NL +upyEP0ZAFgtKmn2ztzcLTamCnqfF5LwkM0AiqXpavIF9EgRFuJ/D80PFRmPtxKxl +PhBvgfD/1U1dWI2XO97Gp3a70RAdJhDkAKIQQesPFHFezb9ExT7BcLJCkmXy21oY +4Xf+nudfj0ts50yyZcmmFF0Yig0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAA +0xxj8WjL8rOM2Q/eHCTXZfAZBBTUFNsDrKCjuVUc8R7OCelOd4kjIarS/TU34+kB +UH9oVKv485pSiDJGO38YFPLxZKTvBOCO81LjcvE4oZ9G56yu1wfPGp+quiLBfCZy +j0UmdlxHmqrsSB2Ja2LKdCjThO57UROFfNP8f+gF70JpEPPsuOiUnGZGOCI04BEg +FPhpbqQ/p4F1LuGGmJIxpC0+Cl2umAIPNgTnINvTknhfgoOnM0jars3xIWENXwTQ +ojOPxbXp84+VEtKIYDzqqV+U41nsJWkIz80DucLBkgj9aQ4ysnpIvhoLFQ80jkTv +Sa0s+fLQFndMSrWEYke9Pa3012ARJea1H9oOZsrV3Krxd9Ep9vGbuLZ+bR04uIVG +LkMeg7XH6vguU9+QuitmIpb+r+D9XFcALK10OcAgTiavTzQntvdMKsO4xD0vkzWP +Yju2haP9i8/vKtGXn227OVnuHy9YK3lNLPD2hz8B4Frl9jgXHrJUQ6N2NFFsL+eE +jrCxtCGm0OGaeQRnUkrAEknr1gfGVgFs5OD6D8plHsUJTAkiPyw/xb8/LsFm1rj4 +oV0iVKFlVll1bNQL6NIa7EL4fXzro9y0wKNTIqbOz5QBEMGqCDme+Y7llhSd336W +33NMdEdMWgUkez0EaGoEoTvvSViAB5655HpRac039Q== +-----END CERTIFICATE REQUEST----- diff --git a/apollo-router/src/services/testdata/tls.md b/apollo-router/src/services/testdata/tls.md new file mode 100644 index 0000000000..c5d46d74bc --- /dev/null +++ b/apollo-router/src/services/testdata/tls.md @@ -0,0 +1,82 @@ +# Certificate generation + +## Server self signed certificate + +``` +openssl genrsa -out server.key 4096 +openssl req -new -key server.key -out server_self_signed.csr +openssl x509 -req -in server_self_signed.csr -signkey server.key -out server_self_signed.crt -extfile server.ext +``` + +## Root certificate authority + +``` +openssl genrsa -out server.key 4096 +openssl req -new -x509 -days 10000 -key ca.key -out ca.crt + +You are about to be asked to enter information that will be incorporated +into your certificate request. +What you are about to enter is what is called a Distinguished Name or a DN. +There are quite a few fields but you can leave some blank +For some fields there will be a default value, +If you enter '.', the field will be left blank. +----- +Country Name (2 letter code) [AU]:FR +State or Province Name (full name) [Some-State]:. +Locality Name (eg, city) []: +Organization Name (eg, company) [Internet Widgits Pty Ltd]:Apollo GraphQL +Organizational Unit Name (eg, section) []: +Common Name (e.g. server FQDN or YOUR name) []:Apollo Test CA +Email Address []: +``` + +## Server certificate + +``` +openssl genrsa -out server.key 4096 +openssl req -new -key server.key -out server.csr +openssl x509 -req -in server.csr -CA ./CA/ca.crt -CAkey ./CA/ca.key -out server.crt -CAcreateserial -days 10000 -sha256 -extfile server.ext +Certificate request self-signature ok +subject=C = FR, O = Apollo GraphQL, CN = router +``` + +## Client certificate + +Generate the key: + +``` +openssl genrsa -out client.key.pem 4096 +``` + +Certificate signing request: + +``` +openssl req -new -key client.key -out client.csr + +You are about to be asked to enter information that will be incorporated +into your certificate request. +What you are about to enter is what is called a Distinguished Name or a DN. +There are quite a few fields but you can leave some blank +For some fields there will be a default value, +If you enter '.', the field will be left blank. +----- +Country Name (2 letter code) [AU]:FR +State or Province Name (full name) [Some-State]:. +Locality Name (eg, city) []: +Organization Name (eg, company) [Internet Widgits Pty Ltd]:Apollo GraphQL +Organizational Unit Name (eg, section) []: +Common Name (e.g. server FQDN or YOUR name) []:router +Email Address []: + +Please enter the following 'extra' attributes +to be sent with your certificate request +A challenge password []: +An optional company name []: +``` + +Generate the certificate: +``` +openssl x509 -req -in client.csr -CA ./CA/ca.crt -CAkey ./CA/ca.key -out client.crt -CAcreateserial -days 10000 -sha256 -extfile client.ext +Certificate request self-signature ok +subject=C = FR, O = Apollo GraphQL, CN = router +``` diff --git a/docs/source/configuration/overview.mdx b/docs/source/configuration/overview.mdx index b68ecb664d..b96f4fcc2a 100644 --- a/docs/source/configuration/overview.mdx +++ b/docs/source/configuration/overview.mdx @@ -567,7 +567,7 @@ The router expects the file referenced in the `certificate_chain` value to be a The router verifies TLS connections to subgraphs using the list of certificate authorities the system provides. You can override this list with a combination of global and per-subgraph settings: -```yaml title="router.yaml" +```yaml tls: subgraph: # Use these certificate authorities unless overridden per-subgraph @@ -605,6 +605,26 @@ issuerAltName = issuer:copy > Make sure to change the `subjectAltName` field to the subgraph's name. This produces the file as `server.crt` which can be used in `certificate_authorities`. +#### TLS client authentication for subgraph requests + +The router support mutual TLS authentication (mTLS) with the subgraphs. This means that it can authenticate itself to the subgraph using a certificate chain and a cryptographic key. It can be configured as follows: + +```yaml +tls: + subgraph: + # Use these certificates and key unless overridden per-subgraph + all: + client_authentication: + certificate_chain: ${file./path/to/certificate_chain.pem} + key: ${file./path/to/key.pem} + # Override global setting for individual subgraphs + subgraphs: + products: + client_authentication: + certificate_chain: ${file./path/to/certificate_chain.pem} + key: ${file./path/to/key.pem} +``` + ### Request limits > **Request limits are currently in [preview](/resources/product-launch-stages#preview).**