mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-09-19 15:58:25 +00:00
Merge pull request #8155 from jodh-intel/runtime-rs-check-ch-tdx-build-feature
runtime-rs: ch: Add TDX CH features check
This commit is contained in:
@@ -67,6 +67,12 @@ pub struct CloudHypervisorInner {
|
||||
// The cloud-hypervisor device-id is later looked up and used while
|
||||
// removing the device.
|
||||
pub(crate) device_ids: HashMap<String, String>,
|
||||
|
||||
// List of Cloud Hypervisor features enabled at Cloud Hypervisor build-time.
|
||||
//
|
||||
// If the version of CH does not provide these details, the value will be
|
||||
// None.
|
||||
pub(crate) ch_features: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
const CH_DEFAULT_TIMEOUT_SECS: u32 = 10;
|
||||
@@ -104,6 +110,7 @@ impl CloudHypervisorInner {
|
||||
shutdown_rx: Some(rx),
|
||||
tasks: None,
|
||||
guest_protection_to_use: GuestProtection::NoProtection,
|
||||
ch_features: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,8 @@ use kata_sys_util::protection::{available_guest_protection, GuestProtection};
|
||||
use kata_types::capabilities::{Capabilities, CapabilityBits};
|
||||
use kata_types::config::default::DEFAULT_CH_ROOTFS_TYPE;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::create_dir_all;
|
||||
use std::os::unix::net::UnixStream;
|
||||
@@ -42,6 +44,20 @@ const CH_NAME: &str = "cloud-hypervisor";
|
||||
/// Number of milliseconds to wait before retrying a CH operation.
|
||||
const CH_POLL_TIME_MS: u64 = 50;
|
||||
|
||||
// The name of the CH JSON key for the build-time features list.
|
||||
const CH_FEATURES_KEY: &str = "features";
|
||||
|
||||
// The name of the CH build-time feature for Intel TDX.
|
||||
const CH_FEATURE_TDX: &str = "tdx";
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct VmmPingResponse {
|
||||
pub build_version: String,
|
||||
pub version: String,
|
||||
pub pid: i64,
|
||||
pub features: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq)]
|
||||
pub enum GuestProtectionError {
|
||||
#[error("guest protection requested but no guest protection available")]
|
||||
@@ -55,6 +71,10 @@ pub enum GuestProtectionError {
|
||||
// "true".
|
||||
#[error("TDX guest protection available and must be used with Cloud Hypervisor (set 'confidential_guest=true')")]
|
||||
TDXProtectionMustBeUsedWithCH,
|
||||
|
||||
// TDX is the only tested CH protection currently.
|
||||
#[error("Expected TDX protection, found {0}")]
|
||||
ExpectedTDXProtection(GuestProtection),
|
||||
}
|
||||
|
||||
impl CloudHypervisorInner {
|
||||
@@ -71,6 +91,14 @@ impl CloudHypervisorInner {
|
||||
.await
|
||||
.context("hypervisor running check failed")?;
|
||||
|
||||
if guest_protection_is_tdx(self.guest_protection_to_use.clone()) {
|
||||
if let Some(features) = &self.ch_features {
|
||||
if !features.contains(&CH_FEATURE_TDX.to_string()) {
|
||||
return Err(anyhow!("Cloud Hypervisor is not built with TDX support"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state = VmmState::VmmServerReady;
|
||||
|
||||
Ok(())
|
||||
@@ -420,6 +448,33 @@ impl CloudHypervisorInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Check the specified ping API response to see if it contains CH's
|
||||
// build-time features list. If so, save them.
|
||||
async fn handle_ch_build_features(&mut self, ping_response: &str) -> Result<()> {
|
||||
let v: Value = serde_json::from_str(ping_response)?;
|
||||
|
||||
let got = &v[CH_FEATURES_KEY];
|
||||
|
||||
if got.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let features_list = got
|
||||
.as_array()
|
||||
.ok_or("expected CH to return array of features")
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
|
||||
let features: Vec<String> = features_list
|
||||
.iter()
|
||||
.map(Value::to_string)
|
||||
.map(|s| s.trim_start_matches('"').trim_end_matches('"').to_string())
|
||||
.collect();
|
||||
|
||||
self.ch_features = Some(features);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn cloud_hypervisor_ping_until_ready(&mut self, _poll_time_ms: u64) -> Result<()> {
|
||||
let socket = self
|
||||
.api_socket
|
||||
@@ -435,7 +490,11 @@ impl CloudHypervisorInner {
|
||||
|
||||
if let Ok(response) = response {
|
||||
if let Some(detail) = response {
|
||||
// Check for a list of built-in features, returned by this
|
||||
// API call in newer versions of CH.
|
||||
debug!(sl!(), "ping response: {:?}", detail);
|
||||
|
||||
self.handle_ch_build_features(&detail).await?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -482,17 +541,25 @@ impl CloudHypervisorInner {
|
||||
task::spawn_blocking(|| -> Result<GuestProtection> { get_guest_protection() })
|
||||
.await??;
|
||||
|
||||
if protection == GuestProtection::NoProtection {
|
||||
if confidential_guest {
|
||||
return Err(anyhow!(GuestProtectionError::NoProtectionAvailable));
|
||||
} else {
|
||||
debug!(sl!(), "no guest protection available");
|
||||
}
|
||||
} else if confidential_guest {
|
||||
self.guest_protection_to_use = protection.clone();
|
||||
self.guest_protection_to_use = protection.clone();
|
||||
|
||||
info!(sl!(), "guest protection available and requested"; "guest-protection" => protection.to_string());
|
||||
info!(sl!(), "guest protection {:?}", protection.to_string());
|
||||
|
||||
if confidential_guest {
|
||||
if protection == GuestProtection::NoProtection {
|
||||
// User wants protection, but none available.
|
||||
return Err(anyhow!(GuestProtectionError::NoProtectionAvailable));
|
||||
} else if let GuestProtection::Tdx(_) = protection {
|
||||
info!(sl!(), "guest protection available and requested"; "guest-protection" => protection.to_string());
|
||||
} else {
|
||||
return Err(anyhow!(GuestProtectionError::ExpectedTDXProtection(
|
||||
protection
|
||||
)));
|
||||
}
|
||||
} else if protection == GuestProtection::NoProtection {
|
||||
debug!(sl!(), "no guest protection available");
|
||||
} else if let GuestProtection::Tdx(_) = protection {
|
||||
// CH requires TDX protection to be used.
|
||||
return Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH));
|
||||
} else {
|
||||
info!(sl!(), "guest protection available but not requested"; "guest-protection" => protection.to_string());
|
||||
@@ -899,13 +966,27 @@ mod tests {
|
||||
confidential_guest: false,
|
||||
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
||||
result: Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH)),
|
||||
guest_protection_to_use: GuestProtection::NoProtection,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
},
|
||||
TestData {
|
||||
confidential_guest: true,
|
||||
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
||||
result: Ok(()),
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details),
|
||||
},
|
||||
TestData {
|
||||
confidential_guest: false,
|
||||
available_protection: Some(GuestProtection::Pef),
|
||||
result: Ok(()),
|
||||
guest_protection_to_use: GuestProtection::NoProtection,
|
||||
},
|
||||
TestData {
|
||||
confidential_guest: true,
|
||||
available_protection: Some(GuestProtection::Pef),
|
||||
result: Err(anyhow!(GuestProtectionError::ExpectedTDXProtection(
|
||||
GuestProtection::Pef
|
||||
))),
|
||||
guest_protection_to_use: GuestProtection::Pef,
|
||||
},
|
||||
];
|
||||
|
||||
|
Reference in New Issue
Block a user