mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-07 04:19:58 +00:00
agent: support sealed secret as env in kata
When sealed-secret is enabled, the Kata Agent intercepts environment variables containing sealed secrets and uses the CDH to unseal the value. Signed-off-by: Tobin Feldman-Fitzthum <tobin@ibm.com> Signed-off-by: Linda Yu <linda.yu@intel.com>
This commit is contained in:
parent
6c1a2f01f8
commit
6003608fe6
1
src/agent/Cargo.lock
generated
1
src/agent/Cargo.lock
generated
@ -2142,6 +2142,7 @@ dependencies = [
|
|||||||
"cgroups-rs",
|
"cgroups-rs",
|
||||||
"clap",
|
"clap",
|
||||||
"const_format",
|
"const_format",
|
||||||
|
"derivative",
|
||||||
"futures",
|
"futures",
|
||||||
"image-rs",
|
"image-rs",
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
|
@ -23,6 +23,7 @@ regex = "1.10.4"
|
|||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
oci-distribution = "0.10.0"
|
oci-distribution = "0.10.0"
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
|
derivative = "2.2.0"
|
||||||
kata-sys-util = { path = "../libs/kata-sys-util" }
|
kata-sys-util = { path = "../libs/kata-sys-util" }
|
||||||
kata-types = { path = "../libs/kata-types" }
|
kata-types = { path = "../libs/kata-types" }
|
||||||
safe-path = { path = "../libs/safe-path" }
|
safe-path = { path = "../libs/safe-path" }
|
||||||
|
62
src/agent/src/cdh.rs
Normal file
62
src/agent/src/cdh.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2023 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
// Confidential Data Hub client wrapper.
|
||||||
|
// Confidential Data Hub is a service running inside guest to provide resource related APIs.
|
||||||
|
// https://github.com/confidential-containers/guest-components/tree/main/confidential-data-hub
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use derivative::Derivative;
|
||||||
|
use protocols::{
|
||||||
|
sealed_secret, sealed_secret_ttrpc_async, sealed_secret_ttrpc_async::SealedSecretServiceClient,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::CDH_SOCKET_URI;
|
||||||
|
|
||||||
|
// Nanoseconds
|
||||||
|
const CDH_UNSEAL_TIMEOUT: i64 = 50 * 1000 * 1000 * 1000;
|
||||||
|
const SEALED_SECRET_PREFIX: &str = "sealed.";
|
||||||
|
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Clone, Debug)]
|
||||||
|
pub struct CDHClient {
|
||||||
|
#[derivative(Debug = "ignore")]
|
||||||
|
sealed_secret_client: SealedSecretServiceClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CDHClient {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let client = ttrpc::asynchronous::Client::connect(CDH_SOCKET_URI)?;
|
||||||
|
let sealed_secret_client =
|
||||||
|
sealed_secret_ttrpc_async::SealedSecretServiceClient::new(client);
|
||||||
|
|
||||||
|
Ok(CDHClient {
|
||||||
|
sealed_secret_client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unseal_secret_async(&self, sealed_secret: &str) -> Result<Vec<u8>> {
|
||||||
|
let mut input = sealed_secret::UnsealSecretInput::new();
|
||||||
|
input.set_secret(sealed_secret.into());
|
||||||
|
|
||||||
|
let unsealed_secret = self
|
||||||
|
.sealed_secret_client
|
||||||
|
.unseal_secret(ttrpc::context::with_timeout(CDH_UNSEAL_TIMEOUT), &input)
|
||||||
|
.await?;
|
||||||
|
Ok(unsealed_secret.plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unseal_env(&self, env: &str) -> Result<String> {
|
||||||
|
if let Some((key, value)) = env.split_once('=') {
|
||||||
|
if value.starts_with(SEALED_SECRET_PREFIX) {
|
||||||
|
let unsealed_value = self.unseal_secret_async(value).await?;
|
||||||
|
let unsealed_env = format!("{}={}", key, std::str::from_utf8(&unsealed_value)?);
|
||||||
|
|
||||||
|
return Ok(unsealed_env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((*env.to_owned()).to_string())
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,7 @@ use std::process::Command;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{instrument, span};
|
use tracing::{instrument, span};
|
||||||
|
|
||||||
|
mod cdh;
|
||||||
mod config;
|
mod config;
|
||||||
mod console;
|
mod console;
|
||||||
mod device;
|
mod device;
|
||||||
@ -59,6 +60,7 @@ mod util;
|
|||||||
mod version;
|
mod version;
|
||||||
mod watcher;
|
mod watcher;
|
||||||
|
|
||||||
|
use cdh::CDHClient;
|
||||||
use config::GuestComponentsProcs;
|
use config::GuestComponentsProcs;
|
||||||
use mount::{cgroups_mount, general_mount};
|
use mount::{cgroups_mount, general_mount};
|
||||||
use sandbox::Sandbox;
|
use sandbox::Sandbox;
|
||||||
@ -104,6 +106,7 @@ const AA_ATTESTATION_URI: &str = concatcp!(UNIX_SOCKET_PREFIX, AA_ATTESTATION_SO
|
|||||||
|
|
||||||
const CDH_PATH: &str = "/usr/local/bin/confidential-data-hub";
|
const CDH_PATH: &str = "/usr/local/bin/confidential-data-hub";
|
||||||
const CDH_SOCKET: &str = "/run/confidential-containers/cdh.sock";
|
const CDH_SOCKET: &str = "/run/confidential-containers/cdh.sock";
|
||||||
|
const CDH_SOCKET_URI: &str = concatcp!(UNIX_SOCKET_PREFIX, CDH_SOCKET);
|
||||||
|
|
||||||
const API_SERVER_PATH: &str = "/usr/local/bin/api-server-rest";
|
const API_SERVER_PATH: &str = "/usr/local/bin/api-server-rest";
|
||||||
|
|
||||||
@ -403,6 +406,7 @@ async fn start_sandbox(
|
|||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
sandbox.lock().await.sender = Some(tx);
|
sandbox.lock().await.sender = Some(tx);
|
||||||
|
|
||||||
|
let mut cdh_client = None;
|
||||||
let gc_procs = config.guest_components_procs;
|
let gc_procs = config.guest_components_procs;
|
||||||
if gc_procs != GuestComponentsProcs::None {
|
if gc_procs != GuestComponentsProcs::None {
|
||||||
if !attestation_binaries_available(logger, &gc_procs) {
|
if !attestation_binaries_available(logger, &gc_procs) {
|
||||||
@ -411,12 +415,19 @@ async fn start_sandbox(
|
|||||||
"attestation binaries requested for launch not available"
|
"attestation binaries requested for launch not available"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
init_attestation_components(logger, config)?;
|
cdh_client = init_attestation_components(logger, config)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vsock:///dev/vsock, port
|
// vsock:///dev/vsock, port
|
||||||
let mut server = rpc::start(sandbox.clone(), config.server_addr.as_str(), init_mode).await?;
|
let mut server = rpc::start(
|
||||||
|
sandbox.clone(),
|
||||||
|
config.server_addr.as_str(),
|
||||||
|
init_mode,
|
||||||
|
cdh_client,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
server.start().await?;
|
server.start().await?;
|
||||||
|
|
||||||
rx.await?;
|
rx.await?;
|
||||||
@ -445,10 +456,11 @@ fn attestation_binaries_available(logger: &Logger, procs: &GuestComponentsProcs)
|
|||||||
// Start-up attestation-agent, CDH and api-server-rest if they are packaged in the rootfs
|
// Start-up attestation-agent, CDH and api-server-rest if they are packaged in the rootfs
|
||||||
// and the corresponding procs are enabled in the agent configuration. the process will be
|
// and the corresponding procs are enabled in the agent configuration. the process will be
|
||||||
// launched in the background and the function will return immediately.
|
// launched in the background and the function will return immediately.
|
||||||
fn init_attestation_components(logger: &Logger, config: &AgentConfig) -> Result<()> {
|
// If the CDH is started, a CDH client will be instantiated and returned.
|
||||||
|
fn init_attestation_components(logger: &Logger, config: &AgentConfig) -> Result<Option<CDHClient>> {
|
||||||
// skip launch of any guest-component
|
// skip launch of any guest-component
|
||||||
if config.guest_components_procs == GuestComponentsProcs::None {
|
if config.guest_components_procs == GuestComponentsProcs::None {
|
||||||
return Ok(());
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(logger, "spawning attestation-agent process {}", AA_PATH);
|
debug!(logger, "spawning attestation-agent process {}", AA_PATH);
|
||||||
@ -463,7 +475,7 @@ fn init_attestation_components(logger: &Logger, config: &AgentConfig) -> Result<
|
|||||||
|
|
||||||
// skip launch of confidential-data-hub and api-server-rest
|
// skip launch of confidential-data-hub and api-server-rest
|
||||||
if config.guest_components_procs == GuestComponentsProcs::AttestationAgent {
|
if config.guest_components_procs == GuestComponentsProcs::AttestationAgent {
|
||||||
return Ok(());
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
@ -479,9 +491,11 @@ fn init_attestation_components(logger: &Logger, config: &AgentConfig) -> Result<
|
|||||||
)
|
)
|
||||||
.map_err(|e| anyhow!("launch_process {} failed: {:?}", CDH_PATH, e))?;
|
.map_err(|e| anyhow!("launch_process {} failed: {:?}", CDH_PATH, e))?;
|
||||||
|
|
||||||
|
let cdh_client = CDHClient::new().context("Failed to create CDH Client")?;
|
||||||
|
|
||||||
// skip launch of api-server-rest
|
// skip launch of api-server-rest
|
||||||
if config.guest_components_procs == GuestComponentsProcs::ConfidentialDataHub {
|
if config.guest_components_procs == GuestComponentsProcs::ConfidentialDataHub {
|
||||||
return Ok(());
|
return Ok(Some(cdh_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
let features = config.guest_components_rest_api;
|
let features = config.guest_components_rest_api;
|
||||||
@ -498,7 +512,7 @@ fn init_attestation_components(logger: &Logger, config: &AgentConfig) -> Result<
|
|||||||
)
|
)
|
||||||
.map_err(|e| anyhow!("launch_process {} failed: {:?}", API_SERVER_PATH, e))?;
|
.map_err(|e| anyhow!("launch_process {} failed: {:?}", API_SERVER_PATH, e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(Some(cdh_client))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_path_to_exist(logger: &Logger, path: &str, timeout_secs: i32) -> Result<()> {
|
fn wait_for_path_to_exist(logger: &Logger, path: &str, timeout_secs: i32) -> Result<()> {
|
||||||
|
@ -76,6 +76,8 @@ use crate::policy::{do_set_policy, is_allowed};
|
|||||||
#[cfg(feature = "guest-pull")]
|
#[cfg(feature = "guest-pull")]
|
||||||
use crate::image;
|
use crate::image;
|
||||||
|
|
||||||
|
use crate::cdh::CDHClient;
|
||||||
|
|
||||||
use opentelemetry::global;
|
use opentelemetry::global;
|
||||||
use tracing::span;
|
use tracing::span;
|
||||||
use tracing_opentelemetry::OpenTelemetrySpanExt;
|
use tracing_opentelemetry::OpenTelemetrySpanExt;
|
||||||
@ -171,6 +173,7 @@ impl<T> OptionToTtrpcResult<T> for Option<T> {
|
|||||||
pub struct AgentService {
|
pub struct AgentService {
|
||||||
sandbox: Arc<Mutex<Sandbox>>,
|
sandbox: Arc<Mutex<Sandbox>>,
|
||||||
init_mode: bool,
|
init_mode: bool,
|
||||||
|
cdh_client: Option<CDHClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AgentService {
|
impl AgentService {
|
||||||
@ -222,6 +225,22 @@ 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 let Some(cdh) = self.cdh_client.as_ref() {
|
||||||
|
let process = oci
|
||||||
|
.process
|
||||||
|
.as_mut()
|
||||||
|
.ok_or_else(|| anyhow!("Spec didn't contain process field"))?;
|
||||||
|
|
||||||
|
for env in process.env.iter_mut() {
|
||||||
|
match cdh.unseal_env(env).await {
|
||||||
|
Ok(unsealed_env) => *env = unsealed_env.to_string(),
|
||||||
|
Err(e) => {
|
||||||
|
warn!(sl(), "Failed to unseal secret: {}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Both rootfs and volumes (invoked with --volume for instance) will
|
// Both rootfs and volumes (invoked with --volume for instance) will
|
||||||
// be processed the same way. The idea is to always mount any provided
|
// be processed the same way. The idea is to always mount any provided
|
||||||
// storage to the specified MountPoint, so that it will match what's
|
// storage to the specified MountPoint, so that it will match what's
|
||||||
@ -1601,10 +1620,12 @@ pub async fn start(
|
|||||||
s: Arc<Mutex<Sandbox>>,
|
s: Arc<Mutex<Sandbox>>,
|
||||||
server_address: &str,
|
server_address: &str,
|
||||||
init_mode: bool,
|
init_mode: bool,
|
||||||
|
cdh_client: Option<CDHClient>,
|
||||||
) -> Result<TtrpcServer> {
|
) -> Result<TtrpcServer> {
|
||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: s,
|
sandbox: s,
|
||||||
init_mode,
|
init_mode,
|
||||||
|
cdh_client,
|
||||||
}) as Box<dyn agent_ttrpc::AgentService + Send + Sync>;
|
}) as Box<dyn agent_ttrpc::AgentService + Send + Sync>;
|
||||||
let aservice = agent_ttrpc::create_agent_service(Arc::new(agent_service));
|
let aservice = agent_ttrpc::create_agent_service(Arc::new(agent_service));
|
||||||
|
|
||||||
@ -2148,6 +2169,7 @@ mod tests {
|
|||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||||
init_mode: true,
|
init_mode: true,
|
||||||
|
cdh_client: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = protocols::agent::UpdateInterfaceRequest::default();
|
let req = protocols::agent::UpdateInterfaceRequest::default();
|
||||||
@ -2162,10 +2184,10 @@ mod tests {
|
|||||||
async fn test_update_routes() {
|
async fn test_update_routes() {
|
||||||
let logger = slog::Logger::root(slog::Discard, o!());
|
let logger = slog::Logger::root(slog::Discard, o!());
|
||||||
let sandbox = Sandbox::new(&logger).unwrap();
|
let sandbox = Sandbox::new(&logger).unwrap();
|
||||||
|
|
||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||||
init_mode: true,
|
init_mode: true,
|
||||||
|
cdh_client: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = protocols::agent::UpdateRoutesRequest::default();
|
let req = protocols::agent::UpdateRoutesRequest::default();
|
||||||
@ -2180,10 +2202,10 @@ mod tests {
|
|||||||
async fn test_add_arp_neighbors() {
|
async fn test_add_arp_neighbors() {
|
||||||
let logger = slog::Logger::root(slog::Discard, o!());
|
let logger = slog::Logger::root(slog::Discard, o!());
|
||||||
let sandbox = Sandbox::new(&logger).unwrap();
|
let sandbox = Sandbox::new(&logger).unwrap();
|
||||||
|
|
||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||||
init_mode: true,
|
init_mode: true,
|
||||||
|
cdh_client: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = protocols::agent::AddARPNeighborsRequest::default();
|
let req = protocols::agent::AddARPNeighborsRequest::default();
|
||||||
@ -2322,6 +2344,7 @@ mod tests {
|
|||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||||
init_mode: true,
|
init_mode: true,
|
||||||
|
cdh_client: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = agent_service
|
let result = agent_service
|
||||||
@ -2811,6 +2834,7 @@ OtherField:other
|
|||||||
let agent_service = Box::new(AgentService {
|
let agent_service = Box::new(AgentService {
|
||||||
sandbox: Arc::new(Mutex::new(sandbox)),
|
sandbox: Arc::new(Mutex::new(sandbox)),
|
||||||
init_mode: true,
|
init_mode: true,
|
||||||
|
cdh_client: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let ctx = mk_ttrpc_context();
|
let ctx = mk_ttrpc_context();
|
||||||
|
Loading…
Reference in New Issue
Block a user