Skip to content

Commit

Permalink
Agent: Add grpctls proxy API endpoints
Browse files Browse the repository at this point in the history
This commit is an initial step towards mitigating the risk of
untrustworthy host systems when running Kata Containers in the context
of confidential computing. It serves to safeguard against malicious,
privileged users gaining access to the vulnerable Kata control
plane. This protective measure becomes crucial in scenarios where a
malicious cloud service provider or administrator might intercept or
compromise commands from the Kata control plane, tamper with container
configuration files, execute processes within the container, retrieve
workload statistics, or obtain sensitive container workload
information.

** Problem statement **

This commit addresses the following open issues:
[Securing the Kata Control Plane](confidential-containers/confidential-containers#53)
and RFC: Separate trust realms for tenant and host kata-containers#1834. A detailed
history can be found in:
https://github.com/ray-valdez/kata-containers/tree/split-api-feature.

** Architectural changes **

The commit introduces a new split API mode in the kata-agent, which
partitions the kata-agent’s API endpoints between **host-side**
and **owner-side** controllers. When this mode is enabled, the
host-side controller is restricted to manage resource allocation
during startup and resource recycling at termination. In contrasts,
the owner-side controller allows workload owners to directly manage
theIR deployment pod and containers. This partitioning implicitly
labels kata-agent’s endpoint APIs as _host-exclusive_,
_owner-exclusive_, or _shared_.

Host-exclusive and owner-exclusive APIs are assigned specifically to
either the host-side or owner-side. For instance, `CreateSandbox` and
`DestroySandbox` are examples of host-exclusive APIs, while `CopyFile`
and `ExecProcess` are examples of owner-exclusive APIs. Shared APIs
include those that must be shared to some extent between the control
planes, such as `GetOOMEvent` and `GetGuestDetails`.

** Content of this commit **

This commit focuses on providing a secure channel for the owner-side
to access owner-exlusive and shared APIs. Future commit(s) will
restrict the host-side access to owner-exclusive APIs when split mode
is enabled on the kata-agent and will address the sharing of APIs
between host-side and owner-side.

This commit implements the following changes:
- Introduces the split mode to the kata-agent.
- Integrates a gRPC TLS server to handle API requests from the
  owner-side.  We refer to this as the kata-agent’s API proxy server
  which ensures that workload owners can establish a secure end-to-end
  communication channel with the kata-agent or invoking API endpoint
  commands.
- Utilizes the Key Broker Service (KBS) to provision secrets, i.e.,
  cryptographic public and private key pairs. These secrets are crucial
  for establishing a secure communication channel between the owner-side
  and the API proxy server.

** Testing **

To enable split mode functionality, the following steps are required:

1. Configuration: Modify the `kernel_params` option in the Kata's
  configuration.toml file to enable split mode and specify the IP
  address of the KBS.
- Add following settings to the `kernel_params` option:
  `agent.split_api=true` and
  `agent.aa_kbc_params=cc_kbc::http://[IP_ADDRESS]:[PORT]`.

2. Dependency on KBS: The kata-agent relies on the KBS to provision
   cryptographic keys to the split API proxy server, facilitating the
   establishment of a secure channel.

- Generate TLS keys and certificates for kata-agent’s API proxy server
  and client (owner-side)

```
$  KATA_DIR=”<PATH to cloned repo>”
$ pushd ${KATA_DIR}/src/agent/grpc_tls_keys
$ ./gen_key_cert.sh
```

 - Create a zip file named 'tls-keys.zip' containing the CA public key
   and the server’s public and private key pair

` $ zip tls-keys.zip server.pem server.key ca.pem`

  - Place this zip file in the KBS resource path
  '/default/tenant-keys/'. During sandbox creation, the kata-agent
  retrieves this file using the KBS 'get resource' API.  It's
  important to note that the KBS conducts a background check on the
  key request, verifying evidence provided by the Trusted Execution
  Environment (TEE). Future extensions to the KBS will automate the
  creation of the server’s public and private key pair for each
  sandbox.

` $ popd`

** External tools required for testing **

To  exercise the API proxy server, we provide the Kata Containers
agent TLS control tool (kata-agent-tls-ctl), derived from the
`kata-agent-ctl` tool in another commit (see `split-api-feature` branch
referenced above).

This tool communicates over a gRPC TLS channel with the kata-agent.
Similar to the kata-agent-ctl, this is a low level tool that is
intended for advanced users. Future commit(s) will introduce a more
user-friendly tool that maintains state, designed to function as a
kubectl plugin for managing owners’ workloads.

Examples of creating and starting a container using
`kata-agent-tls-ctl`:

Setup environment

```
$ export guest_addr=10.89.0.28 # IP address associated with the confidential VM
$ export guest_port=50090         # API proxy server’s port (listens on)
$ export ctl=./target/x86_64-unknown-linux-musl/release/kata-agent-tls-ctl
$ export key_dir=${KATA_DIR}/src/agent/grpc_tls_key
```

Display the status of containers in the sandbox environment

```
$ ${ctl} -l trace connect --key-dir "${key_dir}"  --bundle-dir "${bundle_dir}"  \
--server-address  "ipaddr://${guest_addr}:${guest_port}" \
 -c  "ListContainers"
```

Set a container ID and specify an OCI spec:

```
$ container_id=9e3d1d4750e4e20945d22c358e13c85c6b88922513bce2832c0cf403f065dc6
$ OCI_SPEC_CONFIG=${KATA_DIR}/src/tools/agent-tls-ctl/config.json
```

_Note: the next two commands require pull_image support in the guest!_
**Create container request**
```

$ ${ctl} -l trace connect --key-dir "${key_dir}" --bundle-dir "${bundle_dir}"  \
--server-address  "ipaddr://${guest_addr}:${guest_port}" \
 -c "CreateContainer cid=${container_id} spec=file:///${OCI_SPEC_CONFIG}"
```

**Start container request**

```
$ ${ctl} -l trace connect --no-auto-values --key-dir "${key_dir}" --bundle-dir "${bundle_dir}"  \
--server-address "ipaddr://${guest_addr}:${guest_port}" \
-c "StartContainer json://{\"container_id\": \"${container_id}\"}"
```

Fixes: kata-containers#1834

Signed-off-by: Christophe de Dinechin <dinechin@redhat.com>
  • Loading branch information
ray-valdez authored and c3d committed Feb 26, 2024
1 parent bb5e33b commit 3f772f4
Show file tree
Hide file tree
Showing 24 changed files with 5,797 additions and 173 deletions.
1,227 changes: 1,155 additions & 72 deletions src/agent/Cargo.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/agent/Cargo.toml
Expand Up @@ -23,6 +23,11 @@ regex = "1.5.6"
serial_test = "0.5.1"
kata-sys-util = { path = "../libs/kata-sys-util" }
kata-types = { path = "../libs/kata-types" }
prost = "0.11"
tonic = {version="0.8", features = ["tls"]}
zip = "0.5.4"

kbs_protocol = { git = "https://github.com/confidential-containers/guest-components.git", rev = "42b7c96", default-features = false, features = [ "background_check", "openssl" ]}

# Async helpers
async-trait = "0.1.42"
Expand Down Expand Up @@ -73,6 +78,9 @@ reqwest = { version = "0.11.14", optional = true }
# The "vendored" feature for openssl is required for musl build
openssl = { version = "0.10.54", features = ["vendored"], optional = true }

[build-dependencies]
tonic-build = "0.8"

[dev-dependencies]
tempfile = "3.1.0"
test-utils = { path = "../libs/test-utils" }
Expand Down
4 changes: 4 additions & 0 deletions src/agent/grpc_tls_keys/.gitignore
@@ -0,0 +1,4 @@
*.key
*.pem
*.csr
*.srl
7 changes: 7 additions & 0 deletions src/agent/grpc_tls_keys/cert.ext
@@ -0,0 +1,7 @@
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
51 changes: 51 additions & 0 deletions src/agent/grpc_tls_keys/gen_key_cert.sh
@@ -0,0 +1,51 @@
#!/bin/bash
#
# Create rsa private and public keys
#
CA_KEY=ca.key
CA_PEM=ca.pem

SERVER_KEY=server.key
SERVER_CSR=server.csr
SERVER_PEM=server.pem

CLIENT_KEY=client.key
CLIENT_CSR=client.csr
CLIENT_PEM=client.pem

KEY_SIZE=2048
COUNTRY="AA"
STATE="Default State"
LOCALITY="Default City"
ORG="Default Unit"
ORG_UNIT="Default Unit"
CERT_EXT=cert.ext

#1. Create CA key
openssl genrsa -out ${CA_KEY} ${KEY_SIZE}

#2. Create self-signed cert valid for ten years
openssl req -x509 -new -nodes -key ${CA_KEY} -sha256 -days 3650 -out ${CA_PEM} \
-subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCALITY}/O=${ORG}/OU=${ORG_UNIT}/CN=grpc-tls CA"

