Skip to content

Commit f3985b7

Browse files
ardatankamilkisiela
authored andcommitted
feat(router): HMAC based Subgraph Auth
1 parent ca3d24a commit f3985b7

File tree

25 files changed

+568
-302
lines changed

25 files changed

+568
-302
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
default: minor
3+
---
4+
5+
# Breaking
6+
7+
Removed `pool_idle_timeout_seconds` from `traffic_shaping`, instead use `pool_idle_timeout` with duration format.
8+
9+
```diff
10+
traffic_shaping:
11+
- pool_idle_timeout_seconds: 30
12+
+ pool_idle_timeout: 30s
13+
```
14+
15+
#540 by @ardatan

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/router/src/pipeline/progressive_override.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
use std::collections::{BTreeMap, HashMap, HashSet};
22

33
use hive_router_config::override_labels::{LabelOverrideValue, OverrideLabelsConfig};
4-
use hive_router_plan_executor::execution::client_request_details::ClientRequestDetails;
4+
use hive_router_plan_executor::{
5+
execution::client_request_details::ClientRequestDetails, utils::expression::compile_expression,
6+
};
57
use hive_router_query_planner::{
68
graph::{PlannerOverrideContext, PERCENTAGE_SCALE_FACTOR},
79
state::supergraph_state::SupergraphState,
810
};
911
use rand::Rng;
1012
use vrl::{
11-
compiler::{compile as vrl_compile, Program as VrlProgram, TargetValue as VrlTargetValue},
13+
compiler::Program as VrlProgram,
14+
compiler::TargetValue as VrlTargetValue,
1215
core::Value as VrlValue,
1316
prelude::{
1417
state::RuntimeState as VrlState, Context as VrlContext, ExpressionError,
1518
TimeZone as VrlTimeZone,
1619
},
17-
stdlib::all as vrl_build_functions,
1820
value::Secrets as VrlSecrets,
1921
};
2022

