Skip to content

Commit

Permalink
Merge pull request kata-containers#8870 from LindaYu17/aa2main
Browse files Browse the repository at this point in the history
port attestation agent from CCv0 branch to main branch
  • Loading branch information
stevenhorsman authored Apr 8, 2024
2 parents 42936cb 864e9c2 commit 3242f55
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 46 deletions.
27 changes: 27 additions & 0 deletions src/agent/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions src/agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 26,7 @@ url = "2.5.0"
kata-sys-util = { path = "../libs/kata-sys-util" }
kata-types = { path = "../libs/kata-types" }
safe-path = { path = "../libs/safe-path" }
const_format = "0.2.30"

# Async helpers
async-trait = "0.1.42"
Expand All @@ -36,7 37,7 @@ futures = "0.3.30"
tokio = { version = "1.28.1", features = ["full"] }
tokio-vsock = "0.3.1"

netlink-sys = { version = "0.7.0", features = ["tokio_socket",]}
netlink-sys = { version = "0.7.0", features = ["tokio_socket"] }
rtnetlink = "0.8.0"
netlink-packet-utils = "0.4.1"
ipnetwork = "0.17.0"
Expand All @@ -62,13 63,15 @@ cgroups = { package = "cgroups-rs", version = "0.3.3" }
tracing = "0.1.26"
tracing-subscriber = "0.2.18"
tracing-opentelemetry = "0.13.0"
opentelemetry = { version = "0.14.0", features = ["rt-tokio-current-thread"]}
opentelemetry = { version = "0.14.0", features = ["rt-tokio-current-thread"] }
vsock-exporter = { path = "vsock-exporter" }

# Configuration
serde = { version = "1.0.129", features = ["derive"] }
toml = "0.5.8"
clap = { version = "3.0.1", features = ["derive"] }
strum = "0.26.2"
strum_macros = "0.26.2"

# Communication with the OPA service
http = { version = "0.2.8", optional = true }
Expand All @@ -87,16 90,14 @@ rstest = "0.18.0"
async-std = { version = "1.12.0", features = ["attributes"] }

[workspace]
members = [
"rustjail",
]
members = ["rustjail"]

[profile.release]
lto = true

[features]
# The default-pull feature would support all pull types, including sharing images by virtio-fs and pulling images in the guest
default-pull = [ "guest-pull" ]
default-pull = ["guest-pull"]
seccomp = ["rustjail/seccomp"]
standard-oci-runtime = ["rustjail/standard-oci-runtime"]
agent-policy = ["http", "openssl", "reqwest"]
Expand Down
11 changes: 6 additions & 5 deletions src/agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 116,7 @@ For further details, see the

## Agent options

The kata agent has the ability to configure agent options in guest kernel command line at runtime. Currently, the following agent options can be configured:
The kata agent has the ability to configure agent options in guest kernel command line at runtime. Currently, the following agent options can be configured:

| Option | Name | Description | Type | Default |
|-|-|:-:|:-:|:-:|
Expand All @@ -126,6 126,7 @@ The kata agent has the ability to configure agent options in guest kernel comman
| `agent.debug_console_vport` | Debug console port | Allow to specify the `vsock` port to connect the debugging console | integer | `0` |
| `agent.devmode` | Developer mode | Allow the agent process to coredump | boolean | `false` |
| `agent.hotplug_timeout` | Hotplug timeout | Allow to configure hotplug timeout(seconds) of block devices | integer | `3` |
| `agent.guest_components_rest_api` | `api-server-rest` configuration | Select the features that the API Server Rest attestation component will run with. Valid values are `all`, `attestation`, `resource`, or `none` to not launch the `api-server-rest` component | string | `resource` |
| `agent.https_proxy` | HTTPS proxy | Allow to configure `https_proxy` in the guest | string | `""` |
| `agent.log` | Log level | Allow the agent log level to be changed (produces more or less output) | string | `"info"` |
| `agent.log_vport` | Log port | Allow to specify the `vsock` port to read logs | integer | `0` |
Expand All @@ -136,7 137,7 @@ The kata agent has the ability to configure agent options in guest kernel comman
| `systemd.unified_cgroup_hierarchy` | `Cgroup hierarchy` | Allow to setup v2 cgroups | boolean | `false` |