#3. Create server key (kata_agent)
openssl genrsa -out ${SERVER_KEY} ${KEY_SIZE}

#4. Create cert request
openssl req -new -sha256 -key ${SERVER_KEY} -out ${SERVER_CSR} \
-subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCALITY}/O=${ORG}/OU=${ORG_UNIT}/CN=server" \

# 5. Create server cert
openssl x509 -req -in ${SERVER_CSR} -CA ${CA_PEM} -CAkey ${CA_KEY} -CAcreateserial -out ${SERVER_PEM} -days 3650 -sha256 -extfile ${CERT_EXT} 2> /dev/null

## Repeat steps 3 - 5 for client (tenant)

#3. Create client key
openssl genrsa -out ${CLIENT_KEY} ${KEY_SIZE}

#4. Create cert request
openssl req -new -sha256 -key ${CLIENT_KEY} -out ${CLIENT_CSR} \
-subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCALITY}/O=${ORG}/OU=${ORG_UNIT}/CN=client"

#5. Create cert cert
openssl x509 -req -in ${CLIENT_CSR} -CA ${CA_PEM} -CAkey ${CA_KEY} -CAcreateserial -out ${CLIENT_PEM} -days 3650 -sha256 -extfile ${CERT_EXT} 2> /dev/null
22 changes: 22 additions & 0 deletions src/agent/src/config.rs
Expand Up @@ -25,6 +25,8 @@ const LOG_VPORT_OPTION: &str = "agent.log_vport";
const CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size";
const UNIFIED_CGROUP_HIERARCHY_OPTION: &str = "agent.unified_cgroup_hierarchy";
const CONFIG_FILE: &str = "agent.config_file";
const AA_KBC_PARAMS: &str = "agent.aa_kbc_params";
const SPLIT_API_FLAG: &str = "agent.split_api";

