fix: enforce repository access and cache boundaries
This commit is contained in:
@@ -25,8 +25,9 @@ use worker_runtime::auth::{CapabilityTokenSigner, capability_claims};
|
||||
use worker_runtime::catalog::{
|
||||
ConfigBundleRef, CreateWorkerRequest, ProfileSelector, ProfileSourceArchiveHttpRef,
|
||||
ProfileSourceArchiveSource, WorkerDetail as EmbeddedWorkerDetail,
|
||||
WorkerStatus as EmbeddedWorkerStatus, WorkingDirectoryClaim, WorkingDirectoryRequest,
|
||||
WorkingDirectoryStatus, WorkingDirectorySummary, WorkspaceApiRef,
|
||||
WorkerStatus as EmbeddedWorkerStatus, WorkingDirectoryClaim,
|
||||
WorkingDirectoryRepositoryAccessRequest, WorkingDirectoryRequest, WorkingDirectoryStatus,
|
||||
WorkingDirectorySummary, WorkspaceApiRef,
|
||||
};
|
||||
use worker_runtime::config_bundle::{ConfigBundle, ConfigBundleAvailability, ConfigBundleSummary};
|
||||
#[cfg(test)]
|
||||
@@ -39,11 +40,11 @@ use worker_runtime::execution::WorkerExecutionRunState;
|
||||
use worker_runtime::fs_store::FsRuntimeStoreOptions;
|
||||
use worker_runtime::http_server::{
|
||||
RuntimeHttpConfigBundleAvailabilityResponse, RuntimeHttpConfigBundleSyncRequest,
|
||||
RuntimeHttpErrorResponse, RuntimeHttpSummaryResponse, RuntimeHttpWorkerCompletionsRequest,
|
||||
RuntimeHttpWorkerCompletionsResponse, RuntimeHttpWorkerDeleteResponse,
|
||||
RuntimeHttpWorkerInputResponse, RuntimeHttpWorkerLifecycleRequest,
|
||||
RuntimeHttpWorkerLifecycleResponse, RuntimeHttpWorkerResponse,
|
||||
RuntimeHttpWorkerWorkspaceApiRequest, RuntimeHttpWorkersResponse,
|
||||
RuntimeHttpErrorResponse, RuntimeHttpRepositoryAccessResponse, RuntimeHttpSummaryResponse,
|
||||
RuntimeHttpWorkerCompletionsRequest, RuntimeHttpWorkerCompletionsResponse,
|
||||
RuntimeHttpWorkerDeleteResponse, RuntimeHttpWorkerInputResponse,
|
||||
RuntimeHttpWorkerLifecycleRequest, RuntimeHttpWorkerLifecycleResponse,
|
||||
RuntimeHttpWorkerResponse, RuntimeHttpWorkerWorkspaceApiRequest, RuntimeHttpWorkersResponse,
|
||||
RuntimeHttpWorkingDirectoriesResponse, RuntimeHttpWorkingDirectoryResponse,
|
||||
RuntimeHttpWorkspacePromptProjectionRequest, RuntimeHttpWorkspacePromptProjectionResponse,
|
||||
};
|
||||
@@ -854,6 +855,16 @@ pub trait WorkspaceWorkerRuntime: Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
fn authorize_working_directory_repository_access(
|
||||
&self,
|
||||
_request: WorkingDirectoryRepositoryAccessRequest,
|
||||
) -> std::result::Result<(), Error> {
|
||||
Err(Error::InvalidInput(
|
||||
"Runtime does not support working directory Repository access authorization"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn list_working_directories(&self) -> RuntimeList<WorkingDirectoryStatus> {
|
||||
RuntimeList::new(Vec::new(), Vec::new())
|
||||
}
|
||||
@@ -1427,6 +1438,23 @@ impl RuntimeRegistry {
|
||||
Ok(runtime.create_working_directory(request))
|
||||
}
|
||||
|
||||
pub fn authorize_working_directory_repository_access(
|
||||
&self,
|
||||
runtime_id: &str,
|
||||
request: WorkingDirectoryRepositoryAccessRequest,
|
||||
) -> Result<(), RuntimeRegistryError> {
|
||||
validate_backend_identifier("runtime_id", runtime_id)?;
|
||||
validate_backend_identifier("working_directory_id", &request.working_directory_id)?;
|
||||
let runtime = self.runtime(runtime_id)?;
|
||||
runtime
|
||||
.authorize_working_directory_repository_access(request)
|
||||
.map_err(|error| RuntimeRegistryError::RuntimeOperationFailed {
|
||||
runtime_id: runtime_id.to_string(),
|
||||
code: "working_directory_repository_access_failed".to_string(),
|
||||
message: error.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_working_directories(
|
||||
&self,
|
||||
runtime_id: &str,
|
||||
@@ -3205,6 +3233,18 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
fn authorize_working_directory_repository_access(
|
||||
&self,
|
||||
request: WorkingDirectoryRepositoryAccessRequest,
|
||||
) -> std::result::Result<(), Error> {
|
||||
self.post_json::<_, RuntimeHttpRepositoryAccessResponse>(
|
||||
"/v1/working-directories/repository-access",
|
||||
&request,
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|diagnostic| Error::RegistryInconsistency(diagnostic.message))
|
||||
}
|
||||
|
||||
fn list_working_directories(&self) -> RuntimeList<WorkingDirectoryStatus> {
|
||||
match self.get_json::<RuntimeHttpWorkingDirectoriesResponse>("/v1/working-directories") {
|
||||
Ok(response) => RuntimeList::new(response.working_directories, Vec::new()),
|
||||
|
||||
@@ -1437,6 +1437,18 @@ impl WorkspaceApi {
|
||||
self.validate_worker_spawn_repository_scope(&request)?;
|
||||
let workspace_api = self.workspace_api_ref(runtime_id);
|
||||
request.resolved_workspace_api = Some(workspace_api.clone());
|
||||
if let Some(working_directory) = request.resolved_working_directory.as_ref()
|
||||
&& let Some(access) = repository_access_request_for_workdir(
|
||||
self,
|
||||
runtime_id,
|
||||
&working_directory.working_directory_id,
|
||||
&format!("worker-spawn:{}", WorkerId::now_v7()),
|
||||
)?
|
||||
{
|
||||
self.runtime
|
||||
.authorize_working_directory_repository_access(runtime_id, access)
|
||||
.map_err(RuntimeRegistryError::into_error)?;
|
||||
}
|
||||
let attachment_reservation =
|
||||
request
|
||||
.resolved_working_directory
|
||||
@@ -1652,6 +1664,22 @@ impl WorkspaceApi {
|
||||
&self,
|
||||
worker: &RuntimeWorkerRef,
|
||||
) -> ApiResult<WorkerRestoreResult> {
|
||||
if let Some(link) = self
|
||||
.store
|
||||
.list_worker_workdir_links(&self.config.workspace_id, worker)?
|
||||
.into_iter()
|
||||
.find(|link| link.unlinked_at.is_none())
|
||||
&& let Some(access) = repository_access_request_for_workdir(
|
||||
self,
|
||||
&worker.runtime_id,
|
||||
&link.workdir_id,
|
||||
&format!("worker-restore:{}", WorkerId::now_v7()),
|
||||
)?
|
||||
{
|
||||
self.runtime
|
||||
.authorize_working_directory_repository_access(&worker.runtime_id, access)
|
||||
.map_err(RuntimeRegistryError::into_error)?;
|
||||
}
|
||||
let binding = self
|
||||
.runtime
|
||||
.replace_worker_workspace_api(worker, self.workspace_api_ref(&worker.runtime_id))
|
||||
@@ -13967,6 +13995,11 @@ fn authorize_repository_materialization(
|
||||
host_trust_id: lease.host_trust_id,
|
||||
host_trust_revision: lease.host_trust_revision,
|
||||
access: binding.access,
|
||||
expires_at_epoch_seconds: std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs()
|
||||
.saturating_add(300),
|
||||
private_key: SensitiveString::new(lease.private_key.as_str()),
|
||||
known_hosts_entry: SensitiveString::new(lease.known_hosts_entry),
|
||||
})
|
||||
@@ -13985,6 +14018,47 @@ fn authorize_repository_materialization(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn repository_access_request_for_workdir(
|
||||
api: &WorkspaceApi,
|
||||
runtime_id: &str,
|
||||
working_directory_id: &str,
|
||||
operation_id: &str,
|
||||
) -> ApiResult<Option<worker_runtime::catalog::WorkingDirectoryRepositoryAccessRequest>> {
|
||||
let record = api
|
||||
.config_store
|
||||
.get_workdir_registry(&api.config.workspace_id, working_directory_id)?
|
||||
.ok_or_else(|| {
|
||||
settings_bad_request(
|
||||
"working_directory_not_found",
|
||||
"Working directory is not registered in this Workspace",
|
||||
)
|
||||
})?;
|
||||
if record.runtime_id != runtime_id {
|
||||
return Err(settings_bad_request(
|
||||
"working_directory_runtime_mismatch",
|
||||
"Working directory is owned by a different Runtime",
|
||||
));
|
||||
}
|
||||
let repository = api.require_configured_workspace_repository(&record.repository_id)?;
|
||||
if repository.source.kind != workspace_api::RepositorySourceKind::Ssh {
|
||||
return Ok(None);
|
||||
}
|
||||
let projection = active_repository_access_projection(api, &api.config.workspace_id)?;
|
||||
let mut request = working_directory_request_from_repository(&repository, None);
|
||||
authorize_repository_materialization(api, runtime_id, operation_id, &projection, &mut request)?;
|
||||
Ok(Some(
|
||||
worker_runtime::catalog::WorkingDirectoryRepositoryAccessRequest {
|
||||
working_directory_id: working_directory_id.to_string(),
|
||||
materialization: request.materialization.ok_or_else(|| {
|
||||
settings_bad_request(
|
||||
"working_directory_remote_repository_access_required",
|
||||
"SSH Repository access authority is unavailable",
|
||||
)
|
||||
})?,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn working_directory_request_for_browser(
|
||||
api: &WorkspaceApi,
|
||||
request: BrowserWorkingDirectoryCreateRequest,
|
||||
|
||||
Reference in New Issue
Block a user