Skip to content

Commit 7f887cc

Browse files
committed
refactor(supervisor-middleware): simplify service endpoints
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
1 parent 8d5b4b3 commit 7f887cc

9 files changed

Lines changed: 127 additions & 52 deletions

File tree

crates/openshell-server/src/config_file.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,7 @@ pub struct MiddlewareServiceFileConfig {
174174
/// Operator-facing name used for diagnostics.
175175
pub name: String,
176176
/// Plaintext gRPC endpoint reachable by the gateway and supervisors.
177-
pub endpoint: String,
178-
/// Required explicit opt-in to the local-development-only insecure mode.
179-
#[serde(default)]
180-
pub allow_insecure: bool,
177+
pub grpc_endpoint: String,
181178
/// Operator-owned body limit for every binding exposed by this service.
182179
pub max_body_bytes: u64,
183180
}
@@ -186,8 +183,7 @@ impl From<&MiddlewareServiceFileConfig> for SupervisorMiddlewareService {
186183
fn from(config: &MiddlewareServiceFileConfig) -> Self {
187184
Self {
188185
name: config.name.clone(),
189-
endpoint: config.endpoint.clone(),
190-
allow_insecure: config.allow_insecure,
186+
grpc_endpoint: config.grpc_endpoint.clone(),
191187
max_body_bytes: config.max_body_bytes,
192188
}
193189
}
@@ -439,8 +435,7 @@ allow_unauthenticated_users = true
439435
let toml = r#"
440436
[[openshell.gateway.middleware]]
441437
name = "local-guard"
442-
endpoint = "http://127.0.0.1:50051"
443-
allow_insecure = true
438+
grpc_endpoint = "http://127.0.0.1:50051"
444439
max_body_bytes = 262144
445440
"#;
446441
let tmp = write_tmp(toml);
@@ -449,8 +444,7 @@ max_body_bytes = 262144
449444
file.openshell.gateway.middleware,
450445
vec![MiddlewareServiceFileConfig {
451446
name: "local-guard".into(),
452-
endpoint: "http://127.0.0.1:50051".into(),
453-
allow_insecure: true,
447+
grpc_endpoint: "http://127.0.0.1:50051".into(),
454448
max_body_bytes: 262_144,
455449
}]
456450
);