> **Note:** Accepted values for some agent options
> - `agent.config_file`: If we enable `agent.config_file` in guest kernel command line,
> - `agent.config_file`: If we enable `agent.config_file` in guest kernel command line,
> we will generate the agent config from it.
> The agent will fail to start if the configuration file is not present,
> or if it can't be parsed properly.
Expand All @@ -147,11 148,11 @@ The kata agent has the ability to configure agent options in guest kernel comman
> - `agent.trace`: true | false
> - `systemd.unified_cgroup_hierarchy`: true | false
For instance, you can enable the debug console and set the agent log level to debug by configuring the guest kernel command line in the configuration file:
For instance, you can enable the debug console and set the agent log level to debug by configuring the guest kernel command line in the configuration file:
```toml
kernel_params = "agent.debug_console agent.log=debug agent.hotplug_timeout=10"
kernel_params = "agent.debug_console agent.log=debug agent.hotplug_timeout=10"
```
results in:
```bash
{"msg":"announce","level":"INFO","subsystem":"root","version":"0.1.0","pid":"214","source":"agent","name":"kata-agent","config":"AgentConfig { debug_console: true, dev_mode: false, log_level: Debug, hotplug_timeout: 10s, debug_console_vport: 0, log_vport: 0, container_pipe_size: 0, server_addr: "vsock://-1:1024", passfd_listener_port: 0, unified_cgroup_hierarchy: false, tracing: false, supports_seccomp: true }","agent-version":"3.3.0-alpha0"}
```
```
134 changes: 132 additions & 2 deletions src/agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 3,13 @@
// SPDX-License-Identifier: Apache-2.0
//
use crate::rpc;
use anyhow::{bail, ensure, Context, Result};
use anyhow::{anyhow, bail, ensure, Context, Result};
use serde::Deserialize;
use std::env;
use std::fs;
use std::str::FromStr;
use std::time;
use strum_macros::{Display, EnumString};
use tracing::instrument;
use url::Url;

Expand All @@ -26,6 27,7 @@ const LOG_VPORT_OPTION: &str = "agent.log_vport";
const CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size";
const UNIFIED_CGROUP_HIERARCHY_OPTION: &str = "systemd.unified_cgroup_hierarchy";
const CONFIG_FILE: &str = "agent.config_file";
const GUEST_COMPONENTS_REST_API_OPTION: &str = "agent.guest_components_rest_api";

// Configure the proxy settings for HTTPS requests in the guest,
// to solve the problem of not being able to access the specified image in some cases.
Expand All @@ -48,7 50,6 @@ const ERR_INVALID_GET_VALUE_PARAM: &str = "expected name=value";
const ERR_INVALID_GET_VALUE_NO_NAME: &str = "name=value parameter missing name";
const ERR_INVALID_GET_VALUE_NO_VALUE: &str = "name=value parameter missing value";
const ERR_INVALID_LOG_LEVEL_KEY: &str = "invalid log level key name";

const ERR_INVALID_HOTPLUG_TIMEOUT: &str = "invalid hotplug timeout parameter";
const ERR_INVALID_HOTPLUG_TIMEOUT_PARAM: &str = "unable to parse hotplug timeout";
const ERR_INVALID_HOTPLUG_TIMEOUT_KEY: &str = "invalid hotplug timeout key name";
Expand All @@ -58,6 59,19 @@ const ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM: &str = "unable to parse container p
const ERR_INVALID_CONTAINER_PIPE_SIZE_KEY: &str = "invalid container pipe size key name";
const ERR_INVALID_CONTAINER_PIPE_NEGATIVE: &str = "container pipe size should not be negative";

const ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE: &str = "invalid guest components rest api feature given. Valid values are `all`, `attestation`, `resource`, or `none`";

#[derive(Clone, Copy, Debug, Default, Display, Deserialize, EnumString, PartialEq)]
// Features seem to typically be in kebab-case format, but we only have single words at the moment
#[strum(serialize_all = "kebab-case")]
pub enum GuestComponentsFeatures {
All,
Attestation,
None,
#[default]
Resource,
}

#[derive(Debug)]
pub struct AgentConfig {
pub debug_console: bool,
Expand All @@ -74,6 88,7 @@ pub struct AgentConfig {
pub supports_seccomp: bool,
pub https_proxy: String,
pub no_proxy: String,
pub guest_components_rest_api: GuestComponentsFeatures,
}

#[derive(Debug, Deserialize)]
Expand All @@ -91,6 106,7 @@ pub struct AgentConfigBuilder {
pub tracing: Option<bool>,
pub https_proxy: Option<String>,
pub no_proxy: Option<String>,
pub guest_components_rest_api: Option<GuestComponentsFeatures>,
}

macro_rules! config_override {
Expand Down Expand Up @@ -154,6 170,7 @@ impl Default for AgentConfig {
supports_seccomp: rpc::have_seccomp(),
https_proxy: String::from(""),
no_proxy: String::from(""),
guest_components_rest_api: GuestComponentsFeatures::default(),
}
}
}
Expand Down Expand Up @@ -185,6 202,11 @@ impl FromStr for AgentConfig {
config_override!(agent_config_builder, agent_config, tracing);
config_override!(agent_config_builder, agent_config, https_proxy);
config_override!(agent_config_builder, agent_config, no_proxy);
config_override!(
agent_config_builder,
agent_config,
guest_components_rest_api
);

Ok(agent_config)
}
Expand Down Expand Up @@ -286,6 308,12 @@ impl AgentConfig {
);
parse_cmdline_param!(param, HTTPS_PROXY, config.https_proxy, get_url_value);
parse_cmdline_param!(param, NO_PROXY, config.no_proxy, get_string_value);
parse_cmdline_param!(
param,
GUEST_COMPONENTS_REST_API_OPTION,
config.guest_components_rest_api,
get_guest_components_features_value
);
}

if let Ok(addr) = env::var(SERVER_ADDR_ENV_VAR) {
Expand Down Expand Up @@ -439,6 467,19 @@ fn get_url_value(param: &str) -> Result<String> {
Ok(Url::parse(&value)?.to_string())
}

#[instrument]
fn get_guest_components_features_value(param: &str) -> Result<GuestComponentsFeatures> {
let fields: Vec<&str> = param.split('=').collect();
ensure!(fields.len() >= 2, ERR_INVALID_GET_VALUE_PARAM);

// We need name (but the value can be blank)
ensure!(!fields[0].is_empty(), ERR_INVALID_GET_VALUE_NO_NAME);

let value = fields[1..].join("=");
GuestComponentsFeatures::from_str(&value)
.map_err(|_| anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE))
}

#[cfg(test)]
mod tests {
use test_utils::assert_result;
Expand Down Expand Up @@ -477,6 518,7 @@ mod tests {
tracing: bool,
https_proxy: &'a str,
no_proxy: &'a str,
guest_components_rest_api: GuestComponentsFeatures,
}

impl Default for TestData<'_> {
Expand All @@ -494,6 536,7 @@ mod tests {
tracing: false,
https_proxy: "",
no_proxy: "",
guest_components_rest_api: GuestComponentsFeatures::default(),
}
}
}
Expand Down Expand Up @@ -883,6 926,26 @@ mod tests {
no_proxy: "192.168.1.0/24,172.16.0.0/12",
..Default::default()
},
TestData {
contents: "agent.guest_components_rest_api=attestation",
guest_components_rest_api: GuestComponentsFeatures::Attestation,
..Default::default()
},
TestData {
contents: "agent.guest_components_rest_api=resource",
guest_components_rest_api: GuestComponentsFeatures::Resource,
..Default::default()
},
TestData {
contents: "agent.guest_components_rest_api=all",
guest_components_rest_api: GuestComponentsFeatures::All,
..Default::default()
},
TestData {
contents: "agent.guest_components_rest_api=none",
guest_components_rest_api: GuestComponentsFeatures::None,
..Default::default()
},
];

let dir = tempdir().expect("failed to create tmpdir");
Expand Down Expand Up @@ -932,6 995,11 @@ mod tests {
assert_eq!(d.tracing, config.tracing, "{}", msg);
assert_eq!(d.https_proxy, config.https_proxy, "{}", msg);
assert_eq!(d.no_proxy, config.no_proxy, "{}", msg);
assert_eq!(
d.guest_components_rest_api, config.guest_components_rest_api,
"{}",
msg
);

for v in vars_to_unset {
env::remove_var(v);
Expand Down Expand Up @@ -1403,6 1471,68 @@ Caused by:
}
}

#[test]
fn test_get_guest_components_features_value() {
#[derive(Debug)]
struct TestData<'a> {
param: &'a str,
result: Result<GuestComponentsFeatures>,
}

let tests = &[
TestData {
param: "",
result: Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)),
},
TestData {
param: "=",
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)),
},
TestData {
param: "==",
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)),
},
TestData {
param: "x=all",
result: Ok(GuestComponentsFeatures::All),
},
TestData {
param: "x=attestation",
result: Ok(GuestComponentsFeatures::Attestation),
},
TestData {
param: "x=none",
result: Ok(GuestComponentsFeatures::None),
},
TestData {
param: "x=resource",
result: Ok(GuestComponentsFeatures::Resource),
},
TestData {
param: "x===",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
TestData {
param: "x==x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
TestData {
param: "x=x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
];

for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);

let result = get_guest_components_features_value(d.param);

let msg = format!("{}: result: {:?}", msg, result);

assert_result!(d.result, result, msg);
}
}

#[test]
fn test_config_builder_from_string() {
let config = AgentConfig::from_str(
Expand Down
Loading

0 comments on commit 3242f55

Please sign in to comment.