Skip to content

Commit 9c2912c

Browse files
authored
feat(auth): add grpc proxy (#1155)
* feat(auth): add build configuration and protobuf definitions * feat: add gRPC dependencies to workspace - Add tokio-stream for async streaming support - Add tonic and tonic-build for gRPC framework * feat(auth): add gRPC dependencies and build configuration - Add tonic dependencies for gRPC transport - Add tokio-stream for streaming support - Add tonic-build as build dependency for code generation - Enable HTTP/2 support in axum * feat(auth): add gRPC proxy test module - Add grpc_proxy_tests module for testing gRPC proxy functionality * docs: add gRPC proxy enablement plan - Comprehensive plan for extending auth proxy to support gRPC - Includes objectives, scope, deliverables, and testing strategy - Documents work plan and review checklist * chore: remove temporary PR review comments file - Clean up temporary file containing PR review data * feat: implement OAuth core with JWT assertion verification - Add ServiceOAuthPolicy for per-service OAuth configuration - Implement JWT assertion verifier with signature validation - Add replay protection for JWT tokens - Include rate limiting functionality for OAuth endpoints * feat: add gRPC proxy support with streaming and header sanitization - Implement unified proxy handler for HTTP and gRPC requests - Add gRPC detection based on content-type and HTTP/2 headers - Support unary and streaming gRPC forwarding - Add transport-aware header sanitization for both protocols - Implement binary metadata validation with size limits * test: add comprehensive gRPC proxy integration tests - Add tests for unary and streaming gRPC proxy forwarding - Test security validations for headers and binary metadata - Verify authentication requirements and header injection - Include negative test cases for security violations - Test content-type validation and HTTP/2 requirements * docs: add gRPC proxy implementation plan and testing guide - Document gRPC proxy enablement objectives and scope - Include work plan with implementation steps - Add testing strategy and verification criteria - Provide developer documentation for configuration and usage - Include review checklist for quality assurance * feat: separate HTTP/1.1 and HTTP/2 clients for REST and gRPC support - Add HTTP2Client type alias for gRPC requests - Refactor AuthenticatedProxy to use separate http_client and http2_client - Configure HTTP/1.1 client for REST requests with performance optimizations - Configure HTTP/2 client for gRPC requests with adaptive window control - Update proxy handlers to use appropriate client based on request type - Fix duplicate grpc- header check in is_proxy_injected_header_allowed - Improve header validation logic in apply_additional_headers * docs: remove grpc-proxy-plan.md * fix: remove trailing whitespace in proxy.rs * docs: fix formatting in load_abi macro documentation * lint: fix clippy
1 parent 1684be7 commit 9c2912c

File tree

12 files changed

+1066
-113
lines changed

12 files changed

+1066
-113
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ futures-core = { version = "0.3.31", default-features = false }
158158
tokio = { version = "^1", default-features = false }
159159
tokio-util = { version = "^0.7", default-features = false }
160160
tokio-cron-scheduler = { version = "0.13.0", default-features = false }
161+
tokio-stream = { version = "0.1.15", default-features = false, features = ["net"] }
161162
pin-project-lite = "0.2.7"
162163
tower = { version = "0.5.2", default-features = false }
163164
tower-http = { version = "0.6", default-features = false }

crates/auth/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,18 @@ serde_json = { workspace = true, features = ["alloc"] }
5050
bytes = { workspace = true }
5151
tower = { workspace = true, features = ["util", "timeout", "limit", "load-shed", "steer", "filter", "make"] }
5252
tokio = { workspace = true, features = ["full"] }
53-
axum = { workspace = true, features = ["json", "multipart", "http1", "tokio"] }
53+
axum = { workspace = true, features = ["json", "multipart", "http1", "http2", "tokio"] }
5454
reqwest = { workspace = true, features = ["json", "multipart"] }
5555
futures-util = { workspace = true }
5656
http = { workspace = true }
5757
tempfile = { workspace = true }
5858
tracing-subscriber = { workspace = true, features = ["fmt", "env-filter", "registry"] }
5959
openssl = { version = "0.10", features = ["vendored"] }
60+
tokio-stream = { workspace = true }
61+
tonic = { workspace = true, features = ["codegen", "prost", "transport"] }
62+
63+
[build-dependencies]
64+
tonic-build = { workspace = true, features = ["prost"] }
6065

6166
[features]
6267
default = ["std", "tracing"]

crates/auth/build.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
// Generate test gRPC service definitions for integration tests.
3+
tonic_build::configure()
4+
.build_client(true)
5+
.build_server(true)
6+
.compile_protos(&["proto/grpc_proxy_test.proto"], &["proto"])
7+
.expect("failed to build gRPC test protos");
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
syntax = "proto3";
2+
3+
package blueprint.auth.grpcproxytest;
4+
5+
service EchoService {
6+
rpc Echo(EchoRequest) returns (EchoResponse);
7+
rpc EchoStream(stream EchoRequest) returns (stream EchoResponse);
8+
}
9+
10+
message EchoRequest {
11+
string message = 1;
12+
}
13+
14+
message EchoResponse {
15+
string message = 1;
16+
}

crates/auth/src/oauth/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ pub fn rate_limit_check(
2121
.get(axum::http::header::FORWARDED)
2222
.and_then(|h| h.to_str().ok())
2323
.unwrap_or("unknown");
24-
let key = format!("rl:{}:{}", service_id.to_string(), ip);
24+
let key = format!("rl:{service_id}:{ip}");
2525

2626
let cf = db
2727
.cf_handle(cf::OAUTH_RL_CF)
2828
.ok_or_else(|| "rl store unavailable".to_string())?;
2929

30-
let bucket_key = format!("{}:{}", key, window);
30+
let bucket_key = format!("{key}:{window}");
3131

3232
let current = db
3333
.get_cf(&cf, bucket_key.as_bytes())
@@ -143,7 +143,7 @@ impl<'a> AssertionVerifier<'a> {
143143

144144
// Decode header to inspect alg/kid
145145
let header = jsonwebtoken::decode_header(jwt)
146-
.map_err(|e| VerificationError::InvalidRequest(format!("invalid jwt header: {}", e)))?;
146+
.map_err(|e| VerificationError::InvalidRequest(format!("invalid jwt header: {e}")))?;
147147
let alg = header.alg;
148148
if !matches!(
149149
alg,

0 commit comments

Comments
 (0)