Skip to content

Commit

Permalink
vmm: Unshare IPC and UTS namespaces for container process
Browse files Browse the repository at this point in the history
Container process needs a POD level IPC and UTS namespace unshared with
kuasar-task by default as pause container has been removed.

In this commit, kuasar-task will unshare a new ns and bind mount it to
`/run/sandbox-ns`, in which the container could join. The hostname in the
sandbox pod config will be set into uts ns by runC when creating container.

Signed-off-by: Zhang Tianyang <[email protected]>
  • Loading branch information
Burning1020 committed Jan 12, 2024
1 parent 6a599c0 commit e385ece
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 7 deletions.
5 changes: 5 additions & 0 deletions vmm/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ pub const DEV_SHM: &str = "/dev/shm";
pub const HOSTS_FILENAME: &str = "hosts";
pub const HOSTNAME_FILENAME: &str = "hostname";
pub const RESOLV_FILENAME: &str = "resolv.conf";

pub const SANDBOX_NS_PATH: &str = "/run/sandbox-ns";
pub const NET_NAMESPACE: &str = "net";
pub const IPC_NAMESPACE: &str = "ipc";
pub const UTS_NAMESPACE: &str = "uts";
24 changes: 22 additions & 2 deletions vmm/sandbox/src/container/handler/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use containerd_sandbox::{
};
use path_clean::clean;
use vmm_common::{
ETC_HOSTNAME, ETC_HOSTS, ETC_RESOLV, HOSTNAME_FILENAME, HOSTS_FILENAME, KUASAR_STATE_DIR,
RESOLV_FILENAME,
ETC_HOSTNAME, ETC_HOSTS, ETC_RESOLV, HOSTNAME_FILENAME, HOSTS_FILENAME, IPC_NAMESPACE,
KUASAR_STATE_DIR, RESOLV_FILENAME, SANDBOX_NS_PATH, UTS_NAMESPACE,
};

use crate::{
Expand Down Expand Up @@ -56,6 +56,12 @@ where
sandbox: &mut KuasarSandbox<T>,
) -> containerd_sandbox::error::Result<()> {
let shared_path = sandbox.get_sandbox_shared_path();
let hostname = sandbox
.data
.config
.as_ref()
.map(|c| c.hostname.to_string())
.unwrap_or_default();
let container = sandbox.container_mut(&self.container_id)?;
let spec = container
.data
Expand All @@ -71,6 +77,20 @@ where
}
// Update sandbox files mounts for container
container_mounts(&shared_path, spec);

// Update ipc and uts namespace for container
if let Some(linux) = spec.linux.as_mut() {
for ns in linux.namespaces.iter_mut() {
if ns.r#type == IPC_NAMESPACE || ns.r#type == UTS_NAMESPACE {
ns.path = format!("{}/{}", SANDBOX_NS_PATH, ns.r#type);
continue;
}
}
}

// Update container hostname to sandbox hostname
spec.hostname = hostname;

let spec_str = serde_json::to_string(spec)
.map_err(|e| anyhow!("failed to parse spec in sandbox, {}", e))?;
let config_path = format!("{}/{}", container.data.bundle, CONFIG_FILE_NAME);
Expand Down
69 changes: 64 additions & 5 deletions vmm/task/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

use std::{convert::TryFrom, path::Path, str::FromStr, sync::Arc};
use std::{collections::HashMap, convert::TryFrom, path::Path, str::FromStr, sync::Arc, thread};

use containerd_shim::{
asynchronous::{monitor::monitor_notify_by_pid, util::asyncify},
error::Error,
io_error, other,
protos::{shim::shim_ttrpc_async::create_task, ttrpc::asynchronous::Server},
util::IntoOption,
util::{mkdir, IntoOption},
Result,
};
use futures::StreamExt;
use lazy_static::lazy_static;
use log::{debug, error, info, warn, LevelFilter};
use nix::{
errno::Errno,
sched::{unshare, CloneFlags},
sys::{
wait,
wait::{WaitPidFlag, WaitStatus},
},
unistd::Pid,
unistd::{getpid, gettid, Pid},
};
use signal_hook_tokio::Signals;
use tokio::fs::File;
use vmm_common::{
api::sandbox_ttrpc::create_sandbox_service, mount::mount, ETC_RESOLV, KUASAR_STATE_DIR,
RESOLV_FILENAME,
api::sandbox_ttrpc::create_sandbox_service, mount::mount, ETC_RESOLV, IPC_NAMESPACE,
KUASAR_STATE_DIR, NET_NAMESPACE, RESOLV_FILENAME, SANDBOX_NS_PATH, UTS_NAMESPACE,
};

use crate::{
Expand Down Expand Up @@ -127,6 +129,11 @@ lazy_static! {
dest: KUASAR_STATE_DIR,
options: vec!["relatime", "nodev", "sync", "dirsync",]
},];
static ref CLONE_FLAG_TABLE: HashMap<String, CloneFlags> = HashMap::from([
(String::from(NET_NAMESPACE), CloneFlags::CLONE_NEWNET),
(String::from(IPC_NAMESPACE), CloneFlags::CLONE_NEWIPC),
(String::from(UTS_NAMESPACE), CloneFlags::CLONE_NEWUTS),
]);
}

#[tokio::main]
Expand Down Expand Up @@ -298,6 +305,9 @@ async fn late_init_call() -> Result<()> {
warn!("unable to find DNS files in kuasar state dir");
}

// Setup sandbox namespace
setup_sandbox_ns().await?;

Ok(())
}

Expand Down Expand Up @@ -342,3 +352,52 @@ async fn start_ttrpc_server() -> Result<Server> {
.register_service(task_service)
.register_service(sandbox_service))
}

async fn setup_sandbox_ns() -> Result<()> {
setup_persistent_ns(vec![
String::from(IPC_NAMESPACE),
String::from(UTS_NAMESPACE),
])
.await?;
Ok(())
}

async fn setup_persistent_ns(ns_types: Vec<String>) -> Result<()> {
if ns_types.is_empty() {
return Ok(());
}
mkdir(SANDBOX_NS_PATH, 0o711).await?;

let mut clone_type = CloneFlags::empty();

for ns_type in &ns_types {
let sandbox_ns_path = format!("{}/{}", SANDBOX_NS_PATH, ns_type);
File::create(&sandbox_ns_path).await.map_err(io_error!(
e,
"failed to create: {}",
sandbox_ns_path
))?;

clone_type |= *CLONE_FLAG_TABLE
.get(ns_type)
.ok_or(other!("bad ns type {}", ns_type))?;
}

thread::spawn(move || {
unshare(clone_type).expect("failed to do unshare");

for ns_type in &ns_types {
let sandbox_ns_path = format!("{}/{}", SANDBOX_NS_PATH, ns_type);
let ns_path = format!("/proc/{}/task/{}/ns/{}", getpid(), gettid(), ns_type);
mount(
Some("none"),
Some(ns_path.as_str()),
&["bind".to_string()],
&sandbox_ns_path,
)
.expect("failed to mount sandbox ns");
}
});

Ok(())
}

0 comments on commit e385ece

Please sign in to comment.