@@ -126,27 +128,20 @@ impl OverrideLabelsEvaluator {
126128
) -> Result<Self, OverrideLabelsCompileError> {
127129
let mut static_enabled_labels = HashSet::new();
128130
let mut expressions = HashMap::new();
129-
let vrl_functions = vrl_build_functions();
130131

131132
for (label, value) in override_labels_config.iter() {
132133
match value {
133134
LabelOverrideValue::Boolean(true) => {
134135
static_enabled_labels.insert(label.clone());
135136
}
136137
LabelOverrideValue::Expression { expression } => {
137-
let compilation_result =
138-
vrl_compile(expression, &vrl_functions).map_err(|diagnostics| {
139-
OverrideLabelsCompileError {
140-
label: label.clone(),
141-
error: diagnostics
142-
.errors()
143-
.into_iter()
144-
.map(|d| d.code.to_string() + ": " + &d.message)
145-
.collect::<Vec<_>>()
146-
.join(", "),
147-
}
148-
})?;
149-
expressions.insert(label.clone(), compilation_result.program);
138+
let program = compile_expression(expression, None).map_err(|err| {
139+
OverrideLabelsCompileError {
140+
label: label.clone(),
141+
error: err.to_string(),
142+
}
143+
})?;
144+
expressions.insert(label.clone(), program);
150145
}
151146
_ => {} // Skip false booleans
152147
}

docs/README.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
|[**csrf**](#csrf)|`object`|Configuration for CSRF prevention.<br/>Default: `{"enabled":false,"required_headers":[]}`<br/>||
99
|[**graphiql**](#graphiql)|`object`|Configuration for the GraphiQL interface.<br/>Default: `{"enabled":true}`<br/>||
1010
|[**headers**](#headers)|`object`|Configuration for the headers.<br/>Default: `{}`<br/>||
11+
|[**hmac\_signature**](#hmac_signature)|`object`||yes|
1112
|[**http**](#http)|`object`|Configuration for the HTTP server/listener.<br/>Default: `{"host":"0.0.0.0","port":4000}`<br/>||
1213
|[**jwt**](#jwt)|`object`|Configuration for JWT authentication plugin.<br/>|yes|
1314
|[**log**](#log)|`object`|The router logger configuration.<br/>Default: `{"filter":null,"format":"json","level":"info"}`<br/>||
1415
|[**override\_labels**](#override_labels)|`object`|Configuration for overriding labels.<br/>||
1516
|[**override\_subgraph\_urls**](#override_subgraph_urls)|`object`|Configuration for overriding subgraph URLs.<br/>Default: `{}`<br/>||
1617
|[**query\_planner**](#query_planner)|`object`|Query planning configuration.<br/>Default: `{"allow_expose":false,"timeout":"10s"}`<br/>||
1718
|[**supergraph**](#supergraph)|`object`|Configuration for the Federation supergraph source. By default, the router will use a local file-based supergraph source (`./supergraph.graphql`).<br/>||
18-
|[**traffic\_shaping**](#traffic_shaping)|`object`|Configuration for the traffic-shaper executor. Use these configurations to control how requests are being executed to subgraphs.<br/>Default: `{"dedupe_enabled":true,"max_connections_per_host":100,"pool_idle_timeout_seconds":50}`<br/>||
19+
|[**traffic\_shaping**](#traffic_shaping)|`object`|Configuration for the traffic-shaping of the executor. Use these configurations to control how requests are being executed to subgraphs.<br/>Default: `{"dedupe_enabled":true,"max_connections_per_host":100,"pool_idle_timeout":"50s"}`<br/>||
1920

2021
**Additional Properties:** not allowed
2122
**Example**
@@ -57,6 +58,9 @@ headers:
5758
default: unknown
5859
named: x-tenant-id
5960
rename: x-acct-tenant
61+
hmac_signature:
62+
enabled: false
63+
extension_name: hmac-signature
6064
http:
6165
host: 0.0.0.0
6266
port: 4000
@@ -109,7 +113,7 @@ supergraph: {}
109113
traffic_shaping:
110114
dedupe_enabled: true
111115
max_connections_per_host: 100
112-
pool_idle_timeout_seconds: 50
116+
pool_idle_timeout: 50s
113117

114118
```
115119

@@ -1341,6 +1345,25 @@ For more information on the available functions and syntax, see the
13411345
|**expression**|`string`||yes|
13421346

13431347

1348+
<a name="hmac_signature"></a>
1349+
## hmac\_signature: object
1350+
1351+
**Properties**
1352+
1353+
|Name|Type|Description|Required|
1354+
|----|----|-----------|--------|
1355+
|**enabled**||Default: `false`<br/>|no|
1356+
|**extension\_name**|`string`|Default: `"hmac-signature"`<br/>|no|
1357+
|**secret**|`string`||yes|
1358+
1359+
**Example**
1360+
1361+
```yaml
1362+
enabled: false
1363+
extension_name: hmac-signature
1364+
1365+
```
1366+
13441367
<a name="http"></a>
13451368
## http: object
13461369

@@ -1808,7 +1831,7 @@ Request timeout for the Hive Console CDN requests.
18081831
<a name="traffic_shaping"></a>
18091832
## traffic\_shaping: object
18101833

1811-
Configuration for the traffic-shaper executor. Use these configurations to control how requests are being executed to subgraphs.
1834+
Configuration for the traffic-shaping of the executor. Use these configurations to control how requests are being executed to subgraphs.
18121835

18131836

18141837
**Properties**
@@ -1817,15 +1840,15 @@ Configuration for the traffic-shaper executor. Use these configurations to contr
18171840
|----|----|-----------|--------|
18181841
|**dedupe\_enabled**|`boolean`|Enables/disables request deduplication to subgraphs.<br/><br/>When requests exactly matches the hashing mechanism (e.g., subgraph name, URL, headers, query, variables), and are executed at the same time, they will<br/>be deduplicated by sharing the response of other in-flight requests.<br/>Default: `true`<br/>||
18191842
|**max\_connections\_per\_host**|`integer`|Limits the concurrent amount of requests/connections per host/subgraph.<br/>Default: `100`<br/>Format: `"uint"`<br/>Minimum: `0`<br/>||
1820-
|**pool\_idle\_timeout\_seconds**|`integer`|Timeout for idle sockets being kept-alive.<br/>Default: `50`<br/>Format: `"uint64"`<br/>Minimum: `0`<br/>||
1843+
|**pool\_idle\_timeout**|`string`|Timeout for idle sockets being kept-alive.<br/>Default: `"50s"`<br/>||
18211844

18221845
**Additional Properties:** not allowed
18231846
**Example**
18241847

18251848
```yaml
18261849
dedupe_enabled: true
18271850
max_connections_per_host: 100
1828-
pool_idle_timeout_seconds: 50
1851+
pool_idle_timeout: 50s
18291852
18301853
```
18311854

e2e/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ subgraphs = { path = "../bench/subgraphs" }
2424

2525
mockito = "1.7.0"
2626
tempfile = "3.23.0"
27+
28+
hmac = "0.12.1"
29+
sha2 = "0.10.9"
30+
hex = "0.4.3"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
hmac_signature:
2+
enabled: true
3+
secret: VERY_SECRET

e2e/src/hmac.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#[cfg(test)]
2+
mod hmac_e2e_tests {
3+
use crate::testkit::{
4+
init_graphql_request, init_router_from_config_file, wait_for_readiness, SubgraphsServer,
5+
};
6+
7+
use ntex::web::test;
8+
use sonic_rs::JsonValueTrait;
9+
10+
fn create_hmac_signature(secret: &str, query: &str) -> String {
11+
use hex;
12+
use hmac::{Hmac, Mac};
13+
use sha2::Sha256;
14+
15+
type HmacSha256 = Hmac<Sha256>;
16+
17+
let mut mac =
18+
HmacSha256::new_from_slice(secret.as_bytes()).expect("HMAC can take key of any size");
19+
let message = format!("{{\"query\":\"{}\"}}", query);
20+
mac.update(message.as_bytes());
21+
let result = mac.finalize();
22+
let code_bytes = result.into_bytes();
23+
hex::encode(code_bytes)
24+
}
25+
26+
#[ntex::test]
27+
async fn should_forward_hmac_signature_to_subgraph_via_extensions() {
28+
let subgraphs_server = SubgraphsServer::start().await;
29+
let app = init_router_from_config_file("configs/hmac_forward.router.yaml")
30+
.await
31+
.unwrap();
32+
wait_for_readiness(&app.app).await;
33+
let query = "query{users{id}}";
34+
let req = init_graphql_request(query, None);
35+
let resp: ntex::web::WebResponse = test::call_service(&app.app, req.to_request()).await;
36+
37+
assert!(resp.status().is_success(), "Expected 200 OK");
38+
39+
let subgraph_requests = subgraphs_server
40+
.get_subgraph_requests_log("accounts")
41+
.await
42+
.expect("expected requests sent to accounts subgraph");
43+
assert_eq!(
44+
subgraph_requests.len(),
45+
1,
46+
"expected 1 request to accounts subgraph"
47+
);
48+
let extensions = subgraph_requests[0].request_body.get("extensions").unwrap();
49+
50+
let expected_signature = create_hmac_signature("VERY_SECRET", query);
51+
assert_eq!(
52+
extensions.get("hmac-signature").unwrap(),
53+
&expected_signature
54+
);
55+
}
56+
}

e2e/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ mod file_supergraph;
33
#[cfg(test)]
44
mod hive_cdn_supergraph;
55
#[cfg(test)]
6+
mod hmac;
7+
#[cfg(test)]
68
mod jwt;
79
#[cfg(test)]
810
mod override_subgraph_urls;

lib/executor/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ itoa = "1.0.15"
4949
ryu = "1.0.20"
5050
indexmap = "2.10.0"
5151
bumpalo = "3.19.0"
52+
once_cell = "1.21.3"
53+
hmac = "0.12.1"
54+
sha2 = "0.10.9"
55+
hex = "0.4.3"
5256

5357
[dev-dependencies]
5458
subgraphs = { path = "../../bench/subgraphs" }

0 commit comments

Comments
 (0)