Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions ic-os/bootloader/guestos_boot_args.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
# useful for debug and policy development.
BOOT_ARGS_A="root=/dev/disk/by-partuuid/7c0a626e-e5ea-e543-b5c5-300eb8304db7 console=ttyS0 console=tty0 nomodeset dfinity.system=A security=selinux selinux=1 enforcing=1 root_hash=ROOT_HASH"
BOOT_ARGS_B="root=/dev/disk/by-partuuid/a78bc3a8-376c-054a-96e7-3904b915d0c5 console=ttyS0 console=tty0 nomodeset dfinity.system=B security=selinux selinux=1 enforcing=1 root_hash=ROOT_HASH"
BOOT_ARGS_TEE_A="root=/dev/disk/by-partuuid/7c0a626e-e5ea-e543-b5c5-300eb8304db7 console=ttyS0 console=tty0 nomodeset dfinity.system=A dfinity.tee=1 security=selinux selinux=1 enforcing=1 root_hash=ROOT_HASH"
BOOT_ARGS_TEE_B="root=/dev/disk/by-partuuid/a78bc3a8-376c-054a-96e7-3904b915d0c5 console=ttyS0 console=tty0 nomodeset dfinity.system=B dfinity.tee=1 security=selinux selinux=1 enforcing=1 root_hash=ROOT_HASH"
16 changes: 4 additions & 12 deletions ic-os/components/guestos/environment/90-sev-status.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
#!/bin/bash

# Set SEV_ACTIVE environment variable based on systemd-detect-virt output.
# SEV detection involves querying the CPU using cpuid which goes through the HostOS.
# A malicious HostOS could intercept this call, but by querying the CPU only once early
# in the boot process and setting an environment variable, each service in the GuestOS
# will see the same value. Therefore, faking the initial cpuid is not an attack vector,
# as it would not allow selective manipulation of SEV status between services — it is
# equivalent of enabling/disabling SEV in the GuestOS VM config.

# We only support SEV-SNP and no older SEV variants (sev, sev-es)
if [ "$(systemd-detect-virt --cvm)" = "sev-snp" ]; then
echo "SEV_ACTIVE=1"
# Set TEE_ENABLED environment variable based on kernel command line.
if grep -qE "(^|[[:space:]])dfinity\.tee(=|[[:space:]]|$)" /proc/cmdline; then
echo "TEE_ENABLED=1"
else
echo "SEV_ACTIVE=0"
echo "TEE_ENABLED=0"
fi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ After=init-config.service network-online.target dev-sev\x2dguest.device
Requires=init-config.service
Wants=network-online.target dev-sev\x2dguest.device
RequiresMountsFor=/var
ConditionEnvironment=SEV_ACTIVE=1
ConditionKernelCommandLine=dfinity.tee

[Service]
Type=oneshot
Expand Down
2 changes: 1 addition & 1 deletion ic-os/components/monitoring/guestos/custom-metrics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function update_tee_metrics() {
"guestos_tee_active" \
"Indicates whether the virtual machine is running in a Trusted Execution Environment (1 = TEE enabled, 0 = not in TEE)" \
"gauge"
append_metric "$metric_family" "guestos_tee_active" "" "$SEV_ACTIVE"
append_metric "$metric_family" "guestos_tee_active" "" "$TEE_ENABLED"
}

