diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 4c65509bd3..9e194700f5 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -817,6 +817,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -2081,6 +2101,7 @@ dependencies = [ "cfg-if 1.0.0", "cgroups-rs", "clap", + "const_format", "futures", "http", "image-rs", @@ -5210,6 +5231,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "universal-hash" version = "0.5.1" diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 31092c0aa4..ff7dd65f7f 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -26,6 +26,7 @@ url = "2.5.0" kata-sys-util = { path = "../libs/kata-sys-util" } kata-types = { path = "../libs/kata-types" } safe-path = { path = "../libs/safe-path" } +const_format = "0.2.30" # Async helpers async-trait = "0.1.42" @@ -36,7 +37,7 @@ futures = "0.3.30" tokio = { version = "1.28.1", features = ["full"] } tokio-vsock = "0.3.1" -netlink-sys = { version = "0.7.0", features = ["tokio_socket",]} +netlink-sys = { version = "0.7.0", features = ["tokio_socket"] } rtnetlink = "0.8.0" netlink-packet-utils = "0.4.1" ipnetwork = "0.17.0" @@ -62,7 +63,7 @@ cgroups = { package = "cgroups-rs", version = "0.3.3" } tracing = "0.1.26" tracing-subscriber = "0.2.18" tracing-opentelemetry = "0.13.0" -opentelemetry = { version = "0.14.0", features = ["rt-tokio-current-thread"]} +opentelemetry = { version = "0.14.0", features = ["rt-tokio-current-thread"] } vsock-exporter = { path = "vsock-exporter" } # Configuration @@ -87,16 +88,14 @@ rstest = "0.18.0" async-std = { version = "1.12.0", features = ["attributes"] } [workspace] -members = [ - "rustjail", -] +members = ["rustjail"] [profile.release] lto = true [features] # The default-pull feature would support all pull types, including sharing images by virtio-fs and pulling images in the guest -default-pull = [ "guest-pull" ] +default-pull = ["guest-pull"] seccomp = ["rustjail/seccomp"] standard-oci-runtime = ["rustjail/standard-oci-runtime"] agent-policy = ["http", "openssl", "reqwest"] diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index e79ec6fb35..e6fba188b3 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -22,6 +22,7 @@ extern crate slog; use anyhow::{anyhow, Context, Result}; use cfg_if::cfg_if; use clap::{AppSettings, Parser}; +use const_format::concatcp; use nix::fcntl::OFlag; use nix::sys::socket::{self, AddressFamily, SockFlag, SockType, VsockAddr}; use nix::unistd::{self, dup, Pid}; @@ -32,6 +33,7 @@ use std::os::unix::fs as unixfs; use std::os::unix::io::AsRawFd; use std::path::Path; use std::process::exit; +use std::process::Command; use std::sync::Arc; use tracing::{instrument, span}; @@ -91,6 +93,20 @@ cfg_if! { const NAME: &str = "kata-agent"; +const UNIX_SOCKET_PREFIX: &str = "unix://"; + +const AA_PATH: &str = "/usr/local/bin/attestation-agent"; +const AA_ATTESTATION_SOCKET: &str = + "/run/confidential-containers/attestation-agent/attestation-agent.sock"; +const AA_ATTESTATION_URI: &str = concatcp!(UNIX_SOCKET_PREFIX, AA_ATTESTATION_SOCKET); + +const CDH_PATH: &str = "/usr/local/bin/confidential-data-hub"; +const CDH_SOCKET: &str = "/run/confidential-containers/cdh.sock"; + +const API_SERVER_PATH: &str = "/usr/local/bin/api-server-rest"; + +const DEFAULT_LAUNCH_PROCESS_TIMEOUT: i32 = 6; + lazy_static! { static ref AGENT_CONFIG: AgentConfig = // Note: We can't do AgentOpts.parse() here to send through the processed arguments to AgentConfig @@ -384,6 +400,10 @@ async fn start_sandbox( let (tx, rx) = tokio::sync::oneshot::channel(); sandbox.lock().await.sender = Some(tx); + if Path::new(CDH_PATH).exists() && Path::new(AA_PATH).exists() { + init_attestation_components(logger)?; + } + // vsock:///dev/vsock, port let mut server = rpc::start(sandbox.clone(), config.server_addr.as_str(), init_mode).await?; server.start().await?; @@ -394,6 +414,73 @@ async fn start_sandbox( Ok(()) } +// Start-up attestation-agent, CDH and api-server-rest if they are packaged in the rootfs +fn init_attestation_components(logger: &Logger) -> Result<()> { + // The Attestation Agent will run for the duration of the guest. + launch_process( + logger, + AA_PATH, + &vec!["--attestation_sock", AA_ATTESTATION_URI], + AA_ATTESTATION_SOCKET, + DEFAULT_LAUNCH_PROCESS_TIMEOUT, + ) + .map_err(|e| anyhow!("launch_process {} failed: {:?}", AA_PATH, e))?; + + if let Err(e) = launch_process( + logger, + CDH_PATH, + &vec![], + CDH_SOCKET, + DEFAULT_LAUNCH_PROCESS_TIMEOUT, + ) { + error!(logger, "launch_process {} failed: {:?}", CDH_PATH, e); + } + + Ok(()) +} + +fn wait_for_path_to_exist(logger: &Logger, path: &str, timeout_secs: i32) -> Result<()> { + let p = Path::new(path); + let mut attempts = 0; + loop { + std::thread::sleep(std::time::Duration::from_secs(1)); + if p.exists() { + return Ok(()); + } + if attempts >= timeout_secs { + break; + } + attempts += 1; + info!( + logger, + "waiting for {} to exist (attempts={})", path, attempts + ); + } + + Err(anyhow!("wait for {} to exist timeout.", path)) +} + +fn launch_process( + logger: &Logger, + path: &str, + args: &Vec<&str>, + unix_socket_path: &str, + timeout_secs: i32, +) -> Result<()> { + if !Path::new(path).exists() { + return Err(anyhow!("path {} does not exist.", path)); + } + if !unix_socket_path.is_empty() && Path::new(unix_socket_path).exists() { + fs::remove_file(unix_socket_path)?; + } + Command::new(path).args(args).spawn()?; + if !unix_socket_path.is_empty() && timeout_secs > 0 { + wait_for_path_to_exist(logger, unix_socket_path, timeout_secs)?; + } + + Ok(()) +} + // init_agent_as_init will do the initializations such as setting up the rootfs // when this agent has been run as the init process. fn init_agent_as_init(logger: &Logger, unified_cgroup_hierarchy: bool) -> Result<()> {