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
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use oci::{Mount, Spec};
|
||||
use protocols::{
|
||||
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 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)]
|
||||
pub struct CDHClient {
|
||||
sealed_secret_client: SealedSecretServiceClient,
|
||||
sealed_secret_client: Option<SealedSecretServiceClient>,
|
||||
}
|
||||
|
||||
impl CDHClient {
|
||||
pub fn new() -> Result<Self> {
|
||||
let c = ttrpc::asynchronous::Client::connect(CDH_ADDR)?;
|
||||
let ssclient = sealed_secret_ttrpc_async::SealedSecretServiceClient::new(c);
|
||||
let c = ttrpc::asynchronous::Client::connect(CDH_ADDR);
|
||||
match c {
|
||||
Ok(v) => {
|
||||
let ssclient = sealed_secret_ttrpc_async::SealedSecretServiceClient::new(v);
|
||||
Ok(CDHClient {
|
||||
sealed_secret_client: ssclient,
|
||||
sealed_secret_client: Some(ssclient),
|
||||
})
|
||||
}
|
||||
Err(_) => Ok(CDHClient {
|
||||
sealed_secret_client: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn unseal_secret_async(
|
||||
&self,
|
||||
@ -33,27 +51,25 @@ impl CDHClient {
|
||||
) -> Result<sealed_secret::UnsealSecretOutput> {
|
||||
let secret = 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();
|
||||
input.set_secret(secret.into());
|
||||
let unseal = self
|
||||
.sealed_secret_client
|
||||
.unseal_secret(
|
||||
ttrpc::context::with_timeout(50 * 1000 * 1000 * 1000),
|
||||
&input,
|
||||
)
|
||||
.as_ref()
|
||||
.ok_or(anyhow!("unwrap sealed_secret_client failed"))?
|
||||
.unseal_secret(ttrpc::context::with_timeout(SEALED_SECRET_TIMEOUT), &input)
|
||||
.await?;
|
||||
Ok(unseal)
|
||||
}
|
||||
|
||||
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.") {
|
||||
let unsealed_value = self.unseal_secret_async(value).await;
|
||||
match unsealed_value {
|
||||
Ok(v) => {
|
||||
let plain_env =
|
||||
format!("{}={}", key, std::str::from_utf8(&v.plaintext).unwrap());
|
||||
let plain_env = format!("{}={}", key, std::str::from_utf8(&v.plaintext)?);
|
||||
return Ok(plain_env);
|
||||
}
|
||||
Err(e) => {
|
||||
@ -63,6 +79,81 @@ impl CDHClient {
|
||||
}
|
||||
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 */
|
||||
|
||||
#[cfg(test)]
|
||||
@ -145,5 +236,6 @@ mod tests {
|
||||
assert_eq!(unchanged_env, String::from("key=testdata"));
|
||||
|
||||
rt.shutdown_background();
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
|
@ -209,24 +209,27 @@ impl AgentService {
|
||||
// cannot predict everything from the caller.
|
||||
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
|
||||
.process
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("Spec didn't contain process field"))?;
|
||||
|
||||
for env in process.env.iter_mut() {
|
||||
let client = self
|
||||
.cdh_client
|
||||
.as_ref()
|
||||
.ok_or(anyhow!("get cdh_client failed"))?;
|
||||
for env in process.env.iter_mut() {
|
||||
let unsealed_env = client
|
||||
.unseal_env(env)
|
||||
.await
|
||||
.map_err(|e| anyhow!("unseal env failed: {:?}", e))?;
|
||||
*env = unsealed_env.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
client.create_sealed_secret_mounts(&mut oci)?
|
||||
};
|
||||
|
||||
let linux = oci
|
||||
.linux
|
||||
@ -311,6 +314,17 @@ impl AgentService {
|
||||
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
|
||||
// to ensure no resources are leaked.
|
||||
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 {
|
||||
if guestMount, ok := guestMounts[m.Destination]; ok {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user