function main() {
Expand Down
3 changes: 2 additions & 1 deletion ic-os/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ def icos_build(
# Create GuestLaunchMeasurements JSON for each CPU generation, vCPU count, and boot slot
(for vcpu_flag in """ + vcpu_type_flags + """; do
for vcpus in """ + vcpu_configs + """; do
for cmdline in "$$BOOT_ARGS_A" "$$BOOT_ARGS_B"; do
# Note: we only add TEE boot args to the launch measurements
for cmdline in "$$BOOT_ARGS_TEE_A" "$$BOOT_ARGS_TEE_B"; do
hex=$$($(execpath //ic-os:sev-snp-measure) --mode snp --vcpus $$vcpus --ovmf "$(execpath //ic-os/components/ovmf:ovmf_sev)" $$vcpu_flag --append "$$cmdline" --initrd "$(location extracted_initrd.img)" --kernel "$(location extracted_vmlinuz)")
# Convert hex string to decimal list, e.g. "abcd" -> 171\\n205
measurement=$$(echo -n "$$hex" | fold -w2 | sed "s/^/0x/" | xargs printf "%d\n")
Expand Down
29 changes: 17 additions & 12 deletions rs/ic_os/config/tool/src/guestos/bootstrap_ic_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,20 @@ fn populate_nns_public_key_impl(
/// Bootstrap IC Node from a bootstrap package
#[cfg(target_os = "linux")]
pub fn bootstrap_ic_node(bootstrap_dir: &Path, guestos_config: GuestOSConfig) -> Result<()> {
let is_sev_active = sev_guest::is_sev_active()?;
bootstrap_ic_node_impl(bootstrap_dir, Path::new("/"), guestos_config, is_sev_active)
let is_tee_enabled = sev_guest::is_tee_enabled()?;
bootstrap_ic_node_impl(
bootstrap_dir,
Path::new("/"),
guestos_config,
is_tee_enabled,
)
}

fn bootstrap_ic_node_impl(
bootstrap_dir: &Path,
root: &Path,
guestos_config: GuestOSConfig,
is_sev_active: bool,
is_tee_enabled: bool,
) -> Result<()> {
let config_root = root.join(CONFIG_ROOT_PATH);
let state_root = root.join(STATE_ROOT_PATH);
Expand All @@ -106,7 +111,7 @@ fn bootstrap_ic_node_impl(
&config_root,
&state_root,
guestos_config,
is_sev_active,
is_tee_enabled,
)
.context("bootstrap failed")?;

Expand All @@ -123,9 +128,9 @@ fn process_bootstrap(
config_root: &Path,
state_root: &Path,
guestos_config: GuestOSConfig,
is_sev_active: bool,
is_tee_enabled: bool,
) -> Result<()> {
copy_bootstrap_files(bootstrap_dir, config_root, state_root, is_sev_active)?;
copy_bootstrap_files(bootstrap_dir, config_root, state_root, is_tee_enabled)?;

// Write the node operator key if it was configured
let node_op_key_dst = state_root.join(NODE_OPERATOR_KEY_PATH);
Expand Down Expand Up @@ -180,7 +185,7 @@ fn copy_bootstrap_files(
bootstrap_dir: &Path,
config_root: &Path,
state_root: &Path,
is_sev_active: bool,
is_tee_enabled: bool,
) -> Result<()> {
// set up initial ssh authorized keys
let ssh_keys_src = bootstrap_dir.join("accounts_ssh_authorized_keys");
Expand All @@ -191,7 +196,7 @@ fn copy_bootstrap_files(
}

// Restrict state injection on SEV production nodes
if is_sev_active {
if is_tee_enabled {
#[cfg(not(feature = "dev"))]
{
println!("SEV is active - blocking state injection files for production variant");
Expand Down Expand Up @@ -361,7 +366,7 @@ mod tests {
bootstrap_dir.path(),
test_root.root_path(),
guestos_config,
/*is_sev_active*/ false,
/*is_tee_enabled*/ false,
);
assert!(result.is_ok());

Expand Down Expand Up @@ -416,7 +421,7 @@ mod tests {
bootstrap_dir.path(),
test_root.root_path(),
guestos_config,
/*is_sev_active*/ false,
/*is_tee_enabled*/ false,
)
.unwrap();

Expand Down Expand Up @@ -575,7 +580,7 @@ mod tests {
bootstrap_dir.path(),
test_root.root_path(),
GuestOSConfig::default(),
/*is_sev_active*/ false,
/*is_tee_enabled*/ false,
)
.unwrap();

Expand All @@ -600,7 +605,7 @@ mod tests {

#[test]
#[cfg(not(feature = "dev"))]
fn test_sev_active_prod_state_injection_blocked() {
fn test_tee_enabled_prod_state_injection_blocked() {
// Create extracted directory structure
let temp_dir = TempDir::new().unwrap();
let extracted_dir = temp_dir.path().join("extracted");
Expand Down
5 changes: 3 additions & 2 deletions rs/ic_os/config/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,9 @@ pub struct ICOSSettings {
/// SEV-SNP.
///
/// IMPORTANT: This field only controls whether TEE is enabled in config.
/// In GuestOS code, check the $SEV_ACTIVE environment variable or use the `is_sev_active()`
/// wrapper from the `ic_sev` crate, as this cannot be faked by a malicious HostOS.
/// In GuestOS code, check the dfinity.tee kernel command line argument or the `TEE_ENABLED`
/// environment variable, or use the `is_tee_enabled()` wrapper from the `ic_sev` crate, as this
/// cannot be faked by a malicious HostOS.
#[serde(default)]
pub enable_trusted_execution_environment: bool,
/// This ssh keys directory contains individual files named `admin`, `backup`, `readonly`, `recovery`.
Expand Down
6 changes: 3 additions & 3 deletions rs/ic_os/guest_upgrade/server/src/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use {
config_tool::{DEFAULT_GUESTOS_CONFIG_OBJECT_PATH, deserialize_config},
config_types::GuestOSConfig,
sev::firmware::guest::Firmware,
sev_guest::is_sev_active,
sev_guest::is_tee_enabled,
vsock_lib::LinuxVSockClient,
};

Expand All @@ -20,12 +20,12 @@ pub fn new_disk_encryption_key_exchange_server_agent_for_orchestrator(
handle: Handle,
registry_client: Arc<dyn RegistryClient>,
) -> Option<DiskEncryptionKeyExchangeServerAgent> {
let is_sev_active = is_sev_active().unwrap_or_else(|err| {
let is_tee_enabled = is_tee_enabled().unwrap_or_else(|err| {
eprintln!("Failed to check if SEV is active, assuming it is not active: {err:?}");
false
});

if !is_sev_active {
if !is_tee_enabled {
return None;
}

Expand Down
6 changes: 3 additions & 3 deletions rs/ic_os/os_tools/guest_disk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fn main() -> Result<()> {
run(
args,
&guestos_config,
sev_guest::is_sev_active().context("Failed to check if SEV is active")?,
sev_guest::is_tee_enabled().context("Failed to check if SEV is active")?,
|| {
::sev::firmware::guest::Firmware::open()
.context("Failed to open /dev/sev-guest")
Expand All @@ -74,7 +74,7 @@ fn main() -> Result<()> {
fn run(
args: Args,
guestos_config: &GuestOSConfig,
is_sev_active: bool,
is_tee_enabled: bool,
sev_firmware_factory: impl Fn() -> Result<Box<dyn SevGuestFirmware>>,
previous_key_path: &Path,
generated_key_path: &Path,
Expand All @@ -83,7 +83,7 @@ fn run(
libcryptsetup_rs::set_log_callback::<()>(Some(cryptsetup_log), None);

let metrics_file = metrics_file_path(metrics_dir, args.partition());
let mut encryption: Box<dyn DiskEncryption> = if is_sev_active {
let mut encryption: Box<dyn DiskEncryption> = if is_tee_enabled {
Box::new(SevDiskEncryption {
sev_firmware: sev_firmware_factory().context("Failed to open SEV firmware")?,
guest_vm_type: guestos_config.guest_vm_type,
Expand Down
4 changes: 2 additions & 2 deletions rs/ic_os/remote_attestation/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use remote_attestation_shared::proto::{AttestRequest, AttestResponse};
use sev::firmware::guest::Firmware;
use sev_guest::attestation_package::generate_attestation_package;
use sev_guest::firmware::SevGuestFirmware;
use sev_guest::is_sev_active;
use sev_guest::is_tee_enabled;
use std::net::{IpAddr, SocketAddr};
use std::sync::{Arc, Mutex};
use tonic::transport::Server;
Expand Down Expand Up @@ -86,7 +86,7 @@ impl RemoteAttestationService for RemoteAttestationServiceImpl {
#[cfg(target_os = "linux")]
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let service_impl = if is_sev_active()? {
let service_impl = if is_tee_enabled()? {
let guestos_config: GuestOSConfig = deserialize_config(DEFAULT_GUESTOS_CONFIG_OBJECT_PATH)
.context("Failed to read GuestOS config")?;

Expand Down
1 change: 1 addition & 0 deletions rs/ic_os/sev/guest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ rust_library(
crate_name = "sev_guest",
deps = [
"//rs/ic_os/config/types:config_types",
"//rs/ic_os/linux_kernel_command_line",
"//rs/ic_os/sev/attestation",
"//rs/ic_os/sev/guest/firmware",
"@crate_index//:anyhow",
Expand Down
1 change: 1 addition & 0 deletions rs/ic_os/sev/guest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ sha2 = { workspace = true }

attestation = { path = "../attestation" }
config_types = { path = "../../config/types" }
linux_kernel_command_line = { path = "../../linux_kernel_command_line" }
sev_guest_firmware = { path = "firmware" }

[dev-dependencies]
Expand Down
42 changes: 23 additions & 19 deletions rs/ic_os/sev/guest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(target_os = "linux")]
use anyhow::{Context, Result, anyhow};
use anyhow::{Context, Result};
use linux_kernel_command_line::KernelCommandLine;
use std::str::FromStr;

pub mod attestation_package;
pub mod key_deriver;
Expand All @@ -9,22 +10,25 @@ pub mod key_deriver;
pub use sev_guest_firmware as firmware;

/// Checks if SEV is active in the Guest Virtual Machine
#[cfg(target_os = "linux")]
pub fn is_sev_active() -> Result<bool> {
// We read the environment variable set by systemd instead of alternatives:
// - /dev/sev-guest: This device may not be available early in the boot process even when SEV is
// active.
// - cpuid: The call goes through the Host. If we invoked cpuid on every check, a malicious
// host could intercept the call and return different values thereby making some processes
// believe that the SEV is active and others believe it is not.
match std::env::var("SEV_ACTIVE")
.context("Could not read SEV_ACTIVE environment variable")?
.as_ref()
{
"1" => Ok(true),
"0" => Ok(false),
other => Err(anyhow!(
"SEV_ACTIVE was expected to be 0 or 1 but was: '{other}'"
)),
pub fn is_tee_enabled() -> Result<bool> {
let cmdline = std::fs::read_to_string("/proc/cmdline")
.context("Could not read kernel command line from /proc/cmdline")?;
is_tee_enabled_impl(&cmdline)
}

fn is_tee_enabled_impl(cmdline: &str) -> Result<bool> {
let kernel_command_line =
KernelCommandLine::from_str(cmdline).context("Could not parse kernel command line")?;
Ok(kernel_command_line.get_argument("dfinity.tee").is_some())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_is_tee_enabled() {
assert!(is_tee_enabled_impl("root=/dev/disk/by-partuuid/7c0a626e-e5ea-e543-b5c5-300eb8304db7 console=ttyS0 console=tty0 nomodeset dfinity.system=A dfinity.tee=1 security=selinux selinux=1 enforcing=1 root_hash=abc1234").unwrap());
assert!(!is_tee_enabled_impl("root=/dev/disk/by-partuuid/a78bc3a8-376c-054a-96e7-3904b915d0c5 console=ttyS0 console=tty0 nomodeset dfinity.system=B security=selinux selinux=1 enforcing=1 root_hash=12345").unwrap());
}
}
6 changes: 3 additions & 3 deletions rs/orchestrator/src/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -854,10 +854,10 @@ fn generate_node_registration_attestation(
use der::asn1::OctetStringRef;
use sev::firmware::guest::Firmware;
use sev_guest::attestation_package::generate_attestation_package;
use sev_guest::is_sev_active;
use sev_guest::is_tee_enabled;

// Check if SEV is active
let is_sev_active = match is_sev_active() {
let is_tee_enabled = match is_tee_enabled() {
Ok(active) => active,
Err(err) => {
info!(
Expand All @@ -868,7 +868,7 @@ fn generate_node_registration_attestation(
}
};

if !is_sev_active {
if !is_tee_enabled {
info!(
log,
"SEV is not active, skipping node registration attestation"
Expand Down
Loading