crates/openshell-server/src/grpc/policy.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9331,8 +9331,7 @@ mod tests {
93319331
let settings = HashMap::new();
93329332
let service = openshell_core::proto::SupervisorMiddlewareService {
93339333
name: "local-guard".into(),
9334-
endpoint: "http://127.0.0.1:50051".into(),
9335-
allow_insecure: true,
9334+
grpc_endpoint: "http://127.0.0.1:50051".into(),
93369335
max_body_bytes: 1024,
93379336
};
93389337

crates/openshell-supervisor-middleware/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ miette = { workspace = true }
1717
prost-types = { workspace = true }
1818
regex = { workspace = true }
1919
tokio = { workspace = true }
20-
tonic = { workspace = true, features = ["channel", "server"] }
20+
tonic = { workspace = true, features = ["channel", "server", "tls-native-roots"] }
2121

2222
[dev-dependencies]
2323
tokio-stream = { workspace = true, features = ["net"] }

crates/openshell-supervisor-middleware/src/lib.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,11 @@ fn validate_registration(registration: &SupervisorMiddlewareService) -> Result<(
246246
"supervisor middleware registration name cannot be empty"
247247
));
248248
}
249-
if !registration.allow_insecure {
250-
return Err(miette!(
251-
"middleware registration '{}' must set allow_insecure = true; TLS is not supported in V1",
252-
registration.name
253-
));
254-
}
255-
if !registration.endpoint.starts_with("http://") {
249+
if !registration.grpc_endpoint.starts_with("http://")
250+
&& !registration.grpc_endpoint.starts_with("https://")
251+
{
256252
return Err(miette!(
257-
"middleware registration '{}' endpoint must use http:// in the local-development-only V1",
253+
"middleware registration '{}' grpc_endpoint must use http:// or https://",
258254
registration.name
259255
));
260256
}
@@ -365,7 +361,7 @@ impl MiddlewareRegistry {
365361
let service = Arc::new(
366362
remote::RemoteMiddlewareService::connect(
367363
&registration.name,
368-
&registration.endpoint,
364+
&registration.grpc_endpoint,
369365
)
370366
.await?,
371367
);
@@ -1129,8 +1125,7 @@ mod tests {
11291125
fn external_registration(max_body_bytes: u64) -> SupervisorMiddlewareService {
11301126
SupervisorMiddlewareService {
11311127
name: "local-guard-service".into(),
1132-
endpoint: "http://127.0.0.1:50051".into(),
1133-
allow_insecure: true,
1128+
grpc_endpoint: "http://127.0.0.1:50051".into(),
11341129
max_body_bytes,
11351130
}
11361131
}
@@ -1267,11 +1262,23 @@ mod tests {
12671262
}
12681263

12691264
#[test]
1270-
fn external_registration_requires_explicit_insecure_opt_in() {
1265+
fn external_registration_accepts_http_and_https_grpc_endpoints() {
1266+
for grpc_endpoint in [
1267+
"http://127.0.0.1:50051",
1268+
"https://middleware.example.com:443",
1269+
] {
1270+
let mut registration = external_registration(4096);
1271+
registration.grpc_endpoint = grpc_endpoint.into();
1272+
validate_registration(&registration).expect("supported gRPC endpoint scheme");
1273+
}
1274+
}
1275+
1276+
#[test]
1277+
fn external_registration_rejects_unsupported_grpc_endpoint_scheme() {
12711278
let mut registration = external_registration(4096);
1272-
registration.allow_insecure = false;
1273-
let error = validate_registration(&registration).expect_err("opt-in required");
1274-
assert!(error.to_string().contains("allow_insecure"));
1279+
registration.grpc_endpoint = "ftp://middleware.example.com".into();
1280+
let error = validate_registration(&registration).expect_err("unsupported scheme");
1281+
assert!(error.to_string().contains("http:// or https://"));
12751282
}
12761283

12771284
#[tokio::test]
@@ -1293,7 +1300,7 @@ mod tests {
12931300
let server_task = tokio::spawn(server);
12941301

12951302
let mut registration = external_registration(1024);
1296-
registration.endpoint = format!("http://{address}");
1303+
registration.grpc_endpoint = format!("http://{address}");
12971304
let registry = MiddlewareRegistry::connect_services(vec![registration.clone()])
12981305
.await
12991306
.expect("connect external middleware");

crates/openshell-supervisor-middleware/src/remote.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use openshell_core::proto::{
1010
HttpRequestEvaluation, HttpRequestResult, MiddlewareManifest, ValidateConfigRequest,
1111
ValidateConfigResponse,
1212
};
13-
use tonic::transport::{Channel, Endpoint};
13+
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
1414
use tonic::{Request, Response, Status};
1515

1616
const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
@@ -23,19 +23,30 @@ pub struct RemoteMiddlewareService {
2323
}
2424

2525
impl RemoteMiddlewareService {
26-
pub async fn connect(registration_name: &str, endpoint: &str) -> Result<Self> {
27-
let channel = Endpoint::from_shared(endpoint.to_string())
26+
pub async fn connect(registration_name: &str, grpc_endpoint: &str) -> Result<Self> {
27+
let mut endpoint = Endpoint::from_shared(grpc_endpoint.to_string())
2828
.into_diagnostic()
2929
.wrap_err_with(|| {
30-
format!("middleware registration '{registration_name}' has an invalid endpoint")
31-
})?
30+
format!(
31+
"middleware registration '{registration_name}' has an invalid grpc_endpoint"
32+
)
33+
})?;
34+
if grpc_endpoint.starts_with("https://") {
35+
endpoint = endpoint
36+
.tls_config(ClientTlsConfig::new().with_enabled_roots())
37+
.into_diagnostic()
38+
.wrap_err_with(|| {
39+
format!("middleware registration '{registration_name}' could not configure TLS")
40+
})?;
41+
}
42+
let channel = endpoint
3243
.connect_timeout(CONNECT_TIMEOUT)
3344
.connect()
3445
.await
3546
.into_diagnostic()
3647
.wrap_err_with(|| {
3748
format!(
38-
"middleware registration '{registration_name}' could not connect to {endpoint}"
49+
"middleware registration '{registration_name}' could not connect to {grpc_endpoint}"
3950
)
4051
})?;
4152
Ok(Self {

docs/extensibility/supervisor-middleware.mdx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,14 @@ Start an operator-run service before starting the gateway, then add a registrati
4141
```toml
4242
[[openshell.gateway.middleware]]
4343
name = "local-content-guard"
44-
endpoint = "http://host.openshell.internal:50051"
45-
allow_insecure = true
44+
grpc_endpoint = "http://host.openshell.internal:50051"
4645
max_body_bytes = 262144
4746
```
4847

4948
| Field | Description |
5049
| --- | --- |
5150
| `name` | Operator-facing registration name used in diagnostics. Policies do not reference this value. |
52-
| `endpoint` | Service address reachable from both the gateway and sandbox supervisors. |
53-
| `allow_insecure` | Required acknowledgement for the currently supported plaintext endpoint. |
51+
| `grpc_endpoint` | Service address reachable from both the gateway and sandbox supervisors. Supports plaintext `http://` and TLS `https://` with platform trust roots. |
5452
| `max_body_bytes` | Operator limit applied to every binding exposed by the service. |
5553

5654
The gateway connects to every registered service and verifies its capabilities before accepting traffic. Gateway startup fails when a service is unavailable, reports an invalid capability, or exposes a binding ID already owned by another service. Operator-run services cannot claim the reserved `openshell/` namespace.
@@ -135,7 +133,7 @@ See [Logging](/observability/logging) for log access and [OCSF JSON Export](/obs
135133
- The supported operation and phase are `HttpRequest/pre_credentials`.
136134
- Selection uses destination host include and exclude patterns.
137135
- Required middleware cannot cover `tls: skip` endpoints because OpenShell cannot inspect that traffic.
138-
- Operator-run services currently use explicitly enabled plaintext `http://` endpoints.
139-
- TLS, service authentication, health checks, and runtime registration are not available.
136+
- Operator-run services support plaintext `http://` and TLS `https://` endpoints. HTTPS certificates must chain to a CA in the platform trust store.
137+
- Custom trust roots, client authentication, health checks, and runtime registration are not available.
140138

141139
For a runnable operator workflow, see the [content guard example](https://github.com/NVIDIA/OpenShell/tree/main/examples/supervisor-middleware-content-guard).

docs/reference/gateway-config.mdx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,11 @@ guest_tls_key = "/etc/openshell/certs/client-key.pem"
103103
grpc_rate_limit_requests = 120
104104
grpc_rate_limit_window_seconds = 60
105105

106-
# Local-development-only external supervisor middleware. The endpoint must be
107-
# reachable from both the gateway and sandbox supervisors.
106+
# Operator-run supervisor middleware. The gRPC endpoint must be reachable from
107+
# both the gateway and sandbox supervisors.
108108
[[openshell.gateway.middleware]]
109109
name = "local-content-guard"
110-
endpoint = "http://host.openshell.internal:50051"
111-
allow_insecure = true
110+
grpc_endpoint = "http://host.openshell.internal:50051"
112111
max_body_bytes = 262144
113112

114113
# Gateway listener TLS (distinct from the per-driver guest_tls_*).
@@ -155,8 +154,7 @@ Register operator-run supervisor middleware services with one or more `[[openshe
155154
```toml
156155
[[openshell.gateway.middleware]]
157156
name = "local-content-guard"
158-
endpoint = "http://host.openshell.internal:50051"
159-
allow_insecure = true
157+
grpc_endpoint = "http://host.openshell.internal:50051"
160158
max_body_bytes = 262144
161159
```
162160

@@ -166,7 +164,7 @@ The gateway connects to every registered service and validates `Describe` before
166164

167165
`max_body_bytes` is the operator limit for every binding exposed by the service. It must be greater than zero and no larger than each binding's advertised limit. OpenShell rejects an oversized value instead of silently clamping it.
168166

169-
The service endpoint must use plaintext `http://`, and `allow_insecure = true` is required as an explicit acknowledgement that inspected request content is sent without transport encryption or peer authentication. TLS, authentication, health checks, and runtime registration are not supported. The endpoint must be reachable from both the gateway and sandbox supervisors; use `host.openshell.internal` or another shared address when both runtimes resolve it.
167+
The service `grpc_endpoint` currently supports plaintext `http://` and TLS `https://` using the platform trust store. Custom trust roots, client authentication, health checks, and runtime registration are not currently supported. The endpoint must be reachable from both the gateway and sandbox supervisors; use `host.openshell.internal` or another shared address that can be resolved in both places.
170168

171169
See [Supervisor Middleware](/extensibility/supervisor-middleware) for selection, failure, body-limit, and operational guidance.
172170

0 commit comments

Comments
 (0)