const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info;
const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3);
Expand Down Expand Up @@ -66,6 +68,8 @@ pub struct AgentConfig {
pub unified_cgroup_hierarchy: bool,
pub tracing: bool,
pub supports_seccomp: bool,
pub aa_kbc_params: String,
pub split_api: bool,
}

#[derive(Debug, Deserialize)]
Expand All @@ -81,6 +85,8 @@ pub struct AgentConfigBuilder {
pub passfd_listener_port: Option<i32>,
pub unified_cgroup_hierarchy: Option<bool>,
pub tracing: Option<bool>,
pub aa_kbc_params: Option<String>,
pub split_api: Option<bool>,
}

macro_rules! config_override {
Expand Down Expand Up @@ -142,6 +148,8 @@ impl Default for AgentConfig {
unified_cgroup_hierarchy: false,
tracing: false,
supports_seccomp: rpc::have_seccomp(),
aa_kbc_params: String::from(""),
split_api: false,
}
}
}
Expand Down Expand Up @@ -171,6 +179,8 @@ impl FromStr for AgentConfig {
config_override!(agent_config_builder, agent_config, passfd_listener_port);
config_override!(agent_config_builder, agent_config, unified_cgroup_hierarchy);
config_override!(agent_config_builder, agent_config, tracing);
config_override!(agent_config_builder, agent_config, aa_kbc_params);
config_override!(agent_config_builder, agent_config, split_api);

Ok(agent_config)
}
Expand Down Expand Up @@ -270,6 +280,18 @@ impl AgentConfig {
config.unified_cgroup_hierarchy,
get_bool_value
);
parse_cmdline_param!(
param,
AA_KBC_PARAMS,
config.aa_kbc_params,
get_string_value
);
parse_cmdline_param!(
param,
SPLIT_API_FLAG,
config.split_api,
get_bool_value
);
}

if let Ok(addr) = env::var(SERVER_ADDR_ENV_VAR) {
Expand Down
26 changes: 26 additions & 0 deletions src/agent/src/main.rs
Expand Up @@ -73,6 +73,7 @@ use tokio::{
};

mod rpc;
mod secrets;
mod tracer;

#[cfg(feature = "agent-policy")]
Expand Down Expand Up @@ -376,6 +377,31 @@ async fn start_sandbox(
let mut server = rpc::start(sandbox.clone(), config.server_addr.as_str(), init_mode)?;
server.start().await?;

if config.split_api {
// downloading and extracting the tls keys for the grpctls server
match secrets::retrieve_secrets().await {
Ok(_) => println!("main: Success in getting tenant-keys"),
Err(e) => {
eprintln!("main: Failed to get keys: {:?}", e)
}
}

// if the tls keys are downloaded and extracted, then start the grpctls server
if secrets::tls_keys_exist() {

// TBD Remove owner execlusive APIs from the host side
// let _ = match config.remove_owner_api() {
// Ok(_) => println!("main: Disable Owner API"),
// Err(e) => { println!("main: Unable to disable Owner API: {:?}", e) }
// };

let gserver =
rpc::rpctls::grpcstart(sandbox.clone(), config.server_addr.as_str(), init_mode)
.await?;
gserver.await?;
}
}

rx.await?;
server.shutdown().await?;

Expand Down
2 changes: 2 additions & 0 deletions src/agent/src/rpc.rs
Expand Up @@ -92,6 +92,8 @@ use std::path::PathBuf;

use kata_types::k8s;

pub mod rpctls;

pub const CONTAINER_BASE: &str = "/run/kata-containers";
const MODPROBE_PATH: &str = "/sbin/modprobe";

Expand Down

0 comments on commit 3f772f4

Please sign in to comment.