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
|
// The cloud-hypervisor device-id is later looked up and used while
|
||||||
// removing the device.
|
// removing the device.
|
||||||
pub(crate) device_ids: HashMap<String, String>,
|
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;
|
const CH_DEFAULT_TIMEOUT_SECS: u32 = 10;
|
||||||
@@ -104,6 +110,7 @@ impl CloudHypervisorInner {
|
|||||||
shutdown_rx: Some(rx),
|
shutdown_rx: Some(rx),
|
||||||
tasks: None,
|
tasks: None,
|
||||||
guest_protection_to_use: GuestProtection::NoProtection,
|
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::capabilities::{Capabilities, CapabilityBits};
|
||||||
use kata_types::config::default::DEFAULT_CH_ROOTFS_TYPE;
|
use kata_types::config::default::DEFAULT_CH_ROOTFS_TYPE;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::os::unix::net::UnixStream;
|
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.
|
/// Number of milliseconds to wait before retrying a CH operation.
|
||||||
const CH_POLL_TIME_MS: u64 = 50;
|
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)]
|
#[derive(thiserror::Error, Debug, PartialEq)]
|
||||||
pub enum GuestProtectionError {
|
pub enum GuestProtectionError {
|
||||||
#[error("guest protection requested but no guest protection available")]
|
#[error("guest protection requested but no guest protection available")]
|
||||||
@@ -55,6 +71,10 @@ pub enum GuestProtectionError {
|
|||||||
// "true".
|
// "true".
|
||||||
#[error("TDX guest protection available and must be used with Cloud Hypervisor (set 'confidential_guest=true')")]
|
#[error("TDX guest protection available and must be used with Cloud Hypervisor (set 'confidential_guest=true')")]
|
||||||
TDXProtectionMustBeUsedWithCH,
|
TDXProtectionMustBeUsedWithCH,
|
||||||
|
|
||||||
|
// TDX is the only tested CH protection currently.
|
||||||
|
#[error("Expected TDX protection, found {0}")]
|
||||||
|
ExpectedTDXProtection(GuestProtection),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CloudHypervisorInner {
|
impl CloudHypervisorInner {
|
||||||
@@ -71,6 +91,14 @@ impl CloudHypervisorInner {
|
|||||||
.await
|
.await
|
||||||
.context("hypervisor running check failed")?;
|
.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;
|
self.state = VmmState::VmmServerReady;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -420,6 +448,33 @@ impl CloudHypervisorInner {
|
|||||||
Ok(())
|
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<()> {
|
async fn cloud_hypervisor_ping_until_ready(&mut self, _poll_time_ms: u64) -> Result<()> {
|
||||||
let socket = self
|
let socket = self
|
||||||
.api_socket
|
.api_socket
|
||||||
@@ -435,7 +490,11 @@ impl CloudHypervisorInner {
|
|||||||
|
|
||||||
if let Ok(response) = response {
|
if let Ok(response) = response {
|
||||||
if let Some(detail) = 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);
|
debug!(sl!(), "ping response: {:?}", detail);
|
||||||
|
|
||||||
|
self.handle_ch_build_features(&detail).await?;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -482,17 +541,25 @@ impl CloudHypervisorInner {
|
|||||||
task::spawn_blocking(|| -> Result<GuestProtection> { get_guest_protection() })
|
task::spawn_blocking(|| -> Result<GuestProtection> { get_guest_protection() })
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
if protection == GuestProtection::NoProtection {
|
self.guest_protection_to_use = protection.clone();
|
||||||
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();
|
|
||||||
|
|
||||||
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 {
|
} else if let GuestProtection::Tdx(_) = protection {
|
||||||
|
// CH requires TDX protection to be used.
|
||||||
return Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH));
|
return Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH));
|
||||||
} else {
|
} else {
|
||||||
info!(sl!(), "guest protection available but not requested"; "guest-protection" => protection.to_string());
|
info!(sl!(), "guest protection available but not requested"; "guest-protection" => protection.to_string());
|
||||||
@@ -899,13 +966,27 @@ mod tests {
|
|||||||
confidential_guest: false,
|
confidential_guest: false,
|
||||||
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
||||||
result: Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH)),
|
result: Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH)),
|
||||||
guest_protection_to_use: GuestProtection::NoProtection,
|
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||||
},
|
},
|
||||||
TestData {
|
TestData {
|
||||||
confidential_guest: true,
|
confidential_guest: true,
|
||||||
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
available_protection: Some(GuestProtection::Tdx(tdx_details.clone())),
|
||||||
result: Ok(()),
|
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