mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-28 20:41:04 +00:00
agent: support sealed secret as file in kata
Fixes: #7555 Signed-off-by: Linda Yu <linda.yu@intel.com>
This commit is contained in:
parent
c60adedf99
commit
d7873e5251
@ -8,24 +8,42 @@
|
|||||||
// https://github.com/confidential-containers/guest-components/tree/main/confidential-data-hub
|
// https://github.com/confidential-containers/guest-components/tree/main/confidential-data-hub
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use oci::{Mount, Spec};
|
||||||
use protocols::{
|
use protocols::{
|
||||||
sealed_secret, sealed_secret_ttrpc_async, sealed_secret_ttrpc_async::SealedSecretServiceClient,
|
sealed_secret, sealed_secret_ttrpc_async, sealed_secret_ttrpc_async::SealedSecretServiceClient,
|
||||||
};
|
};
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::symlink;
|
||||||
|
use std::path::Path;
|
||||||
const CDH_ADDR: &str = "unix:///run/confidential-containers/cdh.sock";
|
const CDH_ADDR: &str = "unix:///run/confidential-containers/cdh.sock";
|
||||||
|
const SECRETS_DIR: &str = "/run/secrets/";
|
||||||
|
const SEALED_SECRET_TIMEOUT: i64 = 50 * 1000 * 1000 * 1000;
|
||||||
|
|
||||||
|
// Convenience function to obtain the scope logger.
|
||||||
|
fn sl() -> slog::Logger {
|
||||||
|
slog_scope::logger()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CDHClient {
|
pub struct CDHClient {
|
||||||
sealed_secret_client: SealedSecretServiceClient,
|
sealed_secret_client: Option<SealedSecretServiceClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CDHClient {
|
impl CDHClient {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let c = ttrpc::asynchronous::Client::connect(CDH_ADDR)?;
|
let c = ttrpc::asynchronous::Client::connect(CDH_ADDR);
|
||||||
let ssclient = sealed_secret_ttrpc_async::SealedSecretServiceClient::new(c);
|
match c {
|
||||||
|
Ok(v) => {
|
||||||
|
let ssclient = sealed_secret_ttrpc_async::SealedSecretServiceClient::new(v);
|
||||||
Ok(CDHClient {
|
Ok(CDHClient {
|
||||||
sealed_secret_client: ssclient,
|
sealed_secret_client: Some(ssclient),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Err(_) => Ok(CDHClient {
|
||||||
|
sealed_secret_client: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn unseal_secret_async(
|
pub async fn unseal_secret_async(
|
||||||
&self,
|
&self,
|
||||||
@ -33,27 +51,25 @@ impl CDHClient {
|
|||||||
) -> Result<sealed_secret::UnsealSecretOutput> {
|
) -> Result<sealed_secret::UnsealSecretOutput> {
|
||||||
let secret = sealed
|
let secret = sealed
|
||||||
.strip_prefix("sealed.")
|
.strip_prefix("sealed.")
|
||||||
.ok_or(anyhow!("strip_prefix sealed. failed"))?;
|
.ok_or(anyhow!("strip_prefix \"sealed.\" failed"))?;
|
||||||
let mut input = sealed_secret::UnsealSecretInput::new();
|
let mut input = sealed_secret::UnsealSecretInput::new();
|
||||||
input.set_secret(secret.into());
|
input.set_secret(secret.into());
|
||||||
let unseal = self
|
let unseal = self
|
||||||
.sealed_secret_client
|
.sealed_secret_client
|
||||||
.unseal_secret(
|
.as_ref()
|
||||||
ttrpc::context::with_timeout(50 * 1000 * 1000 * 1000),
|
.ok_or(anyhow!("unwrap sealed_secret_client failed"))?
|
||||||
&input,
|
.unseal_secret(ttrpc::context::with_timeout(SEALED_SECRET_TIMEOUT), &input)
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(unseal)
|
Ok(unseal)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unseal_env(&self, env: &str) -> Result<String> {
|
pub async fn unseal_env(&self, env: &str) -> Result<String> {
|
||||||
let (key, value) = env.split_once('=').unwrap();
|
let (key, value) = env.split_once('=').unwrap_or(("", ""));
|
||||||
if value.starts_with("sealed.") {
|
if value.starts_with("sealed.") {
|
||||||
let unsealed_value = self.unseal_secret_async(value).await;
|
let unsealed_value = self.unseal_secret_async(value).await;
|
||||||
match unsealed_value {
|
match unsealed_value {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
let plain_env =
|
let plain_env = format!("{}={}", key, std::str::from_utf8(&v.plaintext)?);
|
||||||
format!("{}={}", key, std::str::from_utf8(&v.plaintext).unwrap());
|
|
||||||
return Ok(plain_env);
|
return Ok(plain_env);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -63,6 +79,81 @@ impl CDHClient {
|
|||||||
}
|
}
|
||||||
Ok((*env.to_owned()).to_string())
|
Ok((*env.to_owned()).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn unseal_file(&self, sealed_source_path: &String) -> Result<()> {
|
||||||
|
if !Path::new(sealed_source_path).exists() {
|
||||||
|
info!(
|
||||||
|
sl(),
|
||||||
|
"sealed source path {:?} does not exist", sealed_source_path
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in fs::read_dir(sealed_source_path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
|
||||||
|
if !entry.file_type()?.is_symlink()
|
||||||
|
&& !fs::metadata(entry.path())?.file_type().is_file()
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
sl(),
|
||||||
|
"skipping sealed source entry {:?} because its file type is {:?}",
|
||||||
|
entry,
|
||||||
|
entry.file_type()?
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_path = fs::canonicalize(&entry.path())?;
|
||||||
|
info!(sl(), "sealed source entry target path: {:?}", target_path);
|
||||||
|
if !target_path.is_file() {
|
||||||
|
info!(sl(), "sealed source is not a file: {:?}", target_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let secret_name = entry.file_name();
|
||||||
|
let contents = fs::read_to_string(&target_path)?;
|
||||||
|
if contents.starts_with("sealed.") {
|
||||||
|
info!(sl(), "sealed source entry found: {:?}", target_path);
|
||||||
|
let unsealed_filename = SECRETS_DIR.to_string()
|
||||||
|
+ secret_name
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.ok_or(anyhow!("create unsealed_filename failed"))?;
|
||||||
|
let unsealed_value = self.unseal_secret_async(&contents).await?;
|
||||||
|
fs::write(&unsealed_filename, unsealed_value.plaintext)?;
|
||||||
|
fs::remove_file(&entry.path())?;
|
||||||
|
symlink(unsealed_filename, &entry.path())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_sealed_secret_mounts(&self, spec: &mut Spec) -> Result<Vec<String>> {
|
||||||
|
let mut sealed_source_path: Vec<String> = vec![];
|
||||||
|
for m in spec.mounts.iter_mut() {
|
||||||
|
if let Some(unsealed_mount_point) = m.destination.strip_prefix("/sealed") {
|
||||||
|
info!(
|
||||||
|
sl(),
|
||||||
|
"sealed mount destination: {:?} source: {:?}", m.destination, m.source
|
||||||
|
);
|
||||||
|
sealed_source_path.push(m.source.clone());
|
||||||
|
m.destination = unsealed_mount_point.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sealed_source_path.len() > 0 {
|
||||||
|
let sealed_mounts = Mount {
|
||||||
|
destination: SECRETS_DIR.to_string(),
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
source: SECRETS_DIR.to_string(),
|
||||||
|
options: vec!["bind".to_string()],
|
||||||
|
};
|
||||||
|
spec.mounts.push(sealed_mounts);
|
||||||
|
}
|
||||||
|
fs::create_dir_all(SECRETS_DIR)?;
|
||||||
|
Ok(sealed_source_path)
|
||||||
|
}
|
||||||
} /* end of impl CDHClient */
|
} /* end of impl CDHClient */
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -145,5 +236,6 @@ mod tests {
|
|||||||
assert_eq!(unchanged_env, String::from("key=testdata"));
|
assert_eq!(unchanged_env, String::from("key=testdata"));
|
||||||
|
|
||||||
rt.shutdown_background();
|
rt.shutdown_background();
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,24 +209,27 @@ impl AgentService {
|
|||||||
// cannot predict everything from the caller.
|
// cannot predict everything from the caller.
|
||||||
add_devices(&req.devices, &mut oci, &self.sandbox).await?;
|
add_devices(&req.devices, &mut oci, &self.sandbox).await?;
|
||||||
|
|
||||||
if cfg!(feature = "sealed-secret") {
|
#[cfg(feature = "sealed-secret")]
|
||||||
|
let mut sealed_source_path = {
|
||||||
let process = oci
|
let process = oci
|
||||||
.process
|
.process
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or_else(|| anyhow!("Spec didn't contain process field"))?;
|
.ok_or_else(|| anyhow!("Spec didn't contain process field"))?;
|
||||||
|
|
||||||
for env in process.env.iter_mut() {
|
|
||||||
let client = self
|
let client = self
|
||||||
.cdh_client
|
.cdh_client
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(anyhow!("get cdh_client failed"))?;
|
.ok_or(anyhow!("get cdh_client failed"))?;
|
||||||
|
for env in process.env.iter_mut() {
|
||||||
let unsealed_env = client
|
let unsealed_env = client
|
||||||
.unseal_env(env)
|
.unseal_env(env)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("unseal env failed: {:?}", e))?;
|
.map_err(|e| anyhow!("unseal env failed: {:?}", e))?;
|
||||||
*env = unsealed_env.to_string();
|
*env = unsealed_env.to_string();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
client.create_sealed_secret_mounts(&mut oci)?
|
||||||
|
};
|
||||||
|
|
||||||
let linux = oci
|
let linux = oci
|
||||||
.linux
|
.linux
|
||||||
@ -311,6 +314,17 @@ impl AgentService {
|
|||||||
return Err(anyhow!(nix::Error::EINVAL));
|
return Err(anyhow!(nix::Error::EINVAL));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "sealed-secret")]
|
||||||
|
{
|
||||||
|
let client = self
|
||||||
|
.cdh_client
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(anyhow!("get cdh_client failed"))?;
|
||||||
|
for source_path in sealed_source_path.iter_mut() {
|
||||||
|
client.unseal_file(source_path).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if starting container failed, we will do some rollback work
|
// if starting container failed, we will do some rollback work
|
||||||
// to ensure no resources are leaked.
|
// to ensure no resources are leaked.
|
||||||
if let Err(err) = ctr.start(p).await {
|
if let Err(err) = ctr.start(p).await {
|
||||||
|
@ -911,6 +911,10 @@ func (k *kataAgent) replaceOCIMountSource(spec *specs.Spec, guestMounts map[stri
|
|||||||
for index, m := range ociMounts {
|
for index, m := range ociMounts {
|
||||||
if guestMount, ok := guestMounts[m.Destination]; ok {
|
if guestMount, ok := guestMounts[m.Destination]; ok {
|
||||||
k.Logger().Debugf("Replacing OCI mount (%s) source %s with %s", m.Destination, m.Source, guestMount.Source)
|
k.Logger().Debugf("Replacing OCI mount (%s) source %s with %s", m.Destination, m.Source, guestMount.Source)
|
||||||
|
if strings.Contains(m.Source, "kubernetes.io~secret") {
|
||||||
|
ociMounts[index].Destination = "/sealed" + m.Destination
|
||||||
|
k.Logger().Debugf("Replacing OCI mount (%s) with new destination %s", m.Destination, ociMounts[index].Destination)
|
||||||
|
}
|
||||||
ociMounts[index].Source = guestMount.Source
|
ociMounts[index].Source = guestMount.Source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user