feat: add runtime trust capability auth

This commit is contained in:
2026-07-26 20:45:52 +09:00
parent 006762f900
commit d994268a6b
12 changed files with 1235 additions and 33 deletions
+71 -6
View File
@@ -13,6 +13,7 @@ use std::{
sync::{Arc, RwLock},
time::Duration,
};
use worker_runtime::auth::{CapabilityTokenSigner, capability_claims};
use worker_runtime::catalog::{
ConfigBundleRef, CreateWorkerRequest, ProfileSelector, ProfileSourceArchiveHttpRef,
ProfileSourceArchiveSource, WorkerDetail as EmbeddedWorkerDetail,
@@ -2015,11 +2016,18 @@ pub struct RemoteRuntimeConfig {
pub display_name: String,
pub base_url: String,
pub bearer_token: Option<String>,
pub auth: Option<RemoteRuntimeAuthConfig>,
pub cached_capabilities: RuntimeCapabilitySummary,
pub cached_status: String,
pub timeout: Duration,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RemoteRuntimeAuthConfig {
pub server_id: String,
pub server_private_key: String,
}
impl std::fmt::Debug for RemoteRuntimeConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RemoteRuntimeConfig")
@@ -2030,6 +2038,7 @@ impl std::fmt::Debug for RemoteRuntimeConfig {
"bearer_token",
&self.bearer_token.as_ref().map(|_| "<redacted>"),
)
.field("auth", &self.auth.as_ref().map(|_| "<capability-signer>"))
.field("cached_capabilities", &self.cached_capabilities)
.field("cached_status", &self.cached_status)
.field("timeout", &self.timeout)
@@ -2049,6 +2058,7 @@ impl RemoteRuntimeConfig {
display_name: display_name.into(),
base_url: base_url.into(),
bearer_token,
auth: None,
cached_capabilities: remote_runtime_capabilities(
200, false, false, "unknown", "unknown",
),
@@ -2062,6 +2072,11 @@ impl RemoteRuntimeConfig {
self
}
pub fn with_auth(mut self, auth: RemoteRuntimeAuthConfig) -> Self {
self.auth = Some(auth);
self
}
pub fn with_cached_status(mut self, status: impl Into<String>) -> Self {
self.cached_status = status.into();
self
@@ -2081,6 +2096,7 @@ pub struct RemoteWorkerRuntime {
backend_base_url: String,
workspace_id: String,
bearer_token: Option<String>,
auth: Option<RemoteRuntimeAuthConfig>,
cached_capabilities: RuntimeCapabilitySummary,
cached_status: String,
host_id: String,
@@ -2088,6 +2104,21 @@ pub struct RemoteWorkerRuntime {
http: BlockingHttpClient,
}
fn all_remote_runtime_permissions() -> Vec<String> {
[
"workers:list",
"workers:create",
"workers:read",
"workers:delete",
"workers:input",
"workers:stop",
"workers:protocol",
]
.into_iter()
.map(str::to_string)
.collect()
}
impl RemoteWorkerRuntime {
pub fn new(
config: RemoteRuntimeConfig,
@@ -2112,6 +2143,7 @@ impl RemoteWorkerRuntime {
backend_base_url: backend_base_url.trim_end_matches('/').to_string(),
workspace_id,
bearer_token: config.bearer_token,
auth: config.auth,
cached_capabilities: config.cached_capabilities,
cached_status: config.cached_status,
resource_broker: BackendResourceBroker::default(),
@@ -2150,7 +2182,7 @@ impl RemoteWorkerRuntime {
where
T: DeserializeOwned + Send + 'static,
{
self.send_json(self.http.get(self.endpoint(path)))
self.send_json(path, self.http.get(self.endpoint(path)))
}
fn post_json<B, T>(&self, path: &str, body: &B) -> Result<T, RuntimeDiagnostic>
@@ -2158,25 +2190,56 @@ impl RemoteWorkerRuntime {
B: Serialize + ?Sized,
T: DeserializeOwned + Send + 'static,
{
self.send_json(self.http.post(self.endpoint(path)).json(body))
self.send_json(path, self.http.post(self.endpoint(path)).json(body))
}
fn delete_json<T>(&self, path: &str) -> Result<T, RuntimeDiagnostic>
where
T: DeserializeOwned + Send + 'static,
{
self.send_json(self.http.delete(self.endpoint(path)))
self.send_json(path, self.http.delete(self.endpoint(path)))
}
fn send_json<T>(&self, request: RequestBuilder) -> Result<T, RuntimeDiagnostic>
fn runtime_capability_token(&self, path: &str) -> Option<String> {
let auth = self.auth.as_ref()?;
let signer = CapabilityTokenSigner::new(&auth.server_id, &auth.server_private_key);
let claims = capability_claims(
&auth.server_id,
&self.runtime_id,
&self.workspace_id,
all_remote_runtime_permissions(),
300,
)
.map_err(|error| {
eprintln!(
"failed to build Runtime capability claims for {} {}: {error}",
self.runtime_id, path
);
error
})
.ok()?;
signer
.sign(&claims)
.map_err(|error| {
eprintln!(
"failed to sign Runtime capability token for {} {}: {error}",
self.runtime_id, path
);
error
})
.ok()
}
fn send_json<T>(&self, path: &str, request: RequestBuilder) -> Result<T, RuntimeDiagnostic>
where
T: DeserializeOwned + Send + 'static,
{
let runtime_id = self.runtime_id.clone();
let bearer_token = self.bearer_token.clone();
let capability_token = self.runtime_capability_token(path);
run_blocking_http(move || {
let request = request.header(CONTENT_TYPE, "application/json");
let request = if let Some(token) = bearer_token.as_deref() {
let request = if let Some(token) = capability_token.as_deref().or(bearer_token.as_deref()) {
request.header(AUTHORIZATION, format!("Bearer {token}"))
} else {
request
@@ -2653,7 +2716,9 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
runtime_id: self.runtime_id.clone(),
worker_id: worker_id.to_string(),
endpoint: self.ws_endpoint(worker_id),
bearer_token: self.bearer_token.clone(),
bearer_token: self
.runtime_capability_token(&format!("/v1/workers/{worker_id}/protocol"))
.or_else(|| self.bearer_token.clone()),
},
))
}