blob: 2fa542f3efb07cbd75eb24192b800f31fa921fc9 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#[cfg(target_os = "linux")]
pub use crate::linux::mounts::MountLease;
use crate::{Error, Sandbox};
use std::{
ffi::OsString,
path::{Path, PathBuf},
};
/// Platform preparation, not a second process command representation. The
/// process owner retains arguments, environment, I/O, cancellation and receipts.
pub enum Launch {
Direct,
Wrapped {
program: PathBuf,
args: Vec<OsString>,
/// Mount and filter descriptors are CLOEXEC in the Host. The process
/// owner must preserve them only in the child executing the wrapper.
#[cfg(target_os = "linux")]
files: Vec<std::fs::File>,
#[cfg(target_os = "linux")]
lease: MountLease,
},
}
impl Sandbox {
#[cfg(target_os = "linux")]
pub fn prepare_with_private_network(
&self,
cwd: &Path,
directory: Option<&std::fs::File>,
) -> Result<Launch, Error> {
crate::path::validate(cwd)?;
let Self::Managed {
filesystem,
network: network @ crate::Network::Restricted { .. },
} = self
else {
return Err(Error::Invalid(
"private gateway requires a destination-restricted policy".into(),
));
};
crate::linux::prepare(&filesystem.compile()?, network, cwd, directory)
}
/// Compile only after Host authorization. No process starts here. The caller
/// appends its original executable and arguments after a wrapper's arguments.
/// Failure must never fall back to Direct or retry the original effect.
pub fn prepare(&self, cwd: &Path) -> Result<Launch, Error> {
self.prepare_network(cwd, None, None)
}
/// Linux must mount the admitted cwd inode because bubblewrap resolves
/// --chdir inside its new namespace after the outer process's fchdir.
#[cfg(target_os = "linux")]
pub fn prepare_captured(
&self,
cwd: &Path,
directory: Option<&std::fs::File>,
) -> Result<Launch, Error> {
self.prepare_network(cwd, None, directory)
}
/// The execution owner binds and retains the gateway before compiling this
/// exception. It is launch data, never a user-provided policy override.
pub fn prepare_with_proxy(
&self,
cwd: &Path,
proxy: std::net::SocketAddr,
) -> Result<Launch, Error> {
if !proxy.ip().is_loopback() || proxy.port() == 0 {
return Err(Error::Invalid(
"sandbox proxy must be a loopback listener".into(),
));
}
self.prepare_network(cwd, Some(proxy), None)
}
fn prepare_network(
&self,
cwd: &Path,
proxy: Option<std::net::SocketAddr>,
_directory: Option<&std::fs::File>,
) -> Result<Launch, Error> {
crate::path::validate(cwd)?;
match self {
Self::Disabled => Ok(Launch::Direct),
Self::External { .. } => Err(Error::Unsupported(
"external isolation requires an executor-owned launch path".into(),
)),
Self::Managed {
filesystem,
network,
} => {
if matches!(network, crate::Network::Restricted { .. })
&& (proxy.is_none() || !cfg!(target_os = "macos"))
{
return Err(Error::Unsupported(
"destination-restricted execution requires a managed network proxy".into(),
));
}
let filesystem = filesystem.compile()?;
#[cfg(target_os = "macos")]
{
crate::seatbelt::prepare(&filesystem, network, proxy)
}
#[cfg(target_os = "linux")]
{
crate::linux::prepare(&filesystem, network, cwd, _directory)
}
#[cfg(windows)]
{
let _ = (filesystem, network);
Err(Error::Unsupported(
"platform backend is not installed".into(),
))
}
}
}
}
}