From cf8cd3035df192123d88748536cad75670274ddd Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 29 Aug 2022 19:47:44 +0200 Subject: [PATCH 1/9] Rename the configuration options Follow the naming specified in the configuration enhancement: https://github.com/keylime/enhancements/blob/master/72_config_and_simplify_tls.md Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent.conf | 90 +++++++------ src/common.rs | 308 ++++++++++++++++++++------------------------- src/main.rs | 103 ++++++++------- src/revocation.rs | 18 +-- 4 files changed, 241 insertions(+), 278 deletions(-) diff --git a/keylime-agent.conf b/keylime-agent.conf index 1ebf4cc01..1446c21ac 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -1,37 +1,43 @@ #============================================================================= -[general] +[agent] #============================================================================= -# Revocation IP & Port used by either the cloud_agent or keylime_ca to receive -# revocation events from the verifier. -receive_revocation_ip = 127.0.0.1 -receive_revocation_port = 8992 - - -#============================================================================= -[cloud_agent] -#============================================================================= +# The agent's UUID. +# Set to "openstack", it will try to get the UUID from the metadata service. +# If you set this to "generate", Keylime will create a random UUID. +# If you set this to "hash_ek", Keylime will set the UUID to the result +# of 'SHA256(public EK in PEM format)'. +# If you set this to "dmidecode", Keylime will use the UUID from +# 'dmidecode -s system-uuid'. +# If you set this to "hostname", Keylime will use the full qualified domain +# name of current host as the agent id. +uuid = d432fbb3-d2f1-4a97-9ef7-75bd81c00000 # The binding address and port for the agent server -cloudagent_ip = 127.0.0.1 -cloudagent_port = 9002 +ip = 127.0.0.1 +port = 9002 # Address and port where the verifier and tenant can connect to reach the agent. # These keys are optional. -agent_contact_ip = 127.0.0.1 -agent_contact_port = 9002 +contact_ip = 127.0.0.1 +contact_port = 9002 # The address and port of registrar server which agent communicate with registrar_ip = 127.0.0.1 registrar_port = 8890 +# Enable mTLS communication between agent, verifier and tenant. +# Details on why setting it to "False" is generally considered insecure can be found +# on https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r +enable_agent_mtls = True + # The keylime working directory. Can be overriden by setting the KEYLIME_DIR # environment variable. The default value is /var/lib/keylime -# keylime_dir = /var/lib/keylime +keylime_dir = /var/lib/keylime # The CA that signs the client certificates of the tenant and verifier. # If set to default it tries to use $keylime_dir/cv_ca/cacert.crt -keylime_ca = default +trusted_client_ca = default # The name that should be used for the encryption key, placed in the # $keylime_dir/secure/ directory. @@ -46,6 +52,10 @@ dec_payload_file = decrypted_payload # The default below sets it to 1 megabyte. secure_size = 1m +# Use this option to state the existing TPM ownerpassword. This option should +# be set only when ek_handle option points to an existing EK. +tpm_ownerpassword = + # Whether to allow the cloud_agent to automatically extract a zip file in # the delivered payload after it has been decrypted, or not. Defaults to "true". # After decryption, the archive will be unzipped to a directory in $keylime_dir/secure. @@ -53,19 +63,18 @@ secure_size = 1m # option will affect this. extract_payload_zip = True -# The agent's UUID. -# Set to "openstack", it will try to get the UUID from the metadata service. -# If you set this to "generate", Keylime will create a random UUID. -# If you set this to "hash_ek", Keylime will set the UUID to the result -# of 'SHA256(public EK in PEM format)'. -# If you set this to "dmidecode", Keylime will use the UUID from -# 'dmidecode -s system-uuid'. -# If you set this to "hostname", Keylime will use the full qualified domain -# name of current host as the agent id. -agent_uuid = d432fbb3-d2f1-4a97-9ef7-75bd81c00000 - # Whether to listen for revocation notifications from the verifier or not. -listen_notifications = True +enable_revocation_notifications = True + +# The path to the directory containing the pre-installed revocation action +# scripts. Ideally should point to an fixed/immutable location subject to +# attestation. The default is /usr/libexec/keylime. +revocation_actions_dir = /usr/libexec/keylime + +# Revocation IP & Port used by either the cloud_agent to receive revocation +# notifications from the verifier. +revocation_notification_ip = 127.0.0.1 +revocation_notification_port = 8992 # The path to the certificate to verify revocation messages received from the # verifier. The path is relative to $keylime_dir unless an absolute path is @@ -88,29 +97,18 @@ revocation_actions= # with a working directory of $keylime_dir/secure/unzipped. payload_script=autorun.sh -# The path to the directory containing the pre-installed revocation action -# scripts. Ideally should point to an fixed/immutable location subject to -# attestation. The default is /usr/libexec/keylime. -revocation_actions_dir = /usr/libexec/keylime +# In case mTLS for the agent is disabled and the use of payloads is still +# required, this option has to be set to "True" in order to allow the agent +# to start. Details on why this configuration (mTLS disabled and payload enabled) +# is generally considered insecure can be found on +# https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r +enable_insecure_payload = False # Whether to allow running revocation actions sent as part of the payload. The # default is True and setting as False will limit the revocation actions to the # pre-installed ones. allow_payload_revocation_actions = True -# Jason @henn made be do it! He wanted a way for Keylime to measure the -# delivered payload into a pcr of choice. -# Specify a PCR number to turn it on. -# Set to -1 or any negative or out of range PCR value to turn off. -measure_payload_pcr=-1 - -# How long to wait between failed attempts to communicate with the TPM in -# seconds. Floating point values are accepted here. -retry_interval = 1 - -# Integer number of retries to communicate with the TPM before giving up. -max_retries = 10 - # TPM2-specific options, allows customizing default algorithms to use. # Specify the default crypto algorithms to use with a TPM2 for this agent. # @@ -148,4 +146,4 @@ tpm_ownerpassword = # chown keylime /var/lib/keylime/cv_ca # chown keylime /var/lib/keylime/cv_ca/cacert.crt # -run_as = +run_as = keylime:tss diff --git a/src/common.rs b/src/common.rs index f803c1088..ff7b2b1e8 100644 --- a/src/common.rs +++ b/src/common.rs @@ -261,37 +261,37 @@ impl AgentData { #[derive(Clone, Debug)] pub(crate) struct KeylimeConfig { - pub agent_ip: String, - pub agent_port: String, + pub uuid: String, + pub ip: String, + pub port: String, + pub contact_ip: Option, + pub contact_port: Option, pub registrar_ip: String, pub registrar_port: String, - pub agent_uuid: String, - pub agent_contact_ip: Option, - pub agent_contact_port: Option, - pub hash_alg: HashAlgorithm, - pub enc_alg: EncryptionAlgorithm, - pub sign_alg: SignAlgorithm, - pub agent_data: Option, - pub agent_data_path: String, - pub run_revocation: bool, - pub revocation_cert: String, - pub revocation_ip: String, - pub revocation_port: String, + pub enable_agent_mtls: bool, + pub keylime_dir: String, + pub trusted_client_ca: String, + pub enc_keyname: String, + pub dec_payload_file: String, pub secure_size: String, - pub payload_script: String, - pub dec_payload_filename: String, - pub key_filename: String, + pub tpm_ownerpassword: Option, pub extract_payload_zip: bool, - pub keylime_ca_path: String, - pub revocation_actions: String, + pub enable_revocation_notifications: bool, pub revocation_actions_dir: String, - pub allow_payload_revocation_actions: bool, - pub work_dir: String, - pub mtls_enabled: bool, + pub revocation_notification_ip: String, + pub revocation_notification_port: String, + pub revocation_cert: String, + pub revocation_actions: String, + pub payload_script: String, pub enable_insecure_payload: bool, - pub run_as: Option, - pub tpm_ownerpassword: Option, + pub allow_payload_revocation_actions: bool, + pub tpm_hash_alg: HashAlgorithm, + pub tpm_encryption_alg: EncryptionAlgorithm, + pub tpm_signing_alg: SignAlgorithm, pub ek_handle: Option, + pub run_as: Option, + pub agent_data: Option, + pub agent_data_path: String, } impl KeylimeConfig { @@ -308,121 +308,99 @@ impl KeylimeConfig { } }; - let agent_ip = config_get_env( + let ip = config_get_env( &conf_name, &conf, - "cloud_agent", - "cloudagent_ip", + "agent", + "ip", "CLOUDAGENT_IP", )?; - let agent_port = config_get_env( + let port = config_get_env( &conf_name, &conf, - "cloud_agent", - "cloudagent_port", + "agent", + "port", "CLOUDAGENT_PORT", )?; let registrar_ip = config_get_env( &conf_name, &conf, - "cloud_agent", + "agent", "registrar_ip", "REGISTRAR_IP", )?; let registrar_port = config_get_env( &conf_name, &conf, - "cloud_agent", + "agent", "registrar_port", "REGISTRAR_PORT", )?; let agent_uuid_config = - config_get(&conf_name, &conf, "cloud_agent", "agent_uuid")?; - let agent_uuid = get_uuid(&agent_uuid_config); - let agent_contact_ip = cloudagent_contact_ip_get(&conf_name, &conf); - let agent_contact_port = - cloudagent_contact_port_get(&conf_name, &conf)?; - let hash_alg = HashAlgorithm::try_from( - config_get(&conf_name, &conf, "cloud_agent", "tpm_hash_alg")? - .as_str(), + config_get(&conf_name, &conf, "agent", "uuid")?; + let uuid = get_uuid(&agent_uuid_config); + let contact_ip = cloudagent_contact_ip_get(&conf_name, &conf); + let contact_port = cloudagent_contact_port_get(&conf_name, &conf)?; + let tpm_hash_alg = HashAlgorithm::try_from( + config_get(&conf_name, &conf, "agent", "tpm_hash_alg")?.as_str(), )?; - let enc_alg = EncryptionAlgorithm::try_from( - config_get( - &conf_name, - &conf, - "cloud_agent", - "tpm_encryption_alg", - )? - .as_str(), + let tpm_encryption_alg = EncryptionAlgorithm::try_from( + config_get(&conf_name, &conf, "agent", "tpm_encryption_alg")? + .as_str(), )?; - let sign_alg = SignAlgorithm::try_from( - config_get(&conf_name, &conf, "cloud_agent", "tpm_signing_alg")? + let tpm_signing_alg = SignAlgorithm::try_from( + config_get(&conf_name, &conf, "agent", "tpm_signing_alg")? .as_str(), )?; - // There was a typo in Python Keylime and this accounts for having a version - // of Keylime installed that still has this typo. TODO: Remove later - let run_revocation = bool::from_str( + let enable_revocation_notifications = bool::from_str( &config_get( &conf_name, &conf, - "cloud_agent", - "listen_notifications", - ) - .or_else(|_| { - config_get( - &conf_name, - &conf, - "cloud_agent", - "listen_notfications", - ) - })? + "agent", + "enable_revocation_notifications", + )? .to_lowercase(), )?; let revocation_cert = - config_get(&conf_name, &conf, "cloud_agent", "revocation_cert")?; - let revocation_ip = config_get( + config_get(&conf_name, &conf, "agent", "revocation_cert")?; + let revocation_notification_ip = config_get( &conf_name, &conf, - "general", - "receive_revocation_ip", + "agent", + "revocation_notification_ip", )?; - let revocation_port = config_get( + let revocation_notification_port = config_get( &conf_name, &conf, - "general", - "receive_revocation_port", + "agent", + "revocation_notification_port", )?; let secure_size = - config_get(&conf_name, &conf, "cloud_agent", "secure_size")?; + config_get(&conf_name, &conf, "agent", "secure_size")?; let payload_script = - config_get(&conf_name, &conf, "cloud_agent", "payload_script")?; - let dec_payload_filename = - config_get(&conf_name, &conf, "cloud_agent", "dec_payload_file")?; + config_get(&conf_name, &conf, "agent", "payload_script")?; + let dec_payload_file = + config_get(&conf_name, &conf, "agent", "dec_payload_file")?; - let key_filename = - config_get(&conf_name, &conf, "cloud_agent", "enc_keyname")?; + let enc_keyname = + config_get(&conf_name, &conf, "agent", "enc_keyname")?; let extract_payload_zip = bool::from_str( - &config_get( - &conf_name, - &conf, - "cloud_agent", - "extract_payload_zip", - )? - .to_lowercase(), + &config_get(&conf_name, &conf, "agent", "extract_payload_zip")? + .to_lowercase(), )?; - let work_dir = config_get_env( + let keylime_dir = config_get_env( &conf_name, &conf, - "cloud_agent", + "agent", "keylime_dir", "KEYLIME_DIR", ) .or_else::(|_| Ok(String::from(WORK_DIR)))?; - let agent_data_path = PathBuf::from(&work_dir).join(AGENT_DATA); + let agent_data_path = PathBuf::from(&keylime_dir).join(AGENT_DATA); let agent_data = if agent_data_path.exists() { match AgentData::load(&agent_data_path) { Ok(data) => Some(data), @@ -439,32 +417,24 @@ impl KeylimeConfig { None }; - let mut keylime_ca_path = - config_get(&conf_name, &conf, "cloud_agent", "keylime_ca")?; - if keylime_ca_path == "default" { - keylime_ca_path = Path::new(&work_dir) + let mut trusted_client_ca = + config_get(&conf_name, &conf, "agent", "trusted_client_ca")?; + if trusted_client_ca == "default" { + trusted_client_ca = Path::new(&keylime_dir) .join(DEFAULT_CA_PATH) .display() .to_string(); } - let revocation_actions = config_get( - &conf_name, - &conf, - "cloud_agent", - "revocation_actions", - ) - .or_else::(|_| Ok(String::from(REV_ACTIONS)))?; - let revocation_actions_dir = config_get( - &conf_name, - &conf, - "cloud_agent", - "revocation_actions_dir", - ) - .or_else::(|_| Ok(String::from(REV_ACTIONS_DIR)))?; + let revocation_actions = + config_get(&conf_name, &conf, "agent", "revocation_actions") + .or_else::(|_| Ok(String::from(REV_ACTIONS)))?; + let revocation_actions_dir = + config_get(&conf_name, &conf, "agent", "revocation_actions_dir") + .or_else::(|_| Ok(String::from(REV_ACTIONS_DIR)))?; let allow_payload_revocation_actions = match config_get( &conf_name, &conf, - "cloud_agent", + "agent", "allow_payload_revocation_actions", ) { Ok(s) => bool::from_str(&s.to_lowercase())?, @@ -472,17 +442,17 @@ impl KeylimeConfig { }; let run_as = if permissions::get_euid() == 0 { - match config_get(&conf_name, &conf, "cloud_agent", "run_as") { + match config_get(&conf_name, &conf, "agent", "run_as") { Ok(user_group) => { if user_group.is_empty() { - warn!("Cannot drop privileges since 'run_as' is empty in 'cloud_agent' section of keylime-agent.conf."); + warn!("Cannot drop privileges since 'run_as' is empty in 'agent' section of keylime-agent.conf."); None } else { Some(user_group) } } Err(_) => { - warn!("Cannot drop privileges since 'run_as' is missing in 'cloud_agent' section of keylime-agent.conf."); + warn!("Cannot drop privileges since 'run_as' is missing in 'agent' section of keylime-agent.conf."); None } } @@ -490,21 +460,18 @@ impl KeylimeConfig { None }; - let mtls_enabled = match config_get( - &conf_name, - &conf, - "cloud_agent", - "mtls_cert_enabled", - ) { - Ok(enabled) => bool::from_str(&enabled.to_lowercase()) - .or::(Ok(MTLS_ENABLED))?, - Err(_) => true, - }; + let enable_agent_mtls = + match config_get(&conf_name, &conf, "agent", "enable_agent_mtls") + { + Ok(enabled) => bool::from_str(&enabled.to_lowercase()) + .or::(Ok(MTLS_ENABLED))?, + Err(_) => true, + }; let enable_insecure_payload = match config_get( &conf_name, &conf, - "cloud_agent", + "agent", "enable_insecure_payload", ) { Ok(allowed) => bool::from_str(&allowed.to_lowercase()) @@ -513,47 +480,46 @@ impl KeylimeConfig { }; let tpm_ownerpassword = - config_get(&conf_name, &conf, "cloud_agent", "tpm_ownerpassword") + config_get(&conf_name, &conf, "agent", "tpm_ownerpassword") .ok() .filter(|s| s != "generate"); - let ek_handle = - config_get(&conf_name, &conf, "cloud_agent", "ek_handle") - .ok() - .filter(|s| s != "generate"); + let ek_handle = config_get(&conf_name, &conf, "agent", "ek_handle") + .ok() + .filter(|s| s != "generate"); Ok(KeylimeConfig { - agent_ip, - agent_port, + uuid, + ip, + port, + contact_ip, + contact_port, registrar_ip, registrar_port, - agent_uuid, - agent_contact_ip, - agent_contact_port, - hash_alg, - enc_alg, - sign_alg, - agent_data, - agent_data_path: agent_data_path.display().to_string(), - run_revocation, - revocation_cert, - revocation_ip, - revocation_port, + enable_agent_mtls, + keylime_dir, + trusted_client_ca, + enc_keyname, + dec_payload_file, secure_size, - payload_script, - dec_payload_filename, - key_filename, + tpm_ownerpassword, extract_payload_zip, - keylime_ca_path, - revocation_actions, + enable_revocation_notifications, revocation_actions_dir, - allow_payload_revocation_actions, - work_dir, - mtls_enabled, + revocation_notification_ip, + revocation_notification_port, + revocation_cert, + revocation_actions, + payload_script, enable_insecure_payload, - run_as, - tpm_ownerpassword, + allow_payload_revocation_actions, + tpm_hash_alg, + tpm_encryption_alg, + tpm_signing_alg, ek_handle, + run_as, + agent_data, + agent_data_path: agent_data_path.display().to_string(), }) } @@ -566,7 +532,7 @@ impl KeylimeConfig { let pem = openssl_key.public_key_to_pem()?; let mut hash = hash(MessageDigest::sha256(), &pem)?; - self.agent_uuid = hex::encode(hash); + self.uuid = hex::encode(hash); Ok(()) } } @@ -583,36 +549,36 @@ impl Default for KeylimeConfig { }; KeylimeConfig { - agent_ip: "127.0.0.1".to_string(), - agent_port: "9002".to_string(), + ip: "127.0.0.1".to_string(), + port: "9002".to_string(), registrar_ip: "127.0.0.1".to_string(), registrar_port: "8890".to_string(), - agent_uuid: "d432fbb3-d2f1-4a97-9ef7-75bd81c00000".to_string(), - agent_contact_ip: Some("127.0.0.1".to_string()), - agent_contact_port: Some(9002), - hash_alg: HashAlgorithm::Sha256, - enc_alg: EncryptionAlgorithm::Rsa, - sign_alg: SignAlgorithm::RsaSsa, + uuid: "d432fbb3-d2f1-4a97-9ef7-75bd81c00000".to_string(), + contact_ip: Some("127.0.0.1".to_string()), + contact_port: Some(9002), + tpm_hash_alg: HashAlgorithm::Sha256, + tpm_encryption_alg: EncryptionAlgorithm::Rsa, + tpm_signing_alg: SignAlgorithm::RsaSsa, agent_data: None, agent_data_path: Path::new(WORK_DIR) .join(AGENT_DATA) .display() .to_string(), - run_revocation: true, + enable_revocation_notifications: true, revocation_cert: "default".to_string(), - revocation_ip: "127.0.0.1".to_string(), - revocation_port: "8992".to_string(), + revocation_notification_ip: "127.0.0.1".to_string(), + revocation_notification_port: "8992".to_string(), secure_size: "1m".to_string(), payload_script: "autorun.sh".to_string(), - dec_payload_filename: "decrypted_payload".to_string(), - key_filename: "derived_tci_key".to_string(), + dec_payload_file: "decrypted_payload".to_string(), + enc_keyname: "derived_tci_key".to_string(), extract_payload_zip: true, - keylime_ca_path: DEFAULT_CA_PATH.to_string(), + trusted_client_ca: DEFAULT_CA_PATH.to_string(), revocation_actions: "".to_string(), revocation_actions_dir: "/usr/libexec/keylime".to_string(), allow_payload_revocation_actions: true, - work_dir: WORK_DIR.to_string(), - mtls_enabled: true, + keylime_dir: WORK_DIR.to_string(), + enable_agent_mtls: true, enable_insecure_payload: false, run_as, tpm_ownerpassword: None, @@ -674,8 +640,8 @@ fn cloudagent_contact_ip_get(conf_name: &str, conf: &Ini) -> Option { match config_get_env( conf_name, conf, - "cloud_agent", - "agent_contact_ip", + "agent", + "contact_ip", "KEYLIME_AGENT_CONTACT_IP", ) { Ok(ip) => Some(ip), @@ -691,8 +657,8 @@ fn cloudagent_contact_port_get( match config_get_env( conf_name, conf, - "cloud_agent", - "agent_contact_port", + "agent", + "contact_port", "KEYLIME_AGENT_CONTACT_PORT", ) { Ok(port_str) => match port_str.parse::() { @@ -711,7 +677,7 @@ fn cloudagent_contact_port_get( * Return: Returns the matched key * * Example call: - * let port = common::config_get(conf_file_name, file_Ini,"general","cloudagent_port"); + * let port = common::config_get(conf_file_name, file_Ini,"agent","port"); */ fn config_get( conf_name: &str, diff --git a/src/main.rs b/src/main.rs index ba14ecc28..174b9cce8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,8 +144,8 @@ pub(crate) fn setup_unzipped( fs::remove_dir_all(&unzipped)?; } - let dec_payload_path = unzipped.join(&config.dec_payload_filename); - let key_path = unzipped.join(&config.key_filename); + let dec_payload_path = unzipped.join(&config.dec_payload_file); + let key_path = unzipped.join(&config.enc_keyname); fs::create_dir(&unzipped)?; @@ -177,7 +177,7 @@ pub(crate) fn write_out_key_and_payload( } // run a script (such as the init script, if any) and check the status -pub(crate) fn run(dir: &Path, script: &str, agent_uuid: &str) -> Result<()> { +pub(crate) fn run(dir: &Path, script: &str, uuid: &str) -> Result<()> { let script_path = dir.join(script); info!("Running script: {:?}", script_path); @@ -224,7 +224,7 @@ pub(crate) fn optional_unzip_payload( config: &KeylimeConfig, ) -> Result<()> { if config.extract_payload_zip { - let zipped_payload = &config.dec_payload_filename; + let zipped_payload = &config.dec_payload_file; let zipped_payload_path = unzipped.join(zipped_payload); info!("Unzipping payload {} to {:?}", &zipped_payload, unzipped); @@ -270,7 +270,7 @@ pub(crate) async fn run_encrypted_payload( } script => { info!("Payload init script indicated: {}", script); - run(&unzipped, script, config.agent_uuid.as_str())?; + run(&unzipped, script, config.uuid.as_str())?; } } @@ -317,7 +317,7 @@ async fn worker( mount: PathBuf, ) -> Result<()> { // Only run payload scripts if mTLS is enabled or 'enable_insecure_payload' option is set - if config.mtls_enabled || config.enable_insecure_payload { + if config.enable_agent_mtls || config.enable_insecure_payload { run_encrypted_payload( symm_key, symm_key_cvar, @@ -332,7 +332,7 @@ async fn worker( // If with-zmq feature is enabled, run the service listening for ZeroMQ messages #[cfg(feature = "with-zmq")] - if config.run_revocation { + if config.enable_revocation_notifications { return revocation::run_revocation_service(&config, &mount).await; } @@ -396,7 +396,7 @@ async fn main() -> Result<()> { // The agent cannot run when a payload script is defined, but mTLS is disabled and insecure // payloads are not explicitly enabled - if !&config.mtls_enabled + if !&config.enable_agent_mtls && !&config.enable_insecure_payload && !&config.payload_script.is_empty() { @@ -406,7 +406,7 @@ async fn main() -> Result<()> { return Err(Error::Configuration(message)); } - let work_dir = Path::new(&config.work_dir); + let work_dir = Path::new(&config.keylime_dir); let mount = secure_mount::mount(work_dir, &config.secure_size)?; // Drop privileges @@ -465,13 +465,13 @@ async fn main() -> Result<()> { // Gather EK values and certs let ek_result = tpm::create_ek( &mut ctx, - config.enc_alg.into(), + config.tpm_encryption_alg.into(), config.ek_handle.as_deref(), )?; // Try to load persistent Agent data let agent_data = config.agent_data.clone().and_then(|data| - match data.valid(config.hash_alg, config.sign_alg) { + match data.valid(config.tpm_hash_alg, config.tpm_signing_alg) { true => Some(data), false => { warn!( @@ -511,8 +511,8 @@ async fn main() -> Result<()> { let new_ak = tpm::create_ak( &mut ctx, ek_result.key_handle, - config.hash_alg.into(), - config.sign_alg.into(), + config.tpm_hash_alg.into(), + config.tpm_signing_alg.into(), )?; let ak_handle = tpm::load_ak(&mut ctx, ek_result.key_handle, &new_ak)?; @@ -520,11 +520,11 @@ async fn main() -> Result<()> { } }; - if config.agent_uuid == "hash_ek" { + if config.uuid == "hash_ek" { config.set_ek_uuid(ek_result.public.clone())?; } - info!("Agent UUID: {}", config.agent_uuid); + info!("Agent UUID: {}", config.uuid); // Generate key pair for secure transmission of u, v keys. The u, v // keys are two halves of the key used to decrypt the workload after @@ -542,14 +542,14 @@ async fn main() -> Result<()> { let cert: openssl::x509::X509; let mtls_cert; let ssl_context; - if config.mtls_enabled { + if config.enable_agent_mtls { let keylime_ca_cert = - match crypto::load_x509(Path::new(&config.keylime_ca_path)) { + match crypto::load_x509(Path::new(&config.trusted_client_ca)) { Ok(t) => Ok(t), Err(e) => { error!( "Certificate not installed: {}", - config.keylime_ca_path + config.trusted_client_ca ); Err(e) } @@ -558,9 +558,9 @@ async fn main() -> Result<()> { cert = match &agent_data { Some(data) => match data.get_mtls_cert()? { Some(cert) => cert, - None => crypto::generate_x509(&nk_priv, &config.agent_uuid)?, + None => crypto::generate_x509(&nk_priv, &config.uuid)?, }, - None => crypto::generate_x509(&nk_priv, &config.agent_uuid)?, + None => crypto::generate_x509(&nk_priv, &config.uuid)?, }; mtls_cert = Some(&cert); ssl_context = Some(crypto::generate_mtls_context( @@ -576,8 +576,8 @@ async fn main() -> Result<()> { // Store new AgentData let agent_data_new = AgentData::create( - config.hash_alg, - config.sign_alg, + config.tpm_hash_alg, + config.tpm_signing_alg, &ak, &nk_pub, &nk_priv, @@ -590,16 +590,16 @@ async fn main() -> Result<()> { let keyblob = registrar_agent::do_register_agent( &config.registrar_ip, &config.registrar_port, - &config.agent_uuid, + &config.uuid, &PublicBuffer::try_from(ek_result.public.clone())?.marshall()?, ek_result.ek_cert, &PublicBuffer::try_from(ak.public)?.marshall()?, mtls_cert, - config.agent_contact_ip.clone(), - config.agent_contact_port, + config.contact_ip.clone(), + config.contact_port, ) .await?; - info!("SUCCESS: Agent {} registered", config.agent_uuid); + info!("SUCCESS: Agent {} registered", config.uuid); let key = tpm::activate_credential( &mut ctx, @@ -612,20 +612,18 @@ async fn main() -> Result<()> { ctx.flush_context(ek_result.key_handle.into())?; } let mackey = base64::encode(key.value()); - let auth_tag = crypto::compute_hmac( - mackey.as_bytes(), - config.agent_uuid.as_bytes(), - )?; + let auth_tag = + crypto::compute_hmac(mackey.as_bytes(), config.uuid.as_bytes())?; let auth_tag = hex::encode(&auth_tag); registrar_agent::do_activate_agent( &config.registrar_ip, &config.registrar_port, - &config.agent_uuid, + &config.uuid, &auth_tag, ) .await?; - info!("SUCCESS: Agent {} activated", config.agent_uuid); + info!("SUCCESS: Agent {} activated", config.uuid); } let mut encr_payload = Vec::new(); @@ -650,10 +648,10 @@ async fn main() -> Result<()> { })?; let work_dir = - Path::new(&config.work_dir).canonicalize().map_err(|e| { + Path::new(&config.keylime_dir).canonicalize().map_err(|e| { Error::Configuration(format!( "Path {} set in keylime_dir not found: {}", - &config.work_dir, e + &config.keylime_dir, e )) })?; @@ -668,10 +666,10 @@ async fn main() -> Result<()> { payload_symm_key_cvar: symm_key_cvar_arc, encr_payload: encr_payload_arc, auth_tag: Mutex::new([0u8; AUTH_TAG_LEN]), - hash_alg: config.hash_alg, - enc_alg: config.enc_alg, - sign_alg: config.sign_alg, - agent_uuid: config.agent_uuid.clone(), + hash_alg: config.tpm_hash_alg, + enc_alg: config.tpm_encryption_alg, + sign_alg: config.tpm_signing_alg, + agent_uuid: config.uuid.clone(), revocation_cert, revocation_actions: config.revocation_actions.clone(), revocation_actions_dir: actions_dir, @@ -780,27 +778,21 @@ async fn main() -> Result<()> { .disable_signals(); let server; - if config.mtls_enabled && ssl_context.is_some() { + if config.enable_agent_mtls && ssl_context.is_some() { server = actix_server .bind_openssl( - format!("{}:{}", config.agent_ip, config.agent_port), + format!("{}:{}", config.ip, config.port), ssl_context.unwrap(), //#[allow_ci] )? .run(); - info!( - "Listening on https://{}:{}", - config.agent_ip, config.agent_port - ); + info!("Listening on https://{}:{}", config.ip, config.port); } else { server = actix_server - .bind(format!("{}:{}", config.agent_ip, config.agent_port))? + .bind(format!("{}:{}", config.ip, config.port))? .run(); - info!( - "Listening on http://{}:{}", - config.agent_ip, config.agent_port - ); + info!("Listening on http://{}:{}", config.ip, config.port); }; let server_handle = server.handle(); @@ -846,14 +838,17 @@ mod testing { let mut ctx = tpm::get_tpm2_ctx()?; // Gather EK and AK key values and certs - let ek_result = - tpm::create_ek(&mut ctx, test_config.enc_alg.into(), None)?; + let ek_result = tpm::create_ek( + &mut ctx, + test_config.tpm_encryption_alg.into(), + None, + )?; let ak_result = tpm::create_ak( &mut ctx, ek_result.key_handle, - test_config.hash_alg.into(), - test_config.sign_alg.into(), + test_config.tpm_hash_alg.into(), + test_config.tpm_signing_alg.into(), )?; let ak_handle = tpm::load_ak(&mut ctx, ek_result.key_handle, &ak_result)?; @@ -919,7 +914,7 @@ mod testing { hash_alg: algorithms::HashAlgorithm::Sha256, enc_alg: algorithms::EncryptionAlgorithm::Rsa, sign_alg: algorithms::SignAlgorithm::RsaSsa, - agent_uuid: test_config.agent_uuid, + agent_uuid: test_config.uuid, revocation_cert, revocation_actions: String::from(""), revocation_actions_dir: actions_dir, diff --git a/src/revocation.rs b/src/revocation.rs index 2de4418d1..5b0533ff7 100644 --- a/src/revocation.rs +++ b/src/revocation.rs @@ -240,7 +240,7 @@ pub(crate) fn get_revocation_cert_path( config: &KeylimeConfig, ) -> Result { let default_path = - &format!("{}/secure/unzipped/{}", &config.work_dir, REV_CERT); + &format!("{}/secure/unzipped/{}", &config.keylime_dir, REV_CERT); // Unlike the python agent we do not attempt lazy loading. We either // have the certificate, or we don't. If we don't have a key or can't load @@ -259,7 +259,7 @@ pub(crate) fn get_revocation_cert_path( // If the path is not absolute, expand from the WORK_DIR if cert_path_buf.as_path().is_relative() { let rel_path = cert_path_buf; - cert_path_buf = PathBuf::from(&config.work_dir); + cert_path_buf = PathBuf::from(&config.keylime_dir); cert_path_buf.push(rel_path); } @@ -371,7 +371,7 @@ pub(crate) async fn run_revocation_service( config: &KeylimeConfig, mount: &Path, ) -> Result<()> { - let work_dir = Path::new(&config.work_dir); + let work_dir = Path::new(&config.keylime_dir); // Connect to the service via 0mq let context = zmq::Context::new(); @@ -379,8 +379,11 @@ pub(crate) async fn run_revocation_service( mysock.set_subscribe(b"")?; - let endpoint = - format!("tcp://{}:{}", config.revocation_ip, config.revocation_port); + let endpoint = format!( + "tcp://{}:{}", + config.revocation_notification_ip, + config.revocation_notification_port + ); info!("Connecting to revocation endpoint at {}...", endpoint); @@ -561,7 +564,7 @@ mod tests { let test_config = KeylimeConfig::default(); let revocation_cert_path = get_revocation_cert_path(&test_config).unwrap(); //#[allow_ci] - let mut expected = PathBuf::from(&test_config.work_dir); + let mut expected = PathBuf::from(&test_config.keylime_dir); expected.push("secure/unzipped/"); expected.push(REV_CERT); assert_eq!(*revocation_cert_path, expected); @@ -586,7 +589,8 @@ mod tests { }; let revocation_cert_path = get_revocation_cert_path(&test_config).unwrap(); //#[allow_ci] - let mut expected = Path::new(&test_config.work_dir).join("cert.crt"); + let mut expected = + Path::new(&test_config.keylime_dir).join("cert.crt"); assert_eq!(revocation_cert_path, expected); } From 40aee81437fe8ba9b88fd6e1083716198b8f470d Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 12 Sep 2022 13:03:52 +0200 Subject: [PATCH 2/9] Remove the agent TPM data from the config struct Keep only the data path as part of the configuration structure. Then open and load the data when needed. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent.conf | 6 ++++++ src/common.rs | 47 +++++++++++++++++++++++++++++----------------- src/main.rs | 41 ++++++++++++++++++++++++++++------------ 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/keylime-agent.conf b/keylime-agent.conf index 1446c21ac..7c6990a92 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -147,3 +147,9 @@ tpm_ownerpassword = # chown keylime /var/lib/keylime/cv_ca/cacert.crt # run_as = keylime:tss + +# Path where to store the agent tpm data which can be loaded later +# If not an absolute path, it will be considered a relative path from the +# directory set by the keylime_dir option above +agent_data_path = agent_data.json + diff --git a/src/common.rs b/src/common.rs index ff7b2b1e8..d3761568f 100644 --- a/src/common.rs +++ b/src/common.rs @@ -290,7 +290,6 @@ pub(crate) struct KeylimeConfig { pub tpm_signing_alg: SignAlgorithm, pub ek_handle: Option, pub run_as: Option, - pub agent_data: Option, pub agent_data_path: String, } @@ -400,21 +399,37 @@ impl KeylimeConfig { ) .or_else::(|_| Ok(String::from(WORK_DIR)))?; - let agent_data_path = PathBuf::from(&keylime_dir).join(AGENT_DATA); - let agent_data = if agent_data_path.exists() { - match AgentData::load(&agent_data_path) { - Ok(data) => Some(data), - Err(e) => { - warn!("Could not load TPM data"); - None + let agent_data_path = match config_get( + &conf_name, + &conf, + "agent", + "agent_data_path", + ) { + Ok(path) => { + if path.is_empty() { + Path::new(&keylime_dir) + .join(AGENT_DATA) + .display() + .to_string() + } else { + let path = Path::new(&path); + if path.is_relative() { + Path::new(&keylime_dir) + .join(&path) + .display() + .to_string() + } else { + path.display().to_string() + } } } - } else { - warn!( - "Agent Data not available under: {}", - agent_data_path.display() - ); - None + Err(e) => { + warn!("Could not get agent data path from configuration file: {}", e); + Path::new(&keylime_dir) + .join(AGENT_DATA) + .display() + .to_string() + } }; let mut trusted_client_ca = @@ -518,8 +533,7 @@ impl KeylimeConfig { tpm_signing_alg, ek_handle, run_as, - agent_data, - agent_data_path: agent_data_path.display().to_string(), + agent_data_path, }) } @@ -559,7 +573,6 @@ impl Default for KeylimeConfig { tpm_hash_alg: HashAlgorithm::Sha256, tpm_encryption_alg: EncryptionAlgorithm::Rsa, tpm_signing_alg: SignAlgorithm::RsaSsa, - agent_data: None, agent_data_path: Path::new(WORK_DIR) .join(AGENT_DATA) .display() diff --git a/src/main.rs b/src/main.rs index 174b9cce8..8cd4667a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -470,18 +470,31 @@ async fn main() -> Result<()> { )?; // Try to load persistent Agent data - let agent_data = config.agent_data.clone().and_then(|data| - match data.valid(config.tpm_hash_alg, config.tpm_signing_alg) { - true => Some(data), - false => { - warn!( - "Not using old {} because it is not valid with current configuration", - AGENT_DATA - ); + let agent_data_path = Path::new(&config.agent_data_path); + let agent_data = if agent_data_path.exists() { + match AgentData::load(agent_data_path) { + Ok(data) => { + match data.valid(config.tpm_hash_alg, config.tpm_signing_alg) + { + true => Some(data), + false => { + warn!( + "Not using old {} because it is not valid with current configuration", + agent_data_path.display() + ); + None + } + } + } + Err(e) => { + warn!("Could not load TPM data"); None - }, + } } - ); + } else { + warn!("Agent Data not found in: {}", agent_data_path.display()); + None + }; // Try to reuse old AK from Agent Data let old_ak = match &agent_data { @@ -489,13 +502,17 @@ async fn main() -> Result<()> { let ak_result = data.get_ak()?; match tpm::load_ak(&mut ctx, ek_result.key_handle, &ak_result) { Ok(ak_handle) => { - info!("Loaded old AK key from {}", AGENT_DATA); + info!( + "Loaded old AK key from {}", + agent_data_path.display() + ); Some((ak_handle, ak_result)) } Err(e) => { warn!( "Loading old AK key from {} failed: {}", - AGENT_DATA, e + agent_data_path.display(), + e ); None } From 24315ec9cbd1435c0d7ec1d85351a59a5e57b46e Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 12 Sep 2022 20:49:41 +0200 Subject: [PATCH 3/9] Allow using existing key and certificate Separate the key and certificate from the Agent Data and store in separate files. Add the configuration options 'server_key' and 'server_cert' to allow the user to use existing key and certificate. If the files set to 'server_key' or 'server_cert' don't exist, new files are created to store the generated key or certificate. Otherwise the existing files will not be overwritten. This also unify the handling of file paths in the configuration file for the files stored in the 'keylime_dir' by introducing the config_get_file_path() function with the following behavior: - If the option is set as "default" or left empty, the default value is used - If the option is not an absolute path, it is treated as relative from the directory set in the 'keylime_dir' configuration option - If the option is set with an absolute path, it is used without change Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent.conf | 12 +++ src/common.rs | 197 +++++++++++++++++++++---------------- src/crypto.rs | 10 ++ src/main.rs | 237 ++++++++++++++++++++++++++++++--------------- 4 files changed, 292 insertions(+), 164 deletions(-) diff --git a/keylime-agent.conf b/keylime-agent.conf index 7c6990a92..e1bbb1337 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -35,6 +35,18 @@ enable_agent_mtls = True # environment variable. The default value is /var/lib/keylime keylime_dir = /var/lib/keylime +# The name of the file containing the Keylime agent TLS server private key. +# This private key is used to serve the Keylime agent REST API +# A new private key is generated in case it is not found. +# If set as 'default', the 'server-private.pem' value is used. +server_key = default + +# The name of the file containing the X509 certificate used as the Keylime agent +# server TLS certificate. +# This certificate must be self signed. +# If set as 'default', the 'server-cert.crt' value is used +server_cert = default + # The CA that signs the client certificates of the tenant and verifier. # If set to default it tries to use $keylime_dir/cv_ca/cacert.crt trusted_client_ca = default diff --git a/src/common.rs b/src/common.rs index d3761568f..2f45255a2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -45,6 +45,8 @@ pub static IMA_ML: &str = pub static MEASUREDBOOT_ML: &str = "/sys/kernel/security/tpm0/binary_bios_measurements"; // The DEFAULT_CA_PATH is relative from WORK_DIR +pub static DEFAULT_KEY_PATH: &str = "server-key.pem"; +pub static DEFAULT_CERT_PATH: &str = "server-cert.crt"; pub static DEFAULT_CA_PATH: &str = "cv_ca/cacert.crt"; pub static KEY: &str = "secret"; pub const MTLS_ENABLED: bool = true; @@ -184,9 +186,6 @@ pub(crate) struct AgentData { pub ak_sign_alg: SignAlgorithm, ak_public: Vec, ak_private: Vec, - nk_pub: Vec, - nk_priv: Vec, - mtls_cert: Option>, } impl AgentData { @@ -194,24 +193,14 @@ impl AgentData { ak_hash_alg: HashAlgorithm, ak_sign_alg: SignAlgorithm, ak: &tpm::AKResult, - nk_pub: &PKey, - nk_priv: &PKey, - mtls_cert: &Option<&X509>, ) -> Result { let ak_public = ak.public.marshall()?; let ak_private: Vec = ak.private.to_vec(); - let mtls_cert = match mtls_cert { - Some(cert) => Some(cert.to_pem()?), - None => None, - }; Ok(Self { ak_hash_alg, ak_sign_alg, ak_public, ak_private, - nk_pub: nk_pub.public_key_to_pem()?, - nk_priv: nk_priv.private_key_to_pem_pkcs8()?, - mtls_cert, }) } @@ -234,22 +223,6 @@ impl AgentData { Ok(tpm::AKResult { public, private }) } - pub(crate) fn get_nk( - &self, - ) -> Result<(PKey, PKey)> - { - let nk_pub = PKey::public_key_from_pem(&self.nk_pub)?; - let nk_priv = PKey::private_key_from_pem(&self.nk_priv)?; - Ok((nk_pub, nk_priv)) - } - - pub(crate) fn get_mtls_cert(&self) -> Result> { - match &self.mtls_cert { - Some(cert) => Ok(Some(X509::from_pem(cert)?)), - None => Ok(None), - } - } - pub(crate) fn valid( &self, hash_alg: HashAlgorithm, @@ -270,7 +243,9 @@ pub(crate) struct KeylimeConfig { pub registrar_port: String, pub enable_agent_mtls: bool, pub keylime_dir: String, - pub trusted_client_ca: String, + pub server_key: Option, + pub server_cert: Option, + pub trusted_client_ca: Option, pub enc_keyname: String, pub dec_payload_file: String, pub secure_size: String, @@ -290,7 +265,7 @@ pub(crate) struct KeylimeConfig { pub tpm_signing_alg: SignAlgorithm, pub ek_handle: Option, pub run_as: Option, - pub agent_data_path: String, + pub agent_data_path: Option, } impl KeylimeConfig { @@ -399,47 +374,42 @@ impl KeylimeConfig { ) .or_else::(|_| Ok(String::from(WORK_DIR)))?; - let agent_data_path = match config_get( + let mut agent_data_path = config_get_file_path( &conf_name, &conf, "agent", "agent_data_path", - ) { - Ok(path) => { - if path.is_empty() { - Path::new(&keylime_dir) - .join(AGENT_DATA) - .display() - .to_string() - } else { - let path = Path::new(&path); - if path.is_relative() { - Path::new(&keylime_dir) - .join(&path) - .display() - .to_string() - } else { - path.display().to_string() - } - } - } - Err(e) => { - warn!("Could not get agent data path from configuration file: {}", e); - Path::new(&keylime_dir) - .join(AGENT_DATA) - .display() - .to_string() - } - }; + &keylime_dir, + AGENT_DATA, + )?; + + let mut server_key = config_get_file_path( + &conf_name, + &conf, + "agent", + "server_key", + &keylime_dir, + DEFAULT_KEY_PATH, + )?; + + let mut server_cert = config_get_file_path( + &conf_name, + &conf, + "agent", + "server_cert", + &keylime_dir, + DEFAULT_CERT_PATH, + )?; + + let mut trusted_client_ca = config_get_file_path( + &conf_name, + &conf, + "agent", + "trusted_client_ca", + &keylime_dir, + DEFAULT_CA_PATH, + )?; - let mut trusted_client_ca = - config_get(&conf_name, &conf, "agent", "trusted_client_ca")?; - if trusted_client_ca == "default" { - trusted_client_ca = Path::new(&keylime_dir) - .join(DEFAULT_CA_PATH) - .display() - .to_string(); - } let revocation_actions = config_get(&conf_name, &conf, "agent", "revocation_actions") .or_else::(|_| Ok(String::from(REV_ACTIONS)))?; @@ -513,6 +483,8 @@ impl KeylimeConfig { registrar_port, enable_agent_mtls, keylime_dir, + server_key, + server_cert, trusted_client_ca, enc_keyname, dec_payload_file, @@ -536,19 +508,21 @@ impl KeylimeConfig { agent_data_path, }) } +} - // Update function for the uuid if it is set to "hash_ek" - pub fn set_ek_uuid(&mut self, ek_pub: Public) -> Result<()> { - // Converting Public TPM key to PEM - let key = SubjectPublicKeyInfo::try_from(ek_pub)?; - let key_der = picky_asn1_der::to_vec(&key)?; - let openssl_key = PKey::public_key_from_der(&key_der)?; - let pem = openssl_key.public_key_to_pem()?; - - let mut hash = hash(MessageDigest::sha256(), &pem)?; - self.uuid = hex::encode(hash); - Ok(()) - } +/// Calculate the SHA-256 hash of the TPM public key in PEM format +/// +/// This is used as the agent UUID when the configuration option 'uuid' is set as 'hash_ek' +pub(crate) fn hash_ek_pubkey(ek_pub: Public) -> Result { + // Converting Public TPM key to PEM + let key = SubjectPublicKeyInfo::try_from(ek_pub)?; + let key_der = picky_asn1_der::to_vec(&key)?; + let openssl_key = PKey::public_key_from_der(&key_der)?; + let pem = openssl_key.public_key_to_pem()?; + + // Calculate the SHA-256 hash of the public key in PEM format + let mut hash = hash(MessageDigest::sha256(), &pem)?; + Ok(hex::encode(hash)) } // Default test configuration. This should match the defaults in keylime-agent.conf @@ -573,10 +547,9 @@ impl Default for KeylimeConfig { tpm_hash_alg: HashAlgorithm::Sha256, tpm_encryption_alg: EncryptionAlgorithm::Rsa, tpm_signing_alg: SignAlgorithm::RsaSsa, - agent_data_path: Path::new(WORK_DIR) - .join(AGENT_DATA) - .display() - .to_string(), + agent_data_path: Some( + Path::new(WORK_DIR).join(AGENT_DATA).display().to_string(), + ), enable_revocation_notifications: true, revocation_cert: "default".to_string(), revocation_notification_ip: "127.0.0.1".to_string(), @@ -586,7 +559,24 @@ impl Default for KeylimeConfig { dec_payload_file: "decrypted_payload".to_string(), enc_keyname: "derived_tci_key".to_string(), extract_payload_zip: true, - trusted_client_ca: DEFAULT_CA_PATH.to_string(), + server_key: Some( + Path::new(WORK_DIR) + .join(DEFAULT_KEY_PATH) + .display() + .to_string(), + ), + server_cert: Some( + Path::new(WORK_DIR) + .join(DEFAULT_CERT_PATH) + .display() + .to_string(), + ), + trusted_client_ca: Some( + Path::new(WORK_DIR) + .join(DEFAULT_CA_PATH) + .display() + .to_string(), + ), revocation_actions: "".to_string(), revocation_actions_dir: "/usr/libexec/keylime".to_string(), allow_payload_revocation_actions: true, @@ -728,6 +718,45 @@ fn config_get( Ok(value.to_string()) } +/// Get a file path from the configuration file. +/// +/// If the value is set as "default", return the provided default path relative from the provided +/// work_dir. +/// +/// If the option is empty, return Ok(None) +/// +/// If the option is not found in the provided configuration file, return error +fn config_get_file_path( + conf_name: &str, + conf: &Ini, + section: &str, + key: &str, + work_dir: &str, + default: &str, +) -> Result> { + match config_get(conf_name, conf, section, key) { + Ok(path) => { + if path == "default" { + Ok(Some( + Path::new(work_dir).join(default).display().to_string(), + )) + } else if path.is_empty() { + Ok(None) + } else { + let path = Path::new(&path); + if path.is_relative() { + Ok(Some( + Path::new(work_dir).join(path).display().to_string(), + )) + } else { + Ok(Some(path.display().to_string())) + } + } + } + Err(e) => Err(e), + } +} + /* * Input: conf_name, conf,[section] and key and environment variable * Return: Returns the matched key diff --git a/src/crypto.rs b/src/crypto.rs index 5c9af0214..e0274bb74 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -40,6 +40,16 @@ pub(crate) fn load_x509(input_cert_path: &Path) -> Result { Ok(cert) } +/// Read a PEM file and returns the public and private keys +pub(crate) fn load_key_pair( + key_path: &Path, +) -> Result<(PKey, PKey)> { + let pem = std::fs::read(key_path)?; + let private = PKey::private_key_from_pem(&pem)?; + let public = pkey_pub_from_priv(private.clone())?; + Ok((public, private)) +} + pub(crate) fn rsa_generate(key_size: u32) -> Result> { PKey::from_rsa(Rsa::generate(key_size)?).map_err(Error::Crypto) } diff --git a/src/main.rs b/src/main.rs index 8cd4667a4..8d3d74733 100644 --- a/src/main.rs +++ b/src/main.rs @@ -470,55 +470,63 @@ async fn main() -> Result<()> { )?; // Try to load persistent Agent data - let agent_data_path = Path::new(&config.agent_data_path); - let agent_data = if agent_data_path.exists() { - match AgentData::load(agent_data_path) { - Ok(data) => { - match data.valid(config.tpm_hash_alg, config.tpm_signing_alg) - { - true => Some(data), - false => { - warn!( - "Not using old {} because it is not valid with current configuration", - agent_data_path.display() - ); + let old_ak = match &config.agent_data_path { + Some(path) => { + let path = Path::new(&path); + if path.exists() { + match AgentData::load(path) { + Ok(data) => { + match data.valid( + config.tpm_hash_alg, + config.tpm_signing_alg, + ) { + true => { + let ak_result = data.get_ak()?; + match tpm::load_ak( + &mut ctx, + ek_result.key_handle, + &ak_result, + ) { + Ok(ak_handle) => { + info!( + "Loaded old AK key from {}", + path.display() + ); + Some((ak_handle, ak_result)) + } + Err(e) => { + warn!( + "Loading old AK key from {} failed: {}", + path.display(), + e + ); + None + } + } + } + false => { + warn!( + "Not using old {} because it is not valid with current configuration", + path.display() + ); + None + } + } + } + Err(e) => { + warn!("Could not load agent data: {}", e); None } } - } - Err(e) => { - warn!("Could not load TPM data"); + } else { + info!("Agent Data not found in: {}", path.display()); None } } - } else { - warn!("Agent Data not found in: {}", agent_data_path.display()); - None - }; - - // Try to reuse old AK from Agent Data - let old_ak = match &agent_data { - Some(data) => { - let ak_result = data.get_ak()?; - match tpm::load_ak(&mut ctx, ek_result.key_handle, &ak_result) { - Ok(ak_handle) => { - info!( - "Loaded old AK key from {}", - agent_data_path.display() - ); - Some((ak_handle, ak_result)) - } - Err(e) => { - warn!( - "Loading old AK key from {} failed: {}", - agent_data_path.display(), - e - ); - None - } - } + None => { + info!("Agent Data path not set in the configuration file"); + None } - None => None, }; // Use old AK or generate a new one and update the AgentData @@ -537,11 +545,25 @@ async fn main() -> Result<()> { } }; - if config.uuid == "hash_ek" { - config.set_ek_uuid(ek_result.public.clone())?; + // Store new AgentData + let agent_data_new = + AgentData::create(config.tpm_hash_alg, config.tpm_signing_alg, &ak)?; + + match &config.agent_data_path { + Some(path) => { + agent_data_new.store(Path::new(&path))?; + } + None => { + info!("Agent Data not stored"); + } } - info!("Agent UUID: {}", config.uuid); + let uuid = match config.uuid.as_str() { + "hash_ek" => hash_ek_pubkey(ek_result.public.clone())?, + s => s.to_string(), + }; + + info!("Agent UUID: {}", uuid); // Generate key pair for secure transmission of u, v keys. The u, v // keys are two halves of the key used to decrypt the workload after @@ -551,34 +573,100 @@ async fn main() -> Result<()> { // Since we store the u key in memory, discarding this key, which // safeguards u and v keys in transit, is not part of the threat model. - let (nk_pub, nk_priv) = match &agent_data { - Some(data) => data.get_nk()?, - None => crypto::rsa_generate_pair(2048)?, + let (nk_pub, nk_priv) = match config.server_key { + Some(ref path) => { + let key_path = Path::new(&path); + if key_path.exists() { + debug!( + "Loading existing key pair from {}", + key_path.display() + ); + crypto::load_key_pair(key_path)? + } else { + debug!("Generating new key pair"); + let (public, private) = crypto::rsa_generate_pair(2048)?; + { + // Write the generated key to the file + let mut file = std::fs::File::create(key_path)?; + _ = file.write(&private.private_key_to_pem_pkcs8()?)?; + fs::set_permissions( + key_path, + fs::Permissions::from_mode(0o600), + )? + } + (public, private) + } + } + None => { + debug!( + "The server_key option was not set in the configuration file" + ); + debug!("Generating new key pair"); + crypto::rsa_generate_pair(2048)? + } }; let cert: openssl::x509::X509; let mtls_cert; let ssl_context; if config.enable_agent_mtls { - let keylime_ca_cert = - match crypto::load_x509(Path::new(&config.trusted_client_ca)) { - Ok(t) => Ok(t), - Err(e) => { - error!( - "Certificate not installed: {}", - config.trusted_client_ca + cert = match config.server_cert { + Some(ref path) => { + let cert_path = Path::new(&path); + if cert_path.exists() { + debug!( + "Loading existing mTLS certificate from {}", + cert_path.display() ); - Err(e) + crypto::load_x509(cert_path)? + } else { + debug!("Generating new mTLS certificate"); + let cert = crypto::generate_x509(&nk_priv, &uuid)?; + { + // Write the generated key to the file + let mut file = std::fs::File::create(cert_path)?; + _ = file.write(&cert.to_pem()?)?; + } + cert } - }?; - - cert = match &agent_data { - Some(data) => match data.get_mtls_cert()? { - Some(cert) => cert, - None => crypto::generate_x509(&nk_priv, &config.uuid)?, - }, - None => crypto::generate_x509(&nk_priv, &config.uuid)?, + } + None => { + debug!("The server_cert option was not set in the configuration file"); + crypto::generate_x509(&nk_priv, &uuid)? + } + }; + + let ca_cert_path = match config.trusted_client_ca { + None => { + error!("Agent mTLS is enabled, but trusted_client_ca option was not provided"); + return Err(Error::Configuration("Agent mTLS is enabled, but trusted_client_ca option was not provided".to_string())); + } + Some(ref path) => PathBuf::from(&path), }; + + if !ca_cert_path.exists() { + error!( + "Trusted client CA certificate not found: {} does not exist", + ca_cert_path.display() + ); + return Err(Error::Configuration(format!( + "Trusted client CA certificate not found: {} does not exist", + ca_cert_path.display() + ))); + } + + let keylime_ca_cert = match crypto::load_x509(&ca_cert_path) { + Ok(t) => Ok(t), + Err(e) => { + error!( + "Failed to load trusted CA certificate {}: {}", + ca_cert_path.display(), + e + ); + Err(e) + } + }?; + mtls_cert = Some(&cert); ssl_context = Some(crypto::generate_mtls_context( &cert, @@ -591,23 +679,12 @@ async fn main() -> Result<()> { warn!("mTLS disabled, Tenant and Verifier will reach out to agent via HTTP"); } - // Store new AgentData - let agent_data_new = AgentData::create( - config.tpm_hash_alg, - config.tpm_signing_alg, - &ak, - &nk_pub, - &nk_priv, - &mtls_cert, - )?; - agent_data_new.store(Path::new(&config.agent_data_path))?; - { // Request keyblob material let keyblob = registrar_agent::do_register_agent( &config.registrar_ip, &config.registrar_port, - &config.uuid, + &uuid, &PublicBuffer::try_from(ek_result.public.clone())?.marshall()?, ek_result.ek_cert, &PublicBuffer::try_from(ak.public)?.marshall()?, @@ -616,7 +693,7 @@ async fn main() -> Result<()> { config.contact_port, ) .await?; - info!("SUCCESS: Agent {} registered", config.uuid); + info!("SUCCESS: Agent {} registered", &uuid); let key = tpm::activate_credential( &mut ctx, @@ -630,17 +707,17 @@ async fn main() -> Result<()> { } let mackey = base64::encode(key.value()); let auth_tag = - crypto::compute_hmac(mackey.as_bytes(), config.uuid.as_bytes())?; + crypto::compute_hmac(mackey.as_bytes(), uuid.as_bytes())?; let auth_tag = hex::encode(&auth_tag); registrar_agent::do_activate_agent( &config.registrar_ip, &config.registrar_port, - &config.uuid, + &uuid, &auth_tag, ) .await?; - info!("SUCCESS: Agent {} activated", config.uuid); + info!("SUCCESS: Agent {} activated", &uuid); } let mut encr_payload = Vec::new(); From ba0dc9ca062939eba6fa6fa55734e2395912d7af Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 13 Sep 2022 15:44:51 +0200 Subject: [PATCH 4/9] Do not try to load TPM data generated by another TPM Include as part of the stored Agent Data the hash of the public EK of the TPM that generated the AK. Before loading the AK, check if the current TPM is the same TPM that generated the information to avoid loading errors. Signed-off-by: Anderson Toshiyuki Sasaki --- src/common.rs | 9 ++++++++- src/main.rs | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/common.rs b/src/common.rs index 2f45255a2..680f7eff2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -186,6 +186,7 @@ pub(crate) struct AgentData { pub ak_sign_alg: SignAlgorithm, ak_public: Vec, ak_private: Vec, + ek_hash: Vec, } impl AgentData { @@ -193,14 +194,17 @@ impl AgentData { ak_hash_alg: HashAlgorithm, ak_sign_alg: SignAlgorithm, ak: &tpm::AKResult, + ek_hash: &[u8], ) -> Result { let ak_public = ak.public.marshall()?; let ak_private: Vec = ak.private.to_vec(); + let ek_hash: Vec = ek_hash.to_vec(); Ok(Self { ak_hash_alg, ak_sign_alg, ak_public, ak_private, + ek_hash, }) } @@ -227,8 +231,11 @@ impl AgentData { &self, hash_alg: HashAlgorithm, sign_alg: SignAlgorithm, + ek_hash: &[u8], ) -> bool { - hash_alg == self.ak_hash_alg && sign_alg == self.ak_sign_alg + hash_alg == self.ak_hash_alg + && sign_alg == self.ak_sign_alg + && ek_hash.to_vec() == self.ek_hash } } diff --git a/src/main.rs b/src/main.rs index 8d3d74733..eb81434ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -469,6 +469,9 @@ async fn main() -> Result<()> { config.ek_handle.as_deref(), )?; + // Calculate the SHA-256 hash of the public key in PEM format + let ek_hash = hash_ek_pubkey(ek_result.public.clone())?; + // Try to load persistent Agent data let old_ak = match &config.agent_data_path { Some(path) => { @@ -479,6 +482,7 @@ async fn main() -> Result<()> { match data.valid( config.tpm_hash_alg, config.tpm_signing_alg, + ek_hash.as_bytes(), ) { true => { let ak_result = data.get_ak()?; @@ -546,8 +550,12 @@ async fn main() -> Result<()> { }; // Store new AgentData - let agent_data_new = - AgentData::create(config.tpm_hash_alg, config.tpm_signing_alg, &ak)?; + let agent_data_new = AgentData::create( + config.tpm_hash_alg, + config.tpm_signing_alg, + &ak, + ek_hash.as_bytes(), + )?; match &config.agent_data_path { Some(path) => { From 7546683b8b32b48b87dfdf851cc7786ce14b1c92 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 13 Sep 2022 22:00:12 +0200 Subject: [PATCH 5/9] Add support for using passphrase protected key Add the configuration option server_key_password to allow setting a password to be used to encrypt the generated key when writing to a file and to decrypt the key when loading from a file. If the server_key_password is set as empty, the key is assumed to be unencrypted. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent.conf | 6 +++ src/common.rs | 7 +++ src/crypto.rs | 128 ++++++++++++++++++++++++++++++++++++++++++--- src/main.rs | 31 +++++------ 4 files changed, 147 insertions(+), 25 deletions(-) diff --git a/keylime-agent.conf b/keylime-agent.conf index e1bbb1337..83a1b8361 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -41,6 +41,12 @@ keylime_dir = /var/lib/keylime # If set as 'default', the 'server-private.pem' value is used. server_key = default +# Set the password used to encrypt the private key file. +# This password will also be used to protect the generated private key used for +# mTLS authentication +# If left empty, the private key will not be encrypted. +server_key_password = + # The name of the file containing the X509 certificate used as the Keylime agent # server TLS certificate. # This certificate must be self signed. diff --git a/src/common.rs b/src/common.rs index 680f7eff2..35db4de65 100644 --- a/src/common.rs +++ b/src/common.rs @@ -252,6 +252,7 @@ pub(crate) struct KeylimeConfig { pub keylime_dir: String, pub server_key: Option, pub server_cert: Option, + pub server_key_password: Option, pub trusted_client_ca: Option, pub enc_keyname: String, pub dec_payload_file: String, @@ -417,6 +418,10 @@ impl KeylimeConfig { DEFAULT_CA_PATH, )?; + let server_key_password = + config_get(&conf_name, &conf, "agent", "server_key_password") + .ok(); + let revocation_actions = config_get(&conf_name, &conf, "agent", "revocation_actions") .or_else::(|_| Ok(String::from(REV_ACTIONS)))?; @@ -492,6 +497,7 @@ impl KeylimeConfig { keylime_dir, server_key, server_cert, + server_key_password, trusted_client_ca, enc_keyname, dec_payload_file, @@ -572,6 +578,7 @@ impl Default for KeylimeConfig { .display() .to_string(), ), + server_key_password: None, server_cert: Some( Path::new(WORK_DIR) .join(DEFAULT_CERT_PATH) diff --git a/src/crypto.rs b/src/crypto.rs index e0274bb74..247037869 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -16,9 +16,13 @@ use openssl::{ x509::store::X509StoreBuilder, x509::{X509Name, X509}, }; -use std::fs; -use std::path::Path; -use std::string::String; +use std::{ + fs::{read_to_string, set_permissions, File, Permissions}, + io::{Read, Write}, + os::unix::fs::PermissionsExt, + path::Path, + string::String, +}; use crate::{ Error, Result, AES_128_KEY_LEN, AES_256_KEY_LEN, AES_BLOCK_SIZE, @@ -26,7 +30,7 @@ use crate::{ // Read a X509 cert or cert chain and outputs the first certificate pub(crate) fn load_x509(input_cert_path: &Path) -> Result { - let contents = fs::read_to_string(&input_cert_path)?; + let contents = read_to_string(&input_cert_path)?; let mut cert_chain = X509::stack_from_pem(contents.as_bytes())?; if cert_chain.len() != 1 { @@ -40,16 +44,62 @@ pub(crate) fn load_x509(input_cert_path: &Path) -> Result { Ok(cert) } +/// Write a X509 certificate to a file in PEM format +pub(crate) fn write_x509(cert: &X509, file_path: &Path) -> Result<()> { + let mut file = std::fs::File::create(file_path)?; + _ = file.write(&cert.to_pem()?)?; + Ok(()) +} + /// Read a PEM file and returns the public and private keys pub(crate) fn load_key_pair( key_path: &Path, + key_password: &Option, ) -> Result<(PKey, PKey)> { let pem = std::fs::read(key_path)?; - let private = PKey::private_key_from_pem(&pem)?; + let private = match key_password { + Some(ref pw) => { + if pw.is_empty() { + PKey::private_key_from_pem(&pem)? + } else { + PKey::private_key_from_pem_passphrase(&pem, pw.as_bytes())? + } + } + None => PKey::private_key_from_pem(&pem)?, + }; let public = pkey_pub_from_priv(private.clone())?; Ok((public, private)) } +/// Write a private key to a file. +/// +/// If a passphrase is provided, the key will be stored encrypted using AES-256-CBC +pub(crate) fn write_key_pair( + key: &PKey, + file_path: &Path, + passphrase: &Option, +) -> Result<()> { + // Write the generated key to the file + let mut file = std::fs::File::create(file_path)?; + match passphrase { + Some(ref pw) => { + if pw.is_empty() { + _ = file.write(&key.private_key_to_pem_pkcs8()?)?; + } else { + _ = file.write(&key.private_key_to_pem_pkcs8_passphrase( + openssl::symm::Cipher::aes_256_cbc(), + pw.as_bytes(), + )?)?; + } + } + None => { + _ = file.write(&key.private_key_to_pem_pkcs8()?)?; + } + } + set_permissions(file_path, Permissions::from_mode(0o600))?; + Ok(()) +} + pub(crate) fn rsa_generate(key_size: u32) -> Result> { PKey::from_rsa(Rsa::generate(key_size)?).map_err(Error::Crypto) } @@ -293,7 +343,7 @@ pub mod testing { pub(crate) fn rsa_import_pair( path: impl AsRef, ) -> Result<(PKey, PKey)> { - let contents = fs::read_to_string(path)?; + let contents = read_to_string(path)?; let private = PKey::private_key_from_pem(contents.as_bytes())?; let public = pkey_pub_from_priv(private.clone())?; Ok((public, private)) @@ -531,7 +581,7 @@ mod tests { .join("test-rsa.pem"); // Get RSA keys - let contents = fs::read_to_string(rsa_key_path); + let contents = read_to_string(rsa_key_path); let private = PKey::private_key_from_pem(contents.unwrap().as_bytes()).unwrap(); //#[allow_ci] let public = pkey_pub_from_priv(private).unwrap(); //#[allow_ci] @@ -543,8 +593,70 @@ mod tests { .join("test-data") .join("test-rsa.sig"); - let signature = fs::read_to_string(signature_path).unwrap(); //#[allow_ci] + let signature = read_to_string(signature_path).unwrap(); //#[allow_ci] assert!(asym_verify(&public, &message, &signature).unwrap()) //#[allow_ci] } + + #[test] + fn test_password() { + // Import test keypair + let rsa_key_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("test-data") + .join("test-rsa.pem"); + + // Get RSA keys + let (public, private) = rsa_import_pair(rsa_key_path).unwrap(); //#[allow_ci] + + // Create temporary directory and files names + let temp_dir = tempfile::tempdir().unwrap(); //#[allow_ci] + let encrypted_path = + Path::new(&temp_dir.path()).join("encrypted.pem"); + let empty_pw_path = Path::new(&temp_dir.path()).join("empty_pw.pem"); + let none_pw_path = Path::new(&temp_dir.path()).join("none_pw.pem"); + + let message = b"Hello World!"; + + // Write keys to files + assert!(write_key_pair( + &private, + &encrypted_path, + &Some("password".to_string()) + ) + .is_ok()); + assert!(write_key_pair( + &private, + &empty_pw_path, + &Some("".to_string()) + ) + .is_ok()); + assert!(write_key_pair(&private, &none_pw_path, &None).is_ok()); + + // Read keys from files + let (_, priv_from_encrypted) = + load_key_pair(&encrypted_path, &Some("password".to_string())) + .unwrap(); //#[allow_ci] + let (_, priv_from_empty) = + load_key_pair(&empty_pw_path, &Some("".to_string())).unwrap(); //#[allow_ci] + let (_, priv_from_none) = + load_key_pair(&none_pw_path, &None).unwrap(); //#[allow_ci] + + for keypair in [ + priv_from_encrypted.as_ref(), + priv_from_empty.as_ref(), + priv_from_none.as_ref(), + ] { + // Sign the data + let mut signer = + Signer::new(MessageDigest::sha256(), keypair).unwrap(); //#[allow_ci] + signer.update(message).unwrap(); //#[allow_ci] + let signature = signer.sign_to_vec().unwrap(); //#[allow_ci] + + // Verify the data + let mut verifier = + Verifier::new(MessageDigest::sha256(), keypair).unwrap(); //#[allow_ci] + verifier.update(message).unwrap(); //#[allow_ci] + assert!(verifier.verify(&signature).unwrap()); //#[allow_ci] + } + } } diff --git a/src/main.rs b/src/main.rs index eb81434ca..5e2ec6879 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,10 @@ use error::{Error, Result}; use futures::{future::TryFutureExt, try_join}; use ima::ImaMeasurementList; use log::*; -use openssl::pkey::{PKey, Private, Public}; +use openssl::{ + pkey::{PKey, Private, Public}, + x509::X509, +}; use std::{ convert::TryFrom, fs, @@ -589,19 +592,16 @@ async fn main() -> Result<()> { "Loading existing key pair from {}", key_path.display() ); - crypto::load_key_pair(key_path)? + crypto::load_key_pair(key_path, &config.server_key_password)? } else { debug!("Generating new key pair"); let (public, private) = crypto::rsa_generate_pair(2048)?; - { - // Write the generated key to the file - let mut file = std::fs::File::create(key_path)?; - _ = file.write(&private.private_key_to_pem_pkcs8()?)?; - fs::set_permissions( - key_path, - fs::Permissions::from_mode(0o600), - )? - } + // Write the generated key to the file + crypto::write_key_pair( + &private, + key_path, + &config.server_key_password, + ); (public, private) } } @@ -614,7 +614,7 @@ async fn main() -> Result<()> { } }; - let cert: openssl::x509::X509; + let cert: X509; let mtls_cert; let ssl_context; if config.enable_agent_mtls { @@ -630,11 +630,8 @@ async fn main() -> Result<()> { } else { debug!("Generating new mTLS certificate"); let cert = crypto::generate_x509(&nk_priv, &uuid)?; - { - // Write the generated key to the file - let mut file = std::fs::File::create(cert_path)?; - _ = file.write(&cert.to_pem()?)?; - } + // Write the generated certificate + crypto::write_x509(&cert, cert_path)?; cert } } From 4ff077e206168e152709cddee1684ffea06815bc Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 19 Sep 2022 18:10:36 +0200 Subject: [PATCH 6/9] Change configuration format to TOML This introduces several improvements to the agent configuration. - The file format is modified from INI style files to TOML formatted files. - The configuration is constructed using an hierarchical organization following the order: - The default hardcoded values are loaded - If a configuration file is found in /usr/etc/keylime-agent.conf, it is loaded and any option set overrides the default values - Load configuration snippets from /usr/etc/keylime-agent.conf.d/*, overriding the previous values - Load the configuration from /etc/keylime-agent.conf, overriding previous values - Load configuration snippets from /etc/keylime-agent.conf.d/*, overriding previous values - Finally any configuration option can be overriden via environment variable, by using the 'KEYLIME_AGENT_' prefix. For example, KEYLIME_AGENT_PORT=9003 will override the 'port' option from the 'agent' section with the value '9003'. This was introduced by using an added dependency, the 'config' crate using the 'toml' feature for the toml file format. Signed-off-by: Anderson Toshiyuki Sasaki --- Cargo.lock | 193 ++++++++- Cargo.toml | 3 + keylime-agent.conf | 75 ++-- src/common.rs | 613 ----------------------------- src/config.rs | 732 +++++++++++++++++++++++++++++++++++ src/error.rs | 6 +- src/keys_handler.rs | 11 +- src/main.rs | 236 ++++++----- src/notifications_handler.rs | 70 +++- src/revocation.rs | 178 ++++----- 10 files changed, 1232 insertions(+), 885 deletions(-) create mode 100644 src/config.rs diff --git a/Cargo.lock b/Cargo.lock index 293e56c81..96ff5e382 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,6 +454,25 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "config" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f1667b8320afa80d69d8bbe40830df2c8a06003d86f73d8e003b2c48df416d" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini 0.18.0", + "serde", + "serde_json", + "toml 0.5.9", + "yaml-rust", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -549,9 +568,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", @@ -566,6 +585,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -818,6 +843,12 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "h2" version = "0.3.13" @@ -852,6 +883,15 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + [[package]] name = "heck" version = "0.4.0" @@ -1046,6 +1086,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "keylime_agent" version = "0.1.0" @@ -1056,7 +1107,9 @@ dependencies = [ "cfg-if", "clap", "compress-tools", + "config", "futures", + "glob", "hex", "libc", "log", @@ -1065,7 +1118,7 @@ dependencies = [ "picky-asn1-x509", "pretty_env_logger", "reqwest", - "rust-ini", + "rust-ini 0.17.0", "serde", "serde_derive", "serde_json", @@ -1073,6 +1126,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "toml 0.5.9", "tss-esapi", "uuid", "wiremock", @@ -1097,6 +1151,12 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "local-channel" version = "0.1.3" @@ -1165,7 +1225,7 @@ checksum = "73b122901b3a675fac8cecf68dcb2f0d3036193bc861d1ac0e1c337f7d5254c2" dependencies = [ "error-chain", "pkg-config", - "toml", + "toml 0.2.1", ] [[package]] @@ -1174,6 +1234,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.5.3" @@ -1213,6 +1279,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1318,10 +1394,20 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" dependencies = [ - "dlv-list", + "dlv-list 0.2.3", "hashbrown 0.9.1", ] +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list 0.3.0", + "hashbrown 0.12.3", +] + [[package]] name = "os_str_bytes" version = "6.1.0" @@ -1363,6 +1449,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1371,13 +1463,48 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.1.3" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048" dependencies = [ + "thiserror", "ucd-trie", ] +[[package]] +name = "pest_derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6" +dependencies = [ + "once_cell", + "pest", + "sha1", +] + [[package]] name = "picky-asn1" version = "0.3.3" @@ -1665,6 +1792,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64", + "bitflags", + "serde", +] + [[package]] name = "rust-ini" version = "0.17.0" @@ -1672,7 +1810,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" dependencies = [ "cfg-if", - "ordered-multimap", + "ordered-multimap 0.3.1", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap 0.4.3", ] [[package]] @@ -1836,6 +1984,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -2069,6 +2228,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.1" @@ -2421,6 +2589,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zeroize" version = "1.5.5" diff --git a/Cargo.toml b/Cargo.toml index 705e6a196..ddcb2a29f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,9 @@ base64 = "0.13" cfg-if = "1" clap = { version = "~3.1.18", features = ["derive"] } compress-tools = "0.12" +config = { version = "0.13", features = ["toml"] } futures = "0.3.6" +glob = "0.3" hex = "0.4" libc = "0.2.43" log = "0.4" @@ -39,6 +41,7 @@ serde_json = { version = "1.0", features = ["raw_value"] } static_assertions = "1" tempfile = "3.0.4" tokio = {version = "1.13.1", features = ["full"]} +toml = "0.5" tss-esapi = "7.1.0" thiserror = "1.0" uuid = {version = "0.8", features = ["v4"]} diff --git a/keylime-agent.conf b/keylime-agent.conf index 83a1b8361..1a2ef295d 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -11,87 +11,83 @@ # 'dmidecode -s system-uuid'. # If you set this to "hostname", Keylime will use the full qualified domain # name of current host as the agent id. -uuid = d432fbb3-d2f1-4a97-9ef7-75bd81c00000 +uuid = "d432fbb3-d2f1-4a97-9ef7-75bd81c00000" # The binding address and port for the agent server -ip = 127.0.0.1 +ip = "127.0.0.1" port = 9002 # Address and port where the verifier and tenant can connect to reach the agent. # These keys are optional. -contact_ip = 127.0.0.1 +contact_ip = "127.0.0.1" contact_port = 9002 # The address and port of registrar server which agent communicate with -registrar_ip = 127.0.0.1 +registrar_ip = "127.0.0.1" registrar_port = 8890 # Enable mTLS communication between agent, verifier and tenant. -# Details on why setting it to "False" is generally considered insecure can be found +# Details on why setting it to "false" is generally considered insecure can be found # on https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r -enable_agent_mtls = True +enable_agent_mtls = true # The keylime working directory. Can be overriden by setting the KEYLIME_DIR # environment variable. The default value is /var/lib/keylime -keylime_dir = /var/lib/keylime +keylime_dir = "/var/lib/keylime" # The name of the file containing the Keylime agent TLS server private key. # This private key is used to serve the Keylime agent REST API # A new private key is generated in case it is not found. -# If set as 'default', the 'server-private.pem' value is used. -server_key = default +# If set as "default", the "server-private.pem" value is used. +server_key = "default" # Set the password used to encrypt the private key file. # This password will also be used to protect the generated private key used for # mTLS authentication # If left empty, the private key will not be encrypted. -server_key_password = +server_key_password = "" # The name of the file containing the X509 certificate used as the Keylime agent # server TLS certificate. # This certificate must be self signed. -# If set as 'default', the 'server-cert.crt' value is used -server_cert = default +# If set as "default", the "server-cert.crt" value is used +server_cert = "default" # The CA that signs the client certificates of the tenant and verifier. -# If set to default it tries to use $keylime_dir/cv_ca/cacert.crt -trusted_client_ca = default +# If set as "default" the "cv_ca/cacert.crt" value is used +trusted_client_ca = "default" # The name that should be used for the encryption key, placed in the # $keylime_dir/secure/ directory. -enc_keyname = derived_tci_key +enc_keyname = "derived_tci_key" # The name that should be used for the optional decrypted payload, placed in # the $keylime_dir/secure directory. -dec_payload_file = decrypted_payload +dec_payload_file = "decrypted_payload" # The size of the memory-backed tmpfs partition where Keylime stores crypto keys. # Use syntax that the 'mount' command would accept as a size parameter for tmpfs. # The default below sets it to 1 megabyte. -secure_size = 1m - -# Use this option to state the existing TPM ownerpassword. This option should -# be set only when ek_handle option points to an existing EK. -tpm_ownerpassword = +secure_size = "1m" # Whether to allow the cloud_agent to automatically extract a zip file in # the delivered payload after it has been decrypted, or not. Defaults to "true". # After decryption, the archive will be unzipped to a directory in $keylime_dir/secure. # Note: the limits on the size of the tmpfs partition set above with the 'secure_size' # option will affect this. -extract_payload_zip = True +extract_payload_zip = true # Whether to listen for revocation notifications from the verifier or not. -enable_revocation_notifications = True +enable_revocation_notifications = true # The path to the directory containing the pre-installed revocation action # scripts. Ideally should point to an fixed/immutable location subject to # attestation. The default is /usr/libexec/keylime. -revocation_actions_dir = /usr/libexec/keylime +revocation_actions_dir = "/usr/libexec/keylime" # Revocation IP & Port used by either the cloud_agent to receive revocation # notifications from the verifier. -revocation_notification_ip = 127.0.0.1 +revocation_notification_ip = "127.0.0.1" revocation_notification_port = 8992 # The path to the certificate to verify revocation messages received from the @@ -99,7 +95,7 @@ revocation_notification_port = 8992 # provided (i.e. starts with '/'). # If set to "default", Keylime will use the file RevocationNotifier-cert.crt # from the unzipped contents provided by the tenant. -revocation_cert = default +revocation_cert = "default" # A comma-separated list of executables to run upon receiving a revocation # message. Keylime will verify the signature first, then call these executables @@ -108,24 +104,24 @@ revocation_cert = default # # Keylime will also get the list of revocation actions from the file # action_list in the unzipped contents provided by the verifier. -revocation_actions= +revocation_actions = "" # A script to execute after unzipping the tenant payload. This is like # cloud-init lite =) Keylime will run it with a /bin/sh environment and # with a working directory of $keylime_dir/secure/unzipped. -payload_script=autorun.sh +payload_script = "autorun.sh" # In case mTLS for the agent is disabled and the use of payloads is still -# required, this option has to be set to "True" in order to allow the agent +# required, this option has to be set to "true" in order to allow the agent # to start. Details on why this configuration (mTLS disabled and payload enabled) # is generally considered insecure can be found on # https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r -enable_insecure_payload = False +enable_insecure_payload = false # Whether to allow running revocation actions sent as part of the payload. The -# default is True and setting as False will limit the revocation actions to the +# default is true and setting as false will limit the revocation actions to the # pre-installed ones. -allow_payload_revocation_actions = True +allow_payload_revocation_actions = true # TPM2-specific options, allows customizing default algorithms to use. # Specify the default crypto algorithms to use with a TPM2 for this agent. @@ -134,19 +130,19 @@ allow_payload_revocation_actions = True # - hashing: sha512, sha384, sha256 or sha1 # - encryption: ecc or rsa # - signing: rsassa, rsapss, ecdsa, ecdaa or ecschnorr -tpm_hash_alg = sha256 -tpm_encryption_alg = rsa -tpm_signing_alg = rsassa +tpm_hash_alg = "sha256" +tpm_encryption_alg = "rsa" +tpm_signing_alg = "rsassa" # If an EK is already present on the TPM (e.g., with "tpm2_createek") and # you require Keylime to use this EK, change "generate" to the actual EK # handle (e.g. "0x81000000"). The Keylime agent will then not attempt to # create a new EK upon startup, and neither will it flush the EK upon exit. -ek_handle = generate +ek_handle = "generate" # Use this option to state the existing TPM ownerpassword. This option should # be set only when ek_handle option points to an existing EK. -tpm_ownerpassword = +tpm_ownerpassword = "" # The user account to switch to to drop privileges when started as root # If left empty, the agent will keep running with high privileges. @@ -164,10 +160,11 @@ tpm_ownerpassword = # chown keylime /var/lib/keylime/cv_ca # chown keylime /var/lib/keylime/cv_ca/cacert.crt # -run_as = keylime:tss +run_as = "keylime:tss" # Path where to store the agent tpm data which can be loaded later # If not an absolute path, it will be considered a relative path from the # directory set by the keylime_dir option above -agent_data_path = agent_data.json +# If set as "default" Keylime will use "agent_data.json" +agent_data_path = "default" diff --git a/src/common.rs b/src/common.rs index 35db4de65..59e8f6993 100644 --- a/src/common.rs +++ b/src/common.rs @@ -4,7 +4,6 @@ use crate::algorithms::{EncryptionAlgorithm, HashAlgorithm, SignAlgorithm}; use crate::error::{Error, Result}; use crate::{permissions, tpm}; -use ini::Ini; use log::*; use openssl::{ hash::{hash, MessageDigest}, @@ -27,7 +26,6 @@ use tss_esapi::utils::PublicKey; use tss_esapi::{ structures::PcrSlot, traits::UnMarshall, utils::TpmsContext, }; -use uuid::Uuid; /* * Constants and static variables @@ -37,30 +35,12 @@ pub const STUB_VTPM: bool = false; pub const STUB_IMA: bool = true; pub const TPM_DATA_PCR: usize = 16; pub const IMA_PCR: usize = 10; -pub static DEFAULT_CONFIG: &str = "/etc/keylime-agent.conf"; pub static RSA_PUBLICKEY_EXPORTABLE: &str = "rsa placeholder"; -pub static TPM_TOOLS_PATH: &str = "/usr/local/bin/"; pub static IMA_ML: &str = "/sys/kernel/security/ima/ascii_runtime_measurements"; pub static MEASUREDBOOT_ML: &str = "/sys/kernel/security/tpm0/binary_bios_measurements"; -// The DEFAULT_CA_PATH is relative from WORK_DIR -pub static DEFAULT_KEY_PATH: &str = "server-key.pem"; -pub static DEFAULT_CERT_PATH: &str = "server-cert.crt"; -pub static DEFAULT_CA_PATH: &str = "cv_ca/cacert.crt"; pub static KEY: &str = "secret"; -pub const MTLS_ENABLED: bool = true; -pub static WORK_DIR: &str = "/var/lib/keylime"; -pub static AGENT_DATA: &str = "agent_data.json"; -// Note: The revocation certificate name is generated inside the Python tenant and the -// certificate(s) can be generated by running the tenant with the --cert flag. For more -// information, check the README: https://github.com/keylime/keylime/#using-keylime-ca -pub static REV_CERT: &str = "RevocationNotifier-cert.crt"; -pub static REV_ACTIONS_DIR: &str = "/usr/libexec/keylime"; -pub static REV_ACTIONS: &str = ""; -pub static ALLOW_PAYLOAD_REV_ACTIONS: bool = true; -pub static ALLOW_INSECURE_PAYLOAD: bool = false; - pub const AGENT_UUID_LEN: usize = 36; pub const AUTH_TAG_LEN: usize = 96; pub const AES_128_KEY_LEN: usize = 16; @@ -239,290 +219,6 @@ impl AgentData { } } -#[derive(Clone, Debug)] -pub(crate) struct KeylimeConfig { - pub uuid: String, - pub ip: String, - pub port: String, - pub contact_ip: Option, - pub contact_port: Option, - pub registrar_ip: String, - pub registrar_port: String, - pub enable_agent_mtls: bool, - pub keylime_dir: String, - pub server_key: Option, - pub server_cert: Option, - pub server_key_password: Option, - pub trusted_client_ca: Option, - pub enc_keyname: String, - pub dec_payload_file: String, - pub secure_size: String, - pub tpm_ownerpassword: Option, - pub extract_payload_zip: bool, - pub enable_revocation_notifications: bool, - pub revocation_actions_dir: String, - pub revocation_notification_ip: String, - pub revocation_notification_port: String, - pub revocation_cert: String, - pub revocation_actions: String, - pub payload_script: String, - pub enable_insecure_payload: bool, - pub allow_payload_revocation_actions: bool, - pub tpm_hash_alg: HashAlgorithm, - pub tpm_encryption_alg: EncryptionAlgorithm, - pub tpm_signing_alg: SignAlgorithm, - pub ek_handle: Option, - pub run_as: Option, - pub agent_data_path: Option, -} - -impl KeylimeConfig { - pub fn build() -> Result { - let conf_name = config_file_get(); - let conf = match Ini::load_from_file(&conf_name) { - Ok(file) => file, - Err(e) => { - error!( - "Could not load keylime config file: {} due to error: {}", - conf_name, e - ); - return Err(Error::Ini(e)); - } - }; - - let ip = config_get_env( - &conf_name, - &conf, - "agent", - "ip", - "CLOUDAGENT_IP", - )?; - let port = config_get_env( - &conf_name, - &conf, - "agent", - "port", - "CLOUDAGENT_PORT", - )?; - let registrar_ip = config_get_env( - &conf_name, - &conf, - "agent", - "registrar_ip", - "REGISTRAR_IP", - )?; - let registrar_port = config_get_env( - &conf_name, - &conf, - "agent", - "registrar_port", - "REGISTRAR_PORT", - )?; - let agent_uuid_config = - config_get(&conf_name, &conf, "agent", "uuid")?; - let uuid = get_uuid(&agent_uuid_config); - let contact_ip = cloudagent_contact_ip_get(&conf_name, &conf); - let contact_port = cloudagent_contact_port_get(&conf_name, &conf)?; - let tpm_hash_alg = HashAlgorithm::try_from( - config_get(&conf_name, &conf, "agent", "tpm_hash_alg")?.as_str(), - )?; - let tpm_encryption_alg = EncryptionAlgorithm::try_from( - config_get(&conf_name, &conf, "agent", "tpm_encryption_alg")? - .as_str(), - )?; - let tpm_signing_alg = SignAlgorithm::try_from( - config_get(&conf_name, &conf, "agent", "tpm_signing_alg")? - .as_str(), - )?; - let enable_revocation_notifications = bool::from_str( - &config_get( - &conf_name, - &conf, - "agent", - "enable_revocation_notifications", - )? - .to_lowercase(), - )?; - - let revocation_cert = - config_get(&conf_name, &conf, "agent", "revocation_cert")?; - let revocation_notification_ip = config_get( - &conf_name, - &conf, - "agent", - "revocation_notification_ip", - )?; - let revocation_notification_port = config_get( - &conf_name, - &conf, - "agent", - "revocation_notification_port", - )?; - - let secure_size = - config_get(&conf_name, &conf, "agent", "secure_size")?; - let payload_script = - config_get(&conf_name, &conf, "agent", "payload_script")?; - let dec_payload_file = - config_get(&conf_name, &conf, "agent", "dec_payload_file")?; - - let enc_keyname = - config_get(&conf_name, &conf, "agent", "enc_keyname")?; - let extract_payload_zip = bool::from_str( - &config_get(&conf_name, &conf, "agent", "extract_payload_zip")? - .to_lowercase(), - )?; - - let keylime_dir = config_get_env( - &conf_name, - &conf, - "agent", - "keylime_dir", - "KEYLIME_DIR", - ) - .or_else::(|_| Ok(String::from(WORK_DIR)))?; - - let mut agent_data_path = config_get_file_path( - &conf_name, - &conf, - "agent", - "agent_data_path", - &keylime_dir, - AGENT_DATA, - )?; - - let mut server_key = config_get_file_path( - &conf_name, - &conf, - "agent", - "server_key", - &keylime_dir, - DEFAULT_KEY_PATH, - )?; - - let mut server_cert = config_get_file_path( - &conf_name, - &conf, - "agent", - "server_cert", - &keylime_dir, - DEFAULT_CERT_PATH, - )?; - - let mut trusted_client_ca = config_get_file_path( - &conf_name, - &conf, - "agent", - "trusted_client_ca", - &keylime_dir, - DEFAULT_CA_PATH, - )?; - - let server_key_password = - config_get(&conf_name, &conf, "agent", "server_key_password") - .ok(); - - let revocation_actions = - config_get(&conf_name, &conf, "agent", "revocation_actions") - .or_else::(|_| Ok(String::from(REV_ACTIONS)))?; - let revocation_actions_dir = - config_get(&conf_name, &conf, "agent", "revocation_actions_dir") - .or_else::(|_| Ok(String::from(REV_ACTIONS_DIR)))?; - let allow_payload_revocation_actions = match config_get( - &conf_name, - &conf, - "agent", - "allow_payload_revocation_actions", - ) { - Ok(s) => bool::from_str(&s.to_lowercase())?, - Err(_) => ALLOW_PAYLOAD_REV_ACTIONS, - }; - - let run_as = if permissions::get_euid() == 0 { - match config_get(&conf_name, &conf, "agent", "run_as") { - Ok(user_group) => { - if user_group.is_empty() { - warn!("Cannot drop privileges since 'run_as' is empty in 'agent' section of keylime-agent.conf."); - None - } else { - Some(user_group) - } - } - Err(_) => { - warn!("Cannot drop privileges since 'run_as' is missing in 'agent' section of keylime-agent.conf."); - None - } - } - } else { - None - }; - - let enable_agent_mtls = - match config_get(&conf_name, &conf, "agent", "enable_agent_mtls") - { - Ok(enabled) => bool::from_str(&enabled.to_lowercase()) - .or::(Ok(MTLS_ENABLED))?, - Err(_) => true, - }; - - let enable_insecure_payload = match config_get( - &conf_name, - &conf, - "agent", - "enable_insecure_payload", - ) { - Ok(allowed) => bool::from_str(&allowed.to_lowercase()) - .or::(Ok(ALLOW_INSECURE_PAYLOAD))?, - Err(_) => false, - }; - - let tpm_ownerpassword = - config_get(&conf_name, &conf, "agent", "tpm_ownerpassword") - .ok() - .filter(|s| s != "generate"); - - let ek_handle = config_get(&conf_name, &conf, "agent", "ek_handle") - .ok() - .filter(|s| s != "generate"); - - Ok(KeylimeConfig { - uuid, - ip, - port, - contact_ip, - contact_port, - registrar_ip, - registrar_port, - enable_agent_mtls, - keylime_dir, - server_key, - server_cert, - server_key_password, - trusted_client_ca, - enc_keyname, - dec_payload_file, - secure_size, - tpm_ownerpassword, - extract_payload_zip, - enable_revocation_notifications, - revocation_actions_dir, - revocation_notification_ip, - revocation_notification_port, - revocation_cert, - revocation_actions, - payload_script, - enable_insecure_payload, - allow_payload_revocation_actions, - tpm_hash_alg, - tpm_encryption_alg, - tpm_signing_alg, - ek_handle, - run_as, - agent_data_path, - }) - } -} - /// Calculate the SHA-256 hash of the TPM public key in PEM format /// /// This is used as the agent UUID when the configuration option 'uuid' is set as 'hash_ek' @@ -537,312 +233,3 @@ pub(crate) fn hash_ek_pubkey(ek_pub: Public) -> Result { let mut hash = hash(MessageDigest::sha256(), &pem)?; Ok(hex::encode(hash)) } - -// Default test configuration. This should match the defaults in keylime-agent.conf -#[cfg(any(test, feature = "testing"))] -impl Default for KeylimeConfig { - fn default() -> Self { - // In case the tests are executed by privileged user - let run_as = if permissions::get_euid() == 0 { - Some("keylime:tss".to_string()) - } else { - None - }; - - KeylimeConfig { - ip: "127.0.0.1".to_string(), - port: "9002".to_string(), - registrar_ip: "127.0.0.1".to_string(), - registrar_port: "8890".to_string(), - uuid: "d432fbb3-d2f1-4a97-9ef7-75bd81c00000".to_string(), - contact_ip: Some("127.0.0.1".to_string()), - contact_port: Some(9002), - tpm_hash_alg: HashAlgorithm::Sha256, - tpm_encryption_alg: EncryptionAlgorithm::Rsa, - tpm_signing_alg: SignAlgorithm::RsaSsa, - agent_data_path: Some( - Path::new(WORK_DIR).join(AGENT_DATA).display().to_string(), - ), - enable_revocation_notifications: true, - revocation_cert: "default".to_string(), - revocation_notification_ip: "127.0.0.1".to_string(), - revocation_notification_port: "8992".to_string(), - secure_size: "1m".to_string(), - payload_script: "autorun.sh".to_string(), - dec_payload_file: "decrypted_payload".to_string(), - enc_keyname: "derived_tci_key".to_string(), - extract_payload_zip: true, - server_key: Some( - Path::new(WORK_DIR) - .join(DEFAULT_KEY_PATH) - .display() - .to_string(), - ), - server_key_password: None, - server_cert: Some( - Path::new(WORK_DIR) - .join(DEFAULT_CERT_PATH) - .display() - .to_string(), - ), - trusted_client_ca: Some( - Path::new(WORK_DIR) - .join(DEFAULT_CA_PATH) - .display() - .to_string(), - ), - revocation_actions: "".to_string(), - revocation_actions_dir: "/usr/libexec/keylime".to_string(), - allow_payload_revocation_actions: true, - keylime_dir: WORK_DIR.to_string(), - enable_agent_mtls: true, - enable_insecure_payload: false, - run_as, - tpm_ownerpassword: None, - ek_handle: None, - } - } -} - -fn get_uuid(agent_uuid_config: &str) -> String { - match agent_uuid_config { - "openstack" => { - info!("Openstack placeholder..."); - "openstack".into() - } - "hash_ek" => { - info!("Using hashed EK as UUID"); - // DO NOT change this to something else. It is used by KeylimeConfig to later set the correct value. - "hash_ek".into() - } - "generate" => { - let agent_uuid = Uuid::new_v4(); - info!("Generated a new UUID: {}", &agent_uuid); - agent_uuid.to_string() - } - uuid_config => match Uuid::parse_str(uuid_config) { - Ok(uuid_config) => uuid_config.to_string(), - Err(_) => { - info!("Misformatted UUID: {}", &uuid_config); - let agent_uuid = Uuid::new_v4(); - agent_uuid.to_string() - } - }, - } -} - -/* - * Return: Returns the configuration file provided in the environment variable - * KEYLIME_CONFIG or defaults to /etc/keylime-agent.conf - * - * Example call: - * let config = config_file_get(); - */ -fn config_file_get() -> String { - match env::var("KEYLIME_CONFIG") { - Ok(cfg) => { - // The variable length must be larger than 0 to accept - if !cfg.is_empty() { - cfg - } else { - String::from(DEFAULT_CONFIG) - } - } - _ => String::from(DEFAULT_CONFIG), - } -} - -/// Returns the contact ip for the agent if set -fn cloudagent_contact_ip_get(conf_name: &str, conf: &Ini) -> Option { - match config_get_env( - conf_name, - conf, - "agent", - "contact_ip", - "KEYLIME_AGENT_CONTACT_IP", - ) { - Ok(ip) => Some(ip), - Err(_) => None, // Ignore errors because this option might not be set - } -} - -/// Returns the contact ip for the agent if set -fn cloudagent_contact_port_get( - conf_name: &str, - conf: &Ini, -) -> Result> { - match config_get_env( - conf_name, - conf, - "agent", - "contact_port", - "KEYLIME_AGENT_CONTACT_PORT", - ) { - Ok(port_str) => match port_str.parse::() { - Ok(port) => Ok(Some(port)), - _ => Err(Error::Configuration(format!( - "Parse {} to a port number.", - port_str - ))), - }, - _ => Ok(None), // Ignore errors because this option might not be set - } -} - -/* - * Input: conf_name, conf, [section] and key - * Return: Returns the matched key - * - * Example call: - * let port = common::config_get(conf_file_name, file_Ini,"agent","port"); - */ -fn config_get( - conf_name: &str, - conf: &Ini, - section: &str, - key: &str, -) -> Result { - let section = match conf.section(Some(section.to_owned())) { - Some(section) => section, - None => - // TODO: Make Error::Configuration an alternative with data instead of string - { - return Err(Error::Configuration(format!( - "Cannot find section called {} in file {}", - section, conf_name - ))) - } - }; - let value = match section.get(key) { - Some(value) => value.trim(), - None => - // TODO: Make Error::Configuration an alternative with data instead of string - { - return Err(Error::Configuration(format!( - "Cannot find key {} in file {}", - key, conf_name - ))) - } - }; - - if value.is_empty() { - warn!("Cannot find value for key {} in file {}", key, conf_name); - }; - - Ok(value.to_string()) -} - -/// Get a file path from the configuration file. -/// -/// If the value is set as "default", return the provided default path relative from the provided -/// work_dir. -/// -/// If the option is empty, return Ok(None) -/// -/// If the option is not found in the provided configuration file, return error -fn config_get_file_path( - conf_name: &str, - conf: &Ini, - section: &str, - key: &str, - work_dir: &str, - default: &str, -) -> Result> { - match config_get(conf_name, conf, section, key) { - Ok(path) => { - if path == "default" { - Ok(Some( - Path::new(work_dir).join(default).display().to_string(), - )) - } else if path.is_empty() { - Ok(None) - } else { - let path = Path::new(&path); - if path.is_relative() { - Ok(Some( - Path::new(work_dir).join(path).display().to_string(), - )) - } else { - Ok(Some(path.display().to_string())) - } - } - } - Err(e) => Err(e), - } -} - -/* - * Input: conf_name, conf,[section] and key and environment variable - * Return: Returns the matched key - * - * Example call: - * let port = common::config_get_env(conf_file_name, file_Ini, "general","cloudagent_port", "CLOUDAGENT_PORT"); - */ -fn config_get_env( - conf_name: &str, - conf: &Ini, - section: &str, - key: &str, - env: &str, -) -> Result { - match env::var(env) { - Ok(ip) => { - // The variable length must be larger than 0 to accept - if !ip.is_empty() { - Ok(ip) - } else { - config_get(conf_name, conf, section, key) - } - } - _ => config_get(conf_name, conf, section, key), - } -} - -// Unit Testing -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_get_parameters_exist() { - //let result = config_get("keylime-agent.conf", "general", "cloudagent_port"); - //assert_eq!(result, "9002"); - } - - #[test] - fn test_config_file_get() { - let conf_orig = option_env!("KEYLIME_CONFIG").or(Some("")).unwrap(); //#[allow_ci] - - // Test with no environment variable - env::set_var("KEYLIME_CONFIG", ""); - assert_eq!( - config_file_get(), - String::from("/etc/keylime-agent.conf") - ); - - // Test with an environment variable - env::set_var("KEYLIME_CONFIG", "/tmp/testing.conf"); - assert_eq!(config_file_get(), String::from("/tmp/testing.conf")); - // Reset environment - env::set_var("KEYLIME_CONFIG", conf_orig); - } - - #[test] - fn test_get_uuid() { - assert_eq!(get_uuid("openstack"), "openstack"); - assert_eq!(get_uuid("hash_ek"), "hash_ek"); - let _ = Uuid::parse_str(&get_uuid("generate")).unwrap(); //#[allow_ci] - assert_eq!( - get_uuid("D432FBB3-D2F1-4A97-9EF7-75BD81C00000"), - "d432fbb3-d2f1-4a97-9ef7-75bd81c00000" - ); - assert_ne!( - get_uuid("D432FBB3-D2F1-4A97-9EF7-75BD81C0000X"), - "d432fbb3-d2f1-4a97-9ef7-75bd81c0000X" - ); - let _ = Uuid::parse_str(&get_uuid( - "D432FBB3-D2F1-4A97-9EF7-75BD81C0000X", - )) - .unwrap(); //#[allow_ci] - } -} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 000000000..74cb827fc --- /dev/null +++ b/src/config.rs @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Keylime Authors + +use crate::{ + algorithms::{EncryptionAlgorithm, HashAlgorithm, SignAlgorithm}, + error::Error, + permissions, tpm, +}; +use config::{ + builder::DefaultState, Config, ConfigBuilder, ConfigError, Environment, + File, FileFormat, Map, Source, Value, +}; +use glob::glob; +use log::*; +use serde::{Deserialize, Serialize}; +use std::{ + env, + path::{Path, PathBuf}, +}; +use toml::value::Table; +use uuid::Uuid; + +pub static DEFAULT_UUID: &str = "d432fbb3-d2f1-4a97-9ef7-75bd81c00000"; +pub static DEFAULT_IP: &str = "127.0.0.1"; +pub static DEFAULT_PORT: u32 = 9002; +pub static DEFAULT_CONTACT_IP: &str = "127.0.0.1"; +pub static DEFAULT_CONTACT_PORT: u32 = 9002; +pub static DEFAULT_REGISTRAR_IP: &str = "127.0.0.1"; +pub static DEFAULT_REGISTRAR_PORT: u32 = 8890; +pub static DEFAULT_ENABLE_AGENT_MTLS: bool = true; +pub static DEFAULT_KEYLIME_DIR: &str = "/var/lib/keylime"; +pub static DEFAULT_SERVER_KEY: &str = "server-private.pem"; +pub static DEFAULT_SERVER_CERT: &str = "server-cert.crt"; +pub static DEFAULT_SERVER_KEY_PASSWORD: &str = ""; +// The DEFAULT_TRUSTED_CLIENT_CA is relative from KEYLIME_DIR +pub static DEFAULT_TRUSTED_CLIENT_CA: &str = "cv_ca/cacert.crt"; +pub static DEFAULT_ENC_KEYNAME: &str = "derived_tci_key"; +pub static DEFAULT_DEC_PAYLOAD_FILE: &str = "decrypted_payload"; +pub static DEFAULT_SECURE_SIZE: &str = "1m"; +pub static DEFAULT_TPM_OWNERPASSWORD: &str = ""; +pub static DEFAULT_EXTRACT_PAYLOAD_ZIP: bool = true; +pub static DEFAULT_ENABLE_REVOCATION_NOTIFICATIONS: bool = true; +pub static DEFAULT_REVOCATION_ACTIONS_DIR: &str = "/usr/libexec/keylime"; +pub static DEFAULT_REVOCATION_NOTIFICATION_IP: &str = "127.0.0.1"; +pub static DEFAULT_REVOCATION_NOTIFICATION_PORT: u32 = 8992; +// Note: The revocation certificate name is generated inside the Python tenant and the +// certificate(s) can be generated by running the tenant with the --cert flag. For more +// information, check the README: https://github.com/keylime/keylime/#using-keylime-ca +pub static DEFAULT_REVOCATION_CERT: &str = "RevocationNotifier-cert.crt"; +pub static DEFAULT_REVOCATION_ACTIONS: &str = ""; +pub static DEFAULT_PAYLOAD_SCRIPT: &str = "autorun.sh"; +pub static DEFAULT_ENABLE_INSECURE_PAYLOAD: bool = false; +pub static DEFAULT_ALLOW_PAYLOAD_REVOCATION_ACTIONS: bool = true; +pub static DEFAULT_TPM_HASH_ALG: &str = "sha256"; +pub static DEFAULT_TPM_ENCRYPTION_ALG: &str = "rsa"; +pub static DEFAULT_TPM_SIGNING_ALG: &str = "rsassa"; +pub static DEFAULT_EK_HANDLE: &str = "generate"; +pub static DEFAULT_RUN_AS: &str = "keylime:tss"; +pub static DEFAULT_AGENT_DATA_PATH: &str = "agent_data.json"; +pub static DEFAULT_CONFIG: &str = "/etc/keylime-agent.conf"; +pub static DEFAULT_CONFIG_SYS: &str = "/usr/etc/keylime-agent.conf"; + +impl Source for KeylimeConfig { + fn collect(&self) -> Result, ConfigError> { + let agent: Map = Map::from([ + ("uuid".to_string(), self.agent.uuid.to_string().into()), + ("ip".to_string(), self.agent.ip.to_string().into()), + ("port".to_string(), Value::from(self.agent.port)), + ( + "contact_ip".to_string(), + if let Some(ref s) = self.agent.contact_ip { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "contact_port".to_string(), + if let Some(ref s) = self.agent.contact_port { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "registrar_ip".to_string(), + self.agent.registrar_ip.to_string().into(), + ), + ( + "registrar_port".to_string(), + self.agent.registrar_port.into(), + ), + ( + "enable_agent_mtls".to_string(), + self.agent.enable_agent_mtls.into(), + ), + ( + "keylime_dir".to_string(), + self.agent.keylime_dir.to_string().into(), + ), + ( + "server_key".to_string(), + if let Some(ref s) = self.agent.server_key { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "server_key_password".to_string(), + if let Some(ref s) = self.agent.server_key_password { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "server_cert".to_string(), + if let Some(ref s) = self.agent.server_cert { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "trusted_client_ca".to_string(), + if let Some(ref s) = self.agent.trusted_client_ca { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "enc_keyname".to_string(), + self.agent.enc_keyname.to_string().into(), + ), + ( + "dec_payload_file".to_string(), + self.agent.dec_payload_file.to_string().into(), + ), + ( + "secure_size".to_string(), + self.agent.secure_size.to_string().into(), + ), + ( + "tpm_ownerpassword".to_string(), + if let Some(ref s) = self.agent.tpm_ownerpassword { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "extract_payload_zip".to_string(), + self.agent.extract_payload_zip.into(), + ), + ( + "enable_revocation_notifications".to_string(), + self.agent.enable_revocation_notifications.into(), + ), + ( + "revocation_actions_dir".to_string(), + if let Some(ref s) = self.agent.revocation_actions_dir { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "revocation_notification_ip".to_string(), + if let Some(ref s) = self.agent.revocation_notification_ip { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "revocation_notification_port".to_string(), + if let Some(ref s) = self.agent.revocation_notification_port { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "revocation_cert".to_string(), + if let Some(ref s) = self.agent.revocation_cert { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "revocation_actions".to_string(), + if let Some(ref s) = self.agent.revocation_actions { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "payload_script".to_string(), + self.agent.payload_script.to_string().into(), + ), + ( + "enable_insecure_payload".to_string(), + self.agent.enable_insecure_payload.to_string().into(), + ), + ( + "allow_payload_revocation_actions".to_string(), + self.agent.allow_payload_revocation_actions.into(), + ), + ( + "tpm_hash_alg".to_string(), + self.agent.tpm_hash_alg.to_string().into(), + ), + ( + "tpm_encryption_alg".to_string(), + self.agent.tpm_encryption_alg.to_string().into(), + ), + ( + "tpm_signing_alg".to_string(), + self.agent.tpm_signing_alg.to_string().into(), + ), + ( + "ek_handle".to_string(), + if let Some(ref s) = self.agent.ek_handle { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "run_as".to_string(), + if let Some(ref s) = self.agent.run_as { + s.to_string().into() + } else { + "".into() + }, + ), + ( + "agent_data_path".to_string(), + if let Some(ref s) = self.agent.agent_data_path { + s.to_string().into() + } else { + "".into() + }, + ), + ]); + + Ok(Map::from([("agent".to_string(), agent.into())])) + } + + fn clone_into_box(&self) -> Box { + Box::new(self.clone()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub(crate) struct KeylimeConfig { + pub agent: AgentConfig, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub(crate) struct AgentConfig { + pub uuid: String, + pub ip: String, + pub port: u32, + pub contact_ip: Option, + pub contact_port: Option, + pub registrar_ip: String, + pub registrar_port: u32, + pub enable_agent_mtls: bool, + pub keylime_dir: String, + pub server_key: Option, + pub server_cert: Option, + pub server_key_password: Option, + pub trusted_client_ca: Option, + pub enc_keyname: String, + pub dec_payload_file: String, + pub secure_size: String, + pub tpm_ownerpassword: Option, + pub extract_payload_zip: bool, + pub enable_revocation_notifications: bool, + pub revocation_actions_dir: Option, + pub revocation_notification_ip: Option, + pub revocation_notification_port: Option, + pub revocation_cert: Option, + pub revocation_actions: Option, + pub payload_script: String, + pub enable_insecure_payload: bool, + pub allow_payload_revocation_actions: bool, + pub tpm_hash_alg: String, + pub tpm_encryption_alg: String, + pub tpm_signing_alg: String, + pub ek_handle: Option, + pub run_as: Option, + pub agent_data_path: Option, +} + +impl Default for AgentConfig { + fn default() -> Self { + // In case the process is executed by privileged user + let run_as = if permissions::get_euid() == 0 { + Some(DEFAULT_RUN_AS.to_string()) + } else { + None + }; + + AgentConfig { + ip: DEFAULT_IP.to_string(), + port: DEFAULT_PORT, + registrar_ip: DEFAULT_REGISTRAR_IP.to_string(), + registrar_port: DEFAULT_REGISTRAR_PORT, + uuid: DEFAULT_UUID.to_string(), + contact_ip: Some(DEFAULT_CONTACT_IP.to_string()), + contact_port: Some(DEFAULT_CONTACT_PORT), + tpm_hash_alg: DEFAULT_TPM_HASH_ALG.to_string(), + tpm_encryption_alg: DEFAULT_TPM_ENCRYPTION_ALG.to_string(), + tpm_signing_alg: DEFAULT_TPM_SIGNING_ALG.to_string(), + agent_data_path: Some("default".to_string()), + enable_revocation_notifications: + DEFAULT_ENABLE_REVOCATION_NOTIFICATIONS, + revocation_cert: Some("default".to_string()), + revocation_notification_ip: Some( + DEFAULT_REVOCATION_NOTIFICATION_IP.to_string(), + ), + revocation_notification_port: Some( + DEFAULT_REVOCATION_NOTIFICATION_PORT, + ), + secure_size: DEFAULT_SECURE_SIZE.to_string(), + payload_script: DEFAULT_PAYLOAD_SCRIPT.to_string(), + dec_payload_file: DEFAULT_DEC_PAYLOAD_FILE.to_string(), + enc_keyname: DEFAULT_ENC_KEYNAME.to_string(), + extract_payload_zip: DEFAULT_EXTRACT_PAYLOAD_ZIP, + server_key: Some("default".to_string()), + server_key_password: Some( + DEFAULT_SERVER_KEY_PASSWORD.to_string(), + ), + server_cert: Some("default".to_string()), + trusted_client_ca: Some("default".to_string()), + revocation_actions: Some(DEFAULT_REVOCATION_ACTIONS.to_string()), + revocation_actions_dir: Some( + DEFAULT_REVOCATION_ACTIONS_DIR.to_string(), + ), + allow_payload_revocation_actions: + DEFAULT_ALLOW_PAYLOAD_REVOCATION_ACTIONS, + keylime_dir: DEFAULT_KEYLIME_DIR.to_string(), + enable_agent_mtls: DEFAULT_ENABLE_AGENT_MTLS, + enable_insecure_payload: DEFAULT_ENABLE_INSECURE_PAYLOAD, + run_as, + tpm_ownerpassword: Some(DEFAULT_TPM_OWNERPASSWORD.to_string()), + ek_handle: Some(DEFAULT_EK_HANDLE.to_string()), + } + } +} + +impl Default for KeylimeConfig { + fn default() -> Self { + let c = KeylimeConfig { + agent: AgentConfig::default(), + }; + + // The default config should never fail to translate keywords + config_translate_keywords(&c).unwrap() //#[allow_ci] + } +} + +fn config_get_file_setting() -> Result, Error> { + let default_config = KeylimeConfig::default(); + + Ok(Config::builder() + // Default values + .add_source(default_config) + // Add system configuration file + .add_source( + File::new(DEFAULT_CONFIG_SYS, FileFormat::Toml).required(false), + ) + // Add system configuration snippets + .add_source( + glob("/usr/etc/keylime-agent.conf.d/*") + .map_err(Error::GlobPattern)? + .filter_map(|entry| entry.ok()) + .map(|path| { + File::new(&path.display().to_string(), FileFormat::Toml) + .required(false) + }) + .collect::>(), + ) + .add_source( + File::new(DEFAULT_CONFIG, FileFormat::Toml).required(false), + ) + // Add user configuration snippets + .add_source( + glob("/etc/keylime-agent.conf.d/*") + .map_err(Error::GlobPattern)? + .filter_map(|entry| entry.ok()) + .map(|path| { + File::new(&path.display().to_string(), FileFormat::Toml) + .required(false) + }) + .collect::>(), + ) + // Add environment variables overrides + .add_source( + Environment::with_prefix("KEYLIME") + .separator("_") + .prefix_separator("_"), + )) +} + +fn config_get_setting() -> Result, Error> { + if let Ok(env_cfg) = env::var("KEYLIME_AGENT_CONFIG") { + if !env_cfg.is_empty() { + let path = Path::new(&env_cfg); + if (path.exists()) { + return Ok(Config::builder() + .add_source( + File::new(&env_cfg, FileFormat::Toml).required(true), + ) + // Add environment variables overrides + .add_source( + Environment::with_prefix("KEYLIME") + .prefix_separator("_") + .separator("_"), + )); + } else { + warn!("Configuration set in KEYLIME_AGENT_CONFIG environment variable not found"); + return Err(Error::Configuration("Configuration set in KEYLIME_AGENT_CONFIG environment variable not found".to_string())); + } + } + } + config_get_file_setting() +} + +/// Replace the options that support keywords with the final value +fn config_translate_keywords( + config: &KeylimeConfig, +) -> Result { + let uuid = get_uuid(&config.agent.uuid); + + let mut agent_data_path = config_get_file_path( + &config.agent.agent_data_path, + &config.agent.keylime_dir, + DEFAULT_AGENT_DATA_PATH, + ); + + let mut server_key = config_get_file_path( + &config.agent.server_key, + &config.agent.keylime_dir, + DEFAULT_SERVER_KEY, + ); + + let mut server_cert = config_get_file_path( + &config.agent.server_cert, + &config.agent.keylime_dir, + DEFAULT_SERVER_CERT, + ); + + let mut trusted_client_ca = config_get_file_path( + &config.agent.trusted_client_ca, + &config.agent.keylime_dir, + DEFAULT_TRUSTED_CLIENT_CA, + ); + + let mut revocation_cert = config_get_file_path( + &config.agent.revocation_cert, + &config.agent.keylime_dir, + &format!("secure/unzipped/{}", DEFAULT_REVOCATION_CERT), + ); + + let tpm_ownerpassword = match config.agent.tpm_ownerpassword { + Some(ref s) => { + if s.as_str() != "generate" { + Some(s.to_string()) + } else { + None + } + } + None => None, + }; + + let ek_handle = match config.agent.ek_handle { + Some(ref s) => { + if s.as_str() != "generate" { + Some(s.to_string()) + } else { + None + } + } + None => None, + }; + + // Validate the configuration + + // If mTLS is enabled, the trusted client CA certificate is required + if config.agent.enable_agent_mtls + && config.agent.trusted_client_ca.is_none() + { + error!("The option 'enable_agent_mtls' is set as 'true' but no certificate was set in 'trusted_client_ca' option"); + return Err(Error::Configuration( + "The option 'enable_agent_mtls' is set as 'true' but no certificate was set in 'trusted_client_ca' option".to_string())); + } + + // If revocation notifications is enabled, verify all the required options for revocation + if config.agent.enable_revocation_notifications { + if config.agent.revocation_notification_ip.is_none() { + error!("The option 'enable_revocation_notifications' is set as 'true' but no IP was set in 'revocation_notification_ip'"); + return Err(Error::Configuration("The option 'enable_revocation_notifications' is set as 'true' but no IP was set in 'revocation_notification_ip'".to_string())); + } + if config.agent.revocation_notification_port.is_none() { + error!("The option 'enable_revocation_notifications' is set as 'true' but no port was set in 'revocation_notification_port'"); + return Err(Error::Configuration("The option 'enable_revocation_notifications' is set as 'true' but no port was set in 'revocation_notification_port'".to_string())); + } + if config.agent.revocation_cert.is_none() { + error!("The option 'enable_revocation_notifications' is set as 'true' but no certificate was set in 'revocation_cert'"); + return Err(Error::Configuration("The option 'enable_revocation_notifications' is set as 'true' but no certificate was set in 'revocation_notification_cert'".to_string())); + } + let actions_dir = match config.agent.revocation_actions_dir { + Some(ref dir) => dir.to_string(), + None => { + error!("The option 'enable_revocation_notifications' is set as 'true' but the revocation actions directory was not set in 'revocation_actions_dir'"); + return Err(Error::Configuration("The option 'enable_revocation_notifications' is set as 'true' but the revocation actions directory was not set in 'revocation_actions_dir'".to_string())); + } + }; + } + + Ok(KeylimeConfig { + agent: AgentConfig { + uuid, + server_key, + server_cert, + trusted_client_ca, + tpm_ownerpassword, + ek_handle, + agent_data_path, + revocation_cert, + ..config.agent.clone() + }, + }) +} + +impl KeylimeConfig { + pub fn new() -> Result { + // Get the base configuration file from the environment variable or the default locations + let setting = config_get_setting()?.build()?; + let config: KeylimeConfig = setting.try_deserialize()?; + + // Replace keywords with actual values + config_translate_keywords(&config) + } +} + +/// Expand a file path from the configuration file. +/// +/// If the option is None, return None +/// If the option string is empty, return None +/// If the option string is set as "default", return the provided default path relative from the provided work_dir. +/// If the option string is a relative path, return the path relative from the provided work_dir +/// If the option string is an absolute path, return the path without change. +fn config_get_file_path( + path: &Option, + work_dir: &str, + default: &str, +) -> Option { + if let Some(ref value) = path { + if value == "default" { + return Some( + Path::new(work_dir).join(default).display().to_string(), + ); + } else if value.is_empty() { + return None; + } else { + let value = Path::new(&value); + if value.is_relative() { + return Some( + Path::new(work_dir).join(value).display().to_string(), + ); + } else { + return Some(value.display().to_string()); + } + } + } + None +} + +fn get_uuid(agent_uuid_config: &str) -> String { + match agent_uuid_config { + "openstack" => { + info!("Openstack placeholder..."); + "openstack".into() + } + "hash_ek" => { + info!("Using hashed EK as UUID"); + // DO NOT change this to something else. It is used later to set the correct value. + "hash_ek".into() + } + "generate" => { + let agent_uuid = Uuid::new_v4(); + info!("Generated a new UUID: {}", &agent_uuid); + agent_uuid.to_string() + } + uuid_config => match Uuid::parse_str(uuid_config) { + Ok(uuid_config) => uuid_config.to_string(), + Err(_) => { + info!("Misformatted UUID: {}", &uuid_config); + let agent_uuid = Uuid::new_v4(); + agent_uuid.to_string() + } + }, + } +} + +// Unit Testing +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default() { + let default = KeylimeConfig::default(); + } + + #[test] + fn get_revocation_cert_path_default() { + let test_config = KeylimeConfig::default(); + let revocation_cert_path = + (test_config.agent.revocation_cert.clone()).unwrap(); //#[allow_ci] + let mut expected = Path::new(&test_config.agent.keylime_dir) + .join("secure/unzipped") + .join(DEFAULT_REVOCATION_CERT) + .display() + .to_string(); + assert_eq!(revocation_cert_path, expected); + } + + #[test] + fn get_revocation_cert_path_absolute() { + let mut test_config = KeylimeConfig { + agent: AgentConfig { + revocation_cert: Some(String::from("/test/cert.crt")), + ..Default::default() + }, + }; + let result = config_translate_keywords(&test_config); + assert!(result.is_ok()); + let test_config = result.unwrap(); //#[allow_ci] + let revocation_cert_path = + (test_config.agent.revocation_cert).unwrap(); //#[allow_ci] + let mut expected = Path::new("/test/cert.crt").display().to_string(); + assert_eq!(revocation_cert_path, expected); + } + + #[test] + fn get_revocation_cert_path_relative() { + let mut test_config = KeylimeConfig { + agent: AgentConfig { + revocation_cert: Some(String::from("cert.crt")), + ..Default::default() + }, + }; + let result = config_translate_keywords(&test_config); + assert!(result.is_ok()); + let test_config = result.unwrap(); //#[allow_ci] + let revocation_cert_path = + (test_config.agent.revocation_cert.clone()).unwrap(); //#[allow_ci] + let mut expected = Path::new(&test_config.agent.keylime_dir) + .join("cert.crt") + .display() + .to_string(); + assert_eq!(revocation_cert_path, expected); + } + + #[test] + fn get_revocation_cert_path_empty() { + let mut test_config = KeylimeConfig { + agent: AgentConfig { + revocation_cert: Some(String::from("")), + ..Default::default() + }, + }; + let result = config_translate_keywords(&test_config); + assert!(result.is_ok()); + let test_config = result.unwrap(); //#[allow_ci] + assert_eq!(test_config.agent.revocation_cert, None); + } + + #[test] + fn get_revocation_cert_path_none() { + let mut test_config = KeylimeConfig { + agent: AgentConfig { + revocation_cert: None, + ..Default::default() + }, + }; + let result = config_translate_keywords(&test_config); + // Due to enable_revocation_notifications being set + assert!(result.is_err()); + let mut test_config = KeylimeConfig { + agent: AgentConfig { + enable_revocation_notifications: false, + revocation_cert: None, + ..Default::default() + }, + }; + + // Now unset enable_revocation_notifications and check that is allowed + let result = config_translate_keywords(&test_config); + assert!(result.is_ok()); + let test_config = result.unwrap(); //#[allow_ci] + assert_eq!(test_config.agent.revocation_cert, None); + } + + #[test] + fn test_get_uuid() { + assert_eq!(get_uuid("openstack"), "openstack"); + assert_eq!(get_uuid("hash_ek"), "hash_ek"); + let _ = Uuid::parse_str(&get_uuid("generate")).unwrap(); //#[allow_ci] + assert_eq!( + get_uuid("D432FBB3-D2F1-4A97-9EF7-75BD81C00000"), + "d432fbb3-d2f1-4a97-9ef7-75bd81c00000" + ); + assert_ne!( + get_uuid("D432FBB3-D2F1-4A97-9EF7-75BD81C0000X"), + "d432fbb3-d2f1-4a97-9ef7-75bd81c0000X" + ); + let _ = Uuid::parse_str(&get_uuid( + "D432FBB3-D2F1-4A97-9EF7-75BD81C0000X", + )) + .unwrap(); //#[allow_ci] + } +} diff --git a/src/error.rs b/src/error.rs index d43ce0528..403b9ed1e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub(crate) enum Error { #[allow(unused)] InvalidRequest, #[error("Configuration loading error: {0}")] - Ini(#[from] ini::Error), + Config(#[from] config::ConfigError), #[error("Infallible: {0}")] Infallible(#[from] std::convert::Infallible), #[error("Compress tools error: {0}")] @@ -38,6 +38,10 @@ pub(crate) enum Error { Serde(#[from] serde_json::Error), #[error("Permission error")] Permission, + #[error("Glob error")] + Glob(#[from] glob::GlobError), + #[error("Glob pattern error")] + GlobPattern(#[from] glob::PatternError), #[error("IO error: {0}")] Io(#[from] std::io::Error), #[error("Text decoding error: {0}")] diff --git a/src/keys_handler.rs b/src/keys_handler.rs index 0cec15d5b..ea1c39e93 100644 --- a/src/keys_handler.rs +++ b/src/keys_handler.rs @@ -273,14 +273,15 @@ pub async fn verify( #[cfg(test)] mod tests { use super::*; - use crate::common::{ - KeylimeConfig, AES_128_KEY_LEN, AES_256_KEY_LEN, API_VERSION, - }; - use crate::crypto::compute_hmac; #[cfg(feature = "testing")] use crate::crypto::testing::{ encrypt_aead, pkey_pub_from_pem, rsa_oaep_encrypt, }; + use crate::{ + common::{AES_128_KEY_LEN, AES_256_KEY_LEN, API_VERSION}, + config::KeylimeConfig, + crypto::compute_hmac, + }; use actix_rt::Arbiter; use actix_web::{test, web, App}; use openssl::{ @@ -363,7 +364,7 @@ mod tests { rsa_oaep_encrypt("edata.pub_key, u.bytes()).unwrap(); //#[allow_ci] let auth_tag = - compute_hmac(k.bytes(), test_config.agent_uuid.as_bytes()) + compute_hmac(k.bytes(), test_config.agent.uuid.as_bytes()) .unwrap(); //#[allow_ci] let ukey = KeylimeUKey { diff --git a/src/main.rs b/src/main.rs index 5e2ec6879..2700d2c71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ mod algorithms; mod common; +mod config; mod crypto; mod error; mod errors_handler; @@ -106,9 +107,9 @@ pub struct QuoteData { enc_alg: algorithms::EncryptionAlgorithm, sign_alg: algorithms::SignAlgorithm, agent_uuid: String, - revocation_cert: PathBuf, - revocation_actions: String, - revocation_actions_dir: PathBuf, + revocation_cert: Option, + revocation_actions: Option, + revocation_actions_dir: Option, allow_payload_revocation_actions: bool, secure_size: String, work_dir: PathBuf, @@ -137,7 +138,7 @@ pub(crate) fn decrypt_payload( // writing out symmetric key and encrypted payload. returns file paths for // both. pub(crate) fn setup_unzipped( - config: &KeylimeConfig, + config: &config::KeylimeConfig, mount: &Path, ) -> Result<(PathBuf, PathBuf, PathBuf)> { let unzipped = mount.join("unzipped"); @@ -147,8 +148,8 @@ pub(crate) fn setup_unzipped( fs::remove_dir_all(&unzipped)?; } - let dec_payload_path = unzipped.join(&config.dec_payload_file); - let key_path = unzipped.join(&config.enc_keyname); + let dec_payload_path = unzipped.join(&config.agent.dec_payload_file); + let key_path = unzipped.join(&config.agent.enc_keyname); fs::create_dir(&unzipped)?; @@ -224,10 +225,10 @@ pub(crate) fn run(dir: &Path, script: &str, uuid: &str) -> Result<()> { // the input string is the directory where the unzipped file(s) should be stored. pub(crate) fn optional_unzip_payload( unzipped: &Path, - config: &KeylimeConfig, + config: &config::KeylimeConfig, ) -> Result<()> { - if config.extract_payload_zip { - let zipped_payload = &config.dec_payload_file; + if config.agent.extract_payload_zip { + let zipped_payload = &config.agent.dec_payload_file; let zipped_payload_path = unzipped.join(zipped_payload); info!("Unzipping payload {} to {:?}", &zipped_payload, unzipped); @@ -243,7 +244,7 @@ pub(crate) async fn run_encrypted_payload( symm_key: Arc>>, symm_key_cvar: Arc, payload: Arc>>, - config: &KeylimeConfig, + config: &config::KeylimeConfig, mount: &Path, ) -> Result<()> { // do nothing until actix server's handlers have updated the symmetric key @@ -267,13 +268,13 @@ pub(crate) async fn run_encrypted_payload( optional_unzip_payload(&unzipped, config)?; // there may also be also a separate init script - match config.payload_script.as_str() { + match config.agent.payload_script.as_str() { "" => { info!("No payload script specified, skipping"); } script => { info!("Payload init script indicated: {}", script); - run(&unzipped, script, config.uuid.as_str())?; + run(&unzipped, script, config.agent.uuid.as_str())?; } } @@ -316,11 +317,12 @@ async fn worker( symm_key: Arc>>, symm_key_cvar: Arc, payload: Arc>>, - config: KeylimeConfig, + config: config::KeylimeConfig, mount: PathBuf, ) -> Result<()> { // Only run payload scripts if mTLS is enabled or 'enable_insecure_payload' option is set - if config.enable_agent_mtls || config.enable_insecure_payload { + if config.agent.enable_agent_mtls || config.agent.enable_insecure_payload + { run_encrypted_payload( symm_key, symm_key_cvar, @@ -335,7 +337,7 @@ async fn worker( // If with-zmq feature is enabled, run the service listening for ZeroMQ messages #[cfg(feature = "with-zmq")] - if config.enable_revocation_notifications { + if config.agent.enable_revocation_notifications { return revocation::run_revocation_service(&config, &mount).await; } @@ -395,13 +397,13 @@ async fn main() -> Result<()> { }; // Load config - let mut config = KeylimeConfig::build()?; + let mut config = config::KeylimeConfig::new()?; // The agent cannot run when a payload script is defined, but mTLS is disabled and insecure // payloads are not explicitly enabled - if !&config.enable_agent_mtls - && !&config.enable_insecure_payload - && !&config.payload_script.is_empty() + if !&config.agent.enable_agent_mtls + && !&config.agent.enable_insecure_payload + && !&config.agent.payload_script.is_empty() { let message = "The agent mTLS is disabled and 'payload_script' is not empty. To allow the agent to run, 'enable_insecure_payload' has to be set to 'True'".to_string(); @@ -409,11 +411,25 @@ async fn main() -> Result<()> { return Err(Error::Configuration(message)); } - let work_dir = Path::new(&config.keylime_dir); - let mount = secure_mount::mount(work_dir, &config.secure_size)?; + let work_dir = Path::new(&config.agent.keylime_dir); + let mount = secure_mount::mount(work_dir, &config.agent.secure_size)?; + + let run_as = if permissions::get_euid() == 0 { + if let Some(ref run_as) = config.agent.run_as { + Some(run_as.to_string()) + } else { + warn!("Cannot drop privileges since 'run_as' is empty in 'agent' section of 'keylime-agent.conf'."); + None + } + } else { + error!("Cannot drop privileges: not enough permission"); + return Err(Error::Configuration( + "Cannot drop privileges: not enough permission".to_string(), + )); + }; // Drop privileges - if let Some(user_group) = &config.run_as { + if let Some(user_group) = &run_as { permissions::chown(user_group, &mount)?; if let Err(e) = permissions::run_as(user_group) { let message = "The user running the Keylime agent should be set in keylime-agent.conf, using the parameter `run_as`, with the format `user:group`".to_string(); @@ -438,15 +454,22 @@ async fn main() -> Result<()> { cfg_if::cfg_if! { if #[cfg(feature = "legacy-python-actions")] { - // Verify if the python shim is installed in the expected location - let python_shim = - PathBuf::from(&config.revocation_actions_dir).join("shim.py"); - if !python_shim.exists() { - error!("Could not find python shim at {}", python_shim.display()); - return Err(Error::Configuration(format!( - "Could not find python shim at {}", - python_shim.display() - ))); + match config.agent.revocation_actions_dir { + Some(ref actions_dir) => { + // Verify if the python shim is installed in the expected location + let python_shim = Path::new(&actions_dir).join("shim.py"); + if !python_shim.exists() { + error!("Could not find python shim at {}", python_shim.display()); + return Err(Error::Configuration(format!( + "Could not find python shim at {}", + python_shim.display() + ))); + } + }, + None => { + error!("The revocation actions directory was not set in 'revocation_actions_dir'"); + return Err(Error::Configuration("The revocation actions directory was not set in 'revocation_actions_dir'".to_string())); + } } } } @@ -454,7 +477,7 @@ async fn main() -> Result<()> { // When the tpm_ownerpassword is given, set auth for the Endorsement hierarchy. // Note in the Python implementation, tpm_ownerpassword option is also used for claiming // ownership of TPM access, which will not be implemented here. - if let Some(ref v) = config.tpm_ownerpassword { + if let Some(ref v) = config.agent.tpm_ownerpassword { let auth = Auth::try_from(v.as_bytes())?; ctx.tr_set_auth(Hierarchy::Endorsement.into(), auth) .map_err(|e| { @@ -465,26 +488,36 @@ async fn main() -> Result<()> { })?; }; + let tpm_encryption_alg = algorithms::EncryptionAlgorithm::try_from( + config.agent.tpm_encryption_alg.as_str(), + )?; + let tpm_hash_alg = algorithms::HashAlgorithm::try_from( + config.agent.tpm_hash_alg.as_str(), + )?; + let tpm_signing_alg = algorithms::SignAlgorithm::try_from( + config.agent.tpm_signing_alg.as_str(), + )?; + // Gather EK values and certs let ek_result = tpm::create_ek( &mut ctx, - config.tpm_encryption_alg.into(), - config.ek_handle.as_deref(), + tpm_encryption_alg.into(), + config.agent.ek_handle.as_deref(), )?; // Calculate the SHA-256 hash of the public key in PEM format let ek_hash = hash_ek_pubkey(ek_result.public.clone())?; // Try to load persistent Agent data - let old_ak = match &config.agent_data_path { + let old_ak = match &config.agent.agent_data_path { Some(path) => { let path = Path::new(&path); if path.exists() { match AgentData::load(path) { Ok(data) => { match data.valid( - config.tpm_hash_alg, - config.tpm_signing_alg, + tpm_hash_alg, + tpm_signing_alg, ek_hash.as_bytes(), ) { true => { @@ -543,8 +576,8 @@ async fn main() -> Result<()> { let new_ak = tpm::create_ak( &mut ctx, ek_result.key_handle, - config.tpm_hash_alg.into(), - config.tpm_signing_alg.into(), + tpm_hash_alg.into(), + tpm_signing_alg.into(), )?; let ak_handle = tpm::load_ak(&mut ctx, ek_result.key_handle, &new_ak)?; @@ -554,13 +587,13 @@ async fn main() -> Result<()> { // Store new AgentData let agent_data_new = AgentData::create( - config.tpm_hash_alg, - config.tpm_signing_alg, + tpm_hash_alg, + tpm_signing_alg, &ak, ek_hash.as_bytes(), )?; - match &config.agent_data_path { + match &config.agent.agent_data_path { Some(path) => { agent_data_new.store(Path::new(&path))?; } @@ -569,7 +602,7 @@ async fn main() -> Result<()> { } } - let uuid = match config.uuid.as_str() { + let uuid = match config.agent.uuid.as_str() { "hash_ek" => hash_ek_pubkey(ek_result.public.clone())?, s => s.to_string(), }; @@ -584,7 +617,7 @@ async fn main() -> Result<()> { // Since we store the u key in memory, discarding this key, which // safeguards u and v keys in transit, is not part of the threat model. - let (nk_pub, nk_priv) = match config.server_key { + let (nk_pub, nk_priv) = match config.agent.server_key { Some(ref path) => { let key_path = Path::new(&path); if key_path.exists() { @@ -592,7 +625,10 @@ async fn main() -> Result<()> { "Loading existing key pair from {}", key_path.display() ); - crypto::load_key_pair(key_path, &config.server_key_password)? + crypto::load_key_pair( + key_path, + &config.agent.server_key_password, + )? } else { debug!("Generating new key pair"); let (public, private) = crypto::rsa_generate_pair(2048)?; @@ -600,7 +636,7 @@ async fn main() -> Result<()> { crypto::write_key_pair( &private, key_path, - &config.server_key_password, + &config.agent.server_key_password, ); (public, private) } @@ -617,8 +653,8 @@ async fn main() -> Result<()> { let cert: X509; let mtls_cert; let ssl_context; - if config.enable_agent_mtls { - cert = match config.server_cert { + if config.agent.enable_agent_mtls { + cert = match config.agent.server_cert { Some(ref path) => { let cert_path = Path::new(&path); if cert_path.exists() { @@ -641,7 +677,7 @@ async fn main() -> Result<()> { } }; - let ca_cert_path = match config.trusted_client_ca { + let ca_cert_path = match config.agent.trusted_client_ca { None => { error!("Agent mTLS is enabled, but trusted_client_ca option was not provided"); return Err(Error::Configuration("Agent mTLS is enabled, but trusted_client_ca option was not provided".to_string())); @@ -687,15 +723,15 @@ async fn main() -> Result<()> { { // Request keyblob material let keyblob = registrar_agent::do_register_agent( - &config.registrar_ip, - &config.registrar_port, + &config.agent.registrar_ip, + &config.agent.registrar_port.to_string(), &uuid, &PublicBuffer::try_from(ek_result.public.clone())?.marshall()?, ek_result.ek_cert, &PublicBuffer::try_from(ak.public)?.marshall()?, mtls_cert, - config.contact_ip.clone(), - config.contact_port, + config.agent.contact_ip.clone(), + config.agent.contact_port, ) .await?; info!("SUCCESS: Agent {} registered", &uuid); @@ -707,7 +743,7 @@ async fn main() -> Result<()> { ek_result.key_handle, )?; // Flush EK if we created it - if config.ek_handle.is_none() { + if config.agent.ek_handle.is_none() { ctx.flush_context(ek_result.key_handle.into())?; } let mackey = base64::encode(key.value()); @@ -716,8 +752,8 @@ async fn main() -> Result<()> { let auth_tag = hex::encode(&auth_tag); registrar_agent::do_activate_agent( - &config.registrar_ip, - &config.registrar_port, + &config.agent.registrar_ip, + &config.agent.registrar_port.to_string(), &uuid, &auth_tag, ) @@ -736,21 +772,21 @@ async fn main() -> Result<()> { let symm_key_cvar = Arc::clone(&symm_key_cvar_arc); let payload = Arc::clone(&encr_payload_arc); - let revocation_cert = revocation::get_revocation_cert_path(&config)?; - let actions_dir = Path::new(&config.revocation_actions_dir) + let revocation_cert = + config.agent.revocation_cert.as_ref().map(PathBuf::from); + + let actions_dir = config + .agent + .revocation_actions_dir + .as_ref() + .map(PathBuf::from); + + let work_dir = Path::new(&config.agent.keylime_dir) .canonicalize() .map_err(|e| { - Error::Configuration(format!( - "Path {} set in revocation_actions_dir not found: {}", - &config.revocation_actions_dir, e - )) - })?; - - let work_dir = - Path::new(&config.keylime_dir).canonicalize().map_err(|e| { Error::Configuration(format!( "Path {} set in keylime_dir not found: {}", - &config.keylime_dir, e + &config.agent.keylime_dir, e )) })?; @@ -765,16 +801,17 @@ async fn main() -> Result<()> { payload_symm_key_cvar: symm_key_cvar_arc, encr_payload: encr_payload_arc, auth_tag: Mutex::new([0u8; AUTH_TAG_LEN]), - hash_alg: config.tpm_hash_alg, - enc_alg: config.tpm_encryption_alg, - sign_alg: config.tpm_signing_alg, - agent_uuid: config.uuid.clone(), + hash_alg: tpm_hash_alg, + enc_alg: tpm_encryption_alg, + sign_alg: tpm_signing_alg, + agent_uuid: config.agent.uuid.clone(), revocation_cert, - revocation_actions: config.revocation_actions.clone(), + revocation_actions: config.agent.revocation_actions.clone(), revocation_actions_dir: actions_dir, allow_payload_revocation_actions: config + .agent .allow_payload_revocation_actions, - secure_size: config.secure_size.clone(), + secure_size: config.agent.secure_size.clone(), work_dir, ima_ml_file, measuredboot_ml_file, @@ -877,21 +914,27 @@ async fn main() -> Result<()> { .disable_signals(); let server; - if config.enable_agent_mtls && ssl_context.is_some() { + if config.agent.enable_agent_mtls && ssl_context.is_some() { server = actix_server .bind_openssl( - format!("{}:{}", config.ip, config.port), + format!("{}:{}", config.agent.ip, config.agent.port), ssl_context.unwrap(), //#[allow_ci] )? .run(); - info!("Listening on https://{}:{}", config.ip, config.port); + info!( + "Listening on https://{}:{}", + config.agent.ip, config.agent.port + ); } else { server = actix_server - .bind(format!("{}:{}", config.ip, config.port))? + .bind(format!("{}:{}", config.agent.ip, config.agent.port))? .run(); - info!("Listening on http://{}:{}", config.ip, config.port); + info!( + "Listening on http://{}:{}", + config.agent.ip, config.agent.port + ); }; let server_handle = server.handle(); @@ -930,24 +973,35 @@ fn read_in_file(path: String) -> std::io::Result { #[cfg(feature = "testing")] mod testing { use super::*; + use crate::config::KeylimeConfig; impl QuoteData { pub(crate) fn fixture() -> Result { let test_config = KeylimeConfig::default(); let mut ctx = tpm::get_tpm2_ctx()?; + let tpm_encryption_alg = + algorithms::EncryptionAlgorithm::try_from( + test_config.agent.tpm_encryption_alg.as_str(), + )?; + // Gather EK and AK key values and certs - let ek_result = tpm::create_ek( - &mut ctx, - test_config.tpm_encryption_alg.into(), - None, + let ek_result = + tpm::create_ek(&mut ctx, tpm_encryption_alg.into(), None)?; + + let tpm_hash_alg = algorithms::HashAlgorithm::try_from( + test_config.agent.tpm_hash_alg.as_str(), + )?; + + let tpm_signing_alg = algorithms::SignAlgorithm::try_from( + test_config.agent.tpm_signing_alg.as_str(), )?; let ak_result = tpm::create_ak( &mut ctx, ek_result.key_handle, - test_config.tpm_hash_alg.into(), - test_config.tpm_signing_alg.into(), + tpm_hash_alg.into(), + tpm_signing_alg.into(), )?; let ak_handle = tpm::load_ak(&mut ctx, ek_result.key_handle, &ak_result)?; @@ -973,10 +1027,11 @@ mod testing { let payload = Arc::clone(&encr_payload_arc); let revocation_cert = - revocation::get_revocation_cert_path(&test_config)?; + test_config.agent.revocation_cert.map(PathBuf::from); - let actions_dir = - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/actions/"); + let actions_dir = Some( + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/actions/"), + ); let work_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); @@ -1013,13 +1068,14 @@ mod testing { hash_alg: algorithms::HashAlgorithm::Sha256, enc_alg: algorithms::EncryptionAlgorithm::Rsa, sign_alg: algorithms::SignAlgorithm::RsaSsa, - agent_uuid: test_config.uuid, + agent_uuid: test_config.agent.uuid, revocation_cert, - revocation_actions: String::from(""), + revocation_actions: None, revocation_actions_dir: actions_dir, allow_payload_revocation_actions: test_config + .agent .allow_payload_revocation_actions, - secure_size: test_config.secure_size, + secure_size: test_config.agent.secure_size, work_dir, ima_ml_file, measuredboot_ml_file, diff --git a/src/notifications_handler.rs b/src/notifications_handler.rs index 98d78d938..1d7e1a78e 100644 --- a/src/notifications_handler.rs +++ b/src/notifications_handler.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2021 Keylime Authors -use crate::{common::KeylimeConfig, revocation, Error, QuoteData, Result}; +use crate::{common::JsonWrapper, revocation, Error, QuoteData, Result}; use actix_web::{web, HttpRequest, HttpResponse, Responder}; use log::*; use serde::{Deserialize, Serialize}; @@ -21,33 +21,68 @@ pub async fn revocation( ) -> impl Responder { info!("Received revocation"); - let json_body = serde_json::from_slice(&body)?; - let revocation_cert = &data.revocation_cert; + let json_body = match serde_json::from_slice(&body) { + Ok(body) => body, + Err(e) => { + return HttpResponse::BadRequest().json(JsonWrapper::error( + 400, + format!("JSON parsing error: {}", e), + )); + } + }; + + let revocation_cert = match &data.revocation_cert { + Some(cert) => cert, + None => { + return HttpResponse::InternalServerError().json( + JsonWrapper::error(501, "Revocation certificate not set."), + ); + } + }; + let secure_size = &data.secure_size; - let revocation_actions = &data.revocation_actions; - let actions_dir = PathBuf::from(&data.revocation_actions_dir); + let revocation_actions = match &data.revocation_actions { + Some(actions) => actions, + None => "", + }; + + let actions_dir = match &data.revocation_actions_dir { + Some(dir) => dir, + None => { + return HttpResponse::InternalServerError().json( + JsonWrapper::error( + 501, + "Revocation actions directory not set", + ), + ); + } + }; + let payload_actions_allowed = data.allow_payload_revocation_actions; let work_dir = &data.work_dir; let mount = &data.secure_mount; - revocation::process_revocation( + match revocation::process_revocation( json_body, revocation_cert, secure_size, revocation_actions, - &actions_dir, + actions_dir, payload_actions_allowed, work_dir, mount, - )?; - - HttpResponse::Ok().await + ) { + Ok(_) => HttpResponse::Ok().json(JsonWrapper::success(())), + Err(e) => HttpResponse::InternalServerError().json( + JsonWrapper::error(501, "Revocation actions directory not set"), + ), + } } #[cfg(test)] mod tests { use super::*; - use crate::common::{KeylimeConfig, API_VERSION}; + use crate::common::API_VERSION; use actix_web::{test, web, App}; use serde_json::json; use std::{fs, path::Path}; @@ -55,11 +90,14 @@ mod tests { #[cfg(feature = "testing")] #[actix_rt::test] async fn test_revocation() { - let revocation_cert = Path::new(env!("CARGO_MANIFEST_DIR")) - .join("test-data/test-cert.pem"); - - let revocation_actions_dir = - Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/actions"); + let revocation_cert = Some( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("test-data/test-cert.pem"), + ); + + let revocation_actions_dir = Some( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/actions"), + ); let quotedata = web::Data::new(QuoteData { revocation_cert, diff --git a/src/revocation.rs b/src/revocation.rs index 5b0533ff7..fa49cc7fa 100644 --- a/src/revocation.rs +++ b/src/revocation.rs @@ -4,7 +4,7 @@ #[macro_use] use log::*; -use crate::common::{KeylimeConfig, REV_CERT}; +use crate::config::{AgentConfig, KeylimeConfig}; use crate::crypto; use crate::error::*; use crate::secure_mount; @@ -229,43 +229,6 @@ pub(crate) fn run_revocation_actions( Ok(outputs) } -/// Get the revocation certificate path according to the revocation_cert entry -/// from the configuration file -/// -/// If the revocation_cert entry is "default", then use the default path; -/// If the revocation_cert entry is an absolute path, then use the specified path; -/// If the revocation_cert entry is a relative path, then expand from the WORK_DIR; -/// If the revocation_cert is empty, return error. -pub(crate) fn get_revocation_cert_path( - config: &KeylimeConfig, -) -> Result { - let default_path = - &format!("{}/secure/unzipped/{}", &config.keylime_dir, REV_CERT); - - // Unlike the python agent we do not attempt lazy loading. We either - // have the certificate, or we don't. If we don't have a key or can't load - // the key we return a Configuration error as the service will not work. - let mut cert_path_buf = match config.revocation_cert.trim() { - "default" => PathBuf::from(&default_path), - "" => { - error!("revocation_cert is not set in configuration"); - return Err(Error::Configuration(String::from( - "revocation_cert is not set in configuration", - ))); - } - _ => PathBuf::from(config.revocation_cert.trim()), - }; - - // If the path is not absolute, expand from the WORK_DIR - if cert_path_buf.as_path().is_relative() { - let rel_path = cert_path_buf; - cert_path_buf = PathBuf::from(&config.keylime_dir); - cert_path_buf.push(rel_path); - } - - Ok(cert_path_buf) -} - /// Process revocation message received from REST API or 0mq #[allow(clippy::too_many_arguments)] pub(crate) fn process_revocation( @@ -371,7 +334,7 @@ pub(crate) async fn run_revocation_service( config: &KeylimeConfig, mount: &Path, ) -> Result<()> { - let work_dir = Path::new(&config.keylime_dir); + let work_dir = Path::new(&config.agent.keylime_dir); // Connect to the service via 0mq let context = zmq::Context::new(); @@ -379,18 +342,57 @@ pub(crate) async fn run_revocation_service( mysock.set_subscribe(b"")?; - let endpoint = format!( - "tcp://{}:{}", - config.revocation_notification_ip, - config.revocation_notification_port - ); + let ip = if let Some(i) = &config.agent.revocation_notification_ip { + i + } else { + error!("No IP set in 'revocation_notification_ip' option"); + return Err(Error::Configuration( + "No IP set in 'revocation_notification_ip' option".to_string(), + )); + }; - info!("Connecting to revocation endpoint at {}...", endpoint); + let port = if let Some(p) = &config.agent.revocation_notification_port { + p + } else { + error!("No port set in 'revocation_notification_port' option"); + return Err(Error::Configuration( + "No port set in 'revocation_notification_port' option" + .to_string(), + )); + }; + + let endpoint = format!("tcp://{}:{}", ip, port); + + info!( + "Connecting to revocation notification endpoint at {}...", + endpoint + ); mysock.connect(endpoint.as_str())?; - let revocation_cert = get_revocation_cert_path(config)?; - let actions_dir = PathBuf::from(&config.revocation_actions_dir.trim()); + let revocation_cert = if let Some(cert) = &config.agent.revocation_cert { + Path::new(cert) + } else { + error!("No revocation certificate set in 'revocation_cert' option"); + return Err(Error::Configuration( + "No revocation certificate set in 'revocation_cert' option" + .to_string(), + )); + }; + + let actions_dir = if let Some(dir) = &config.agent.revocation_actions_dir + { + Path::new(dir) + } else { + error!("No revocation actions directory set in 'revocation_actions_dir' option"); + return Err(Error::Configuration("No revocation actions directory set in 'revocation_actions_dir' option".to_string())); + }; + + let actions = if let Some(a) = &config.agent.revocation_actions { + a + } else { + "" + }; info!("Waiting for revocation messages on 0mq {}", endpoint); @@ -414,11 +416,11 @@ pub(crate) async fn run_revocation_service( let body: Value = serde_json::from_str(rawbody.as_str())?; let _ = process_revocation( body, - &revocation_cert, - &config.secure_size, - &config.revocation_actions, - &actions_dir, - config.allow_payload_revocation_actions, + revocation_cert, + &config.agent.secure_size, + actions, + actions_dir, + config.agent.allow_payload_revocation_actions, work_dir, mount, ); @@ -453,8 +455,8 @@ mod tests { symlink(unzipped_dir, tmpfs_dir.join("unzipped")).unwrap(); //#[allow_ci] let outputs = run_revocation_actions( json, - &test_config.secure_size, - &test_config.revocation_actions, + &test_config.agent.secure_size, + "", actions_dir, true, work_dir.path(), @@ -493,8 +495,8 @@ mod tests { symlink(unzipped_dir, tmpfs_dir.join("unzipped")).unwrap(); //#[allow_ci] let outputs = run_revocation_actions( json, - &test_config.secure_size, - &test_config.revocation_actions, + &test_config.agent.secure_size, + "", actions_dir, true, work_dir.path(), @@ -512,12 +514,9 @@ mod tests { ); cfg_if::cfg_if! { if #[cfg(feature = "legacy-python-actions")] { - test_config.revocation_actions = - String::from("local_action_hello, local_action_payload, local_action_stand_alone.py, local_action_rev_script1.py"); + let revocation_actions = "local_action_hello, local_action_payload, local_action_stand_alone.py, local_action_rev_script1.py"; } else { - test_config.revocation_actions = String::from( - "local_action_stand_alone.py, local_action_rev_script1.py", - ); + let revocation_actions = "local_action_stand_alone.py, local_action_rev_script1.py"; } } let json_str = std::fs::read_to_string(json_file).unwrap(); //#[allow_ci] @@ -532,8 +531,8 @@ mod tests { symlink(unzipped_dir, tmpfs_dir.join("unzipped")).unwrap(); //#[allow_ci] let outputs = run_revocation_actions( json, - &test_config.secure_size, - &test_config.revocation_actions, + &test_config.agent.secure_size, + revocation_actions, actions_dir, true, work_dir.path(), @@ -559,53 +558,6 @@ mod tests { } } - #[test] - fn get_revocation_cert_path_default() { - let test_config = KeylimeConfig::default(); - let revocation_cert_path = - get_revocation_cert_path(&test_config).unwrap(); //#[allow_ci] - let mut expected = PathBuf::from(&test_config.keylime_dir); - expected.push("secure/unzipped/"); - expected.push(REV_CERT); - assert_eq!(*revocation_cert_path, expected); - } - - #[test] - fn get_revocation_cert_path_absolute() { - let mut test_config = KeylimeConfig { - revocation_cert: String::from("/test/cert.crt"), - ..Default::default() - }; - let revocation_cert_path = - get_revocation_cert_path(&test_config).unwrap(); //#[allow_ci] - assert_eq!(revocation_cert_path, PathBuf::from("/test/cert.crt")); - } - - #[test] - fn get_revocation_cert_path_relative() { - let mut test_config = KeylimeConfig { - revocation_cert: String::from("cert.crt"), - ..Default::default() - }; - let revocation_cert_path = - get_revocation_cert_path(&test_config).unwrap(); //#[allow_ci] - let mut expected = - Path::new(&test_config.keylime_dir).join("cert.crt"); - assert_eq!(revocation_cert_path, expected); - } - - #[test] - fn get_revocation_cert_path_empty() { - let mut test_config = KeylimeConfig { - revocation_cert: String::from(""), - ..Default::default() - }; - assert!( - get_revocation_cert_path(&test_config).is_err(), - "revocation_cert is not set in configuration" - ); - } - #[test] fn test_lookup_action() { let work_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); @@ -749,10 +701,10 @@ mod tests { let result = process_revocation( body, &cert_path, - &test_config.secure_size, - &test_config.revocation_actions, + &test_config.agent.secure_size, + "", &actions_dir, - test_config.allow_payload_revocation_actions, + test_config.agent.allow_payload_revocation_actions, &work_dir, &tmpfs_dir, ); From 46e76d497fb4585d9b9cc4099ea0ae80abeb55aa Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 20 Sep 2022 13:30:52 +0200 Subject: [PATCH 7/9] config: Add back support for KEYLIME_DIR env var Add back the support for overriding the 'keylime_dir' option by setting the 'KEYLIME_DIR' environment variable. Note that it is also possible to override the 'keylime_dir' option by setting the 'KEYLIME_AGENT_KEYLIME_DIR' environment variable, although 'KEYLIME_DIR' has priority. Signed-off-by: Anderson Toshiyuki Sasaki --- src/config.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/config.rs b/src/config.rs index 74cb827fc..a1fee8018 100644 --- a/src/config.rs +++ b/src/config.rs @@ -439,33 +439,45 @@ fn config_translate_keywords( ) -> Result { let uuid = get_uuid(&config.agent.uuid); + let env_keylime_dir = env::var("KEYLIME_DIR").ok(); + let keylime_dir = match env_keylime_dir { + Some(ref dir) => { + if !dir.is_empty() { + dir.to_string() + } else { + config.agent.keylime_dir.to_string() + } + } + None => config.agent.keylime_dir.to_string(), + }; + let mut agent_data_path = config_get_file_path( &config.agent.agent_data_path, - &config.agent.keylime_dir, + &keylime_dir, DEFAULT_AGENT_DATA_PATH, ); let mut server_key = config_get_file_path( &config.agent.server_key, - &config.agent.keylime_dir, + &keylime_dir, DEFAULT_SERVER_KEY, ); let mut server_cert = config_get_file_path( &config.agent.server_cert, - &config.agent.keylime_dir, + &keylime_dir, DEFAULT_SERVER_CERT, ); let mut trusted_client_ca = config_get_file_path( &config.agent.trusted_client_ca, - &config.agent.keylime_dir, + &keylime_dir, DEFAULT_TRUSTED_CLIENT_CA, ); let mut revocation_cert = config_get_file_path( &config.agent.revocation_cert, - &config.agent.keylime_dir, + &keylime_dir, &format!("secure/unzipped/{}", DEFAULT_REVOCATION_CERT), ); @@ -527,6 +539,7 @@ fn config_translate_keywords( Ok(KeylimeConfig { agent: AgentConfig { + keylime_dir, uuid, server_key, server_cert, From 4071212a00843c118a6d89185e53c92c3d636487 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 20 Sep 2022 14:31:21 +0200 Subject: [PATCH 8/9] config: Add configuration file version This is not currently used, but will be important in future to check configuration file compatibility and upgrades. Signed-off-by: Anderson Toshiyuki Sasaki --- keylime-agent.conf | 3 +++ src/config.rs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/keylime-agent.conf b/keylime-agent.conf index 1a2ef295d..39eec3052 100644 --- a/keylime-agent.conf +++ b/keylime-agent.conf @@ -2,6 +2,9 @@ [agent] #============================================================================= +# The configuration file version +version = "2.0" + # The agent's UUID. # Set to "openstack", it will try to get the UUID from the metadata service. # If you set this to "generate", Keylime will create a random UUID. diff --git a/src/config.rs b/src/config.rs index a1fee8018..98a6af57d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,6 +20,7 @@ use std::{ use toml::value::Table; use uuid::Uuid; +pub static CONFIG_VERSION: &str = "2.0"; pub static DEFAULT_UUID: &str = "d432fbb3-d2f1-4a97-9ef7-75bd81c00000"; pub static DEFAULT_IP: &str = "127.0.0.1"; pub static DEFAULT_PORT: u32 = 9002; @@ -63,6 +64,7 @@ pub static DEFAULT_CONFIG_SYS: &str = "/usr/etc/keylime-agent.conf"; impl Source for KeylimeConfig { fn collect(&self) -> Result, ConfigError> { let agent: Map = Map::from([ + ("version".to_string(), self.agent.version.to_string().into()), ("uuid".to_string(), self.agent.uuid.to_string().into()), ("ip".to_string(), self.agent.ip.to_string().into()), ("port".to_string(), Value::from(self.agent.port)), @@ -263,6 +265,7 @@ pub(crate) struct KeylimeConfig { #[derive(Clone, Debug, Deserialize, Serialize)] pub(crate) struct AgentConfig { + pub version: String, pub uuid: String, pub ip: String, pub port: u32, @@ -308,6 +311,7 @@ impl Default for AgentConfig { }; AgentConfig { + version: CONFIG_VERSION.to_string(), ip: DEFAULT_IP.to_string(), port: DEFAULT_PORT, registrar_ip: DEFAULT_REGISTRAR_IP.to_string(), From 97b0d26687ed38796e49dd2efed4dd18795b7f85 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 20 Sep 2022 20:02:23 +0200 Subject: [PATCH 9/9] config: Align config locations with the python components The python components expect the configuration files to be in /etc/keylime/*.conf and the configuration snippets to be in /etc/keylime/*.conf.d This patch modifies the paths where the configuration files are searched to align with the python components. In the case, the agent configuration will be installed in /etc/keylime/agent.conf and the snippets are searched in /etc/keylime/agent.conf.d/ Signed-off-by: Anderson Toshiyuki Sasaki --- GNUmakefile | 4 +++- src/config.rs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index d5b5d7ba7..5f827229f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,7 +27,9 @@ $(programs): .PHONY: install install: all - cp ${CONFFILE} /etc/${CONFFILE} + mkdir -p /etc/keylime/ + mkdir -p /etc/keylime/agent.conf.d + cp ${CONFFILE} /etc/keylime/agent.conf for f in $(programs); do \ install -D -t ${DESTDIR}/usr/bin "$$f"; \ done diff --git a/src/config.rs b/src/config.rs index 98a6af57d..11f10c696 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,8 +58,8 @@ pub static DEFAULT_TPM_SIGNING_ALG: &str = "rsassa"; pub static DEFAULT_EK_HANDLE: &str = "generate"; pub static DEFAULT_RUN_AS: &str = "keylime:tss"; pub static DEFAULT_AGENT_DATA_PATH: &str = "agent_data.json"; -pub static DEFAULT_CONFIG: &str = "/etc/keylime-agent.conf"; -pub static DEFAULT_CONFIG_SYS: &str = "/usr/etc/keylime-agent.conf"; +pub static DEFAULT_CONFIG: &str = "/etc/keylime/agent.conf"; +pub static DEFAULT_CONFIG_SYS: &str = "/usr/etc/keylime/agent.conf"; impl Source for KeylimeConfig { fn collect(&self) -> Result, ConfigError> { @@ -382,7 +382,7 @@ fn config_get_file_setting() -> Result, Error> { ) // Add system configuration snippets .add_source( - glob("/usr/etc/keylime-agent.conf.d/*") + glob("/usr/etc/keylime/agent.conf.d/*") .map_err(Error::GlobPattern)? .filter_map(|entry| entry.ok()) .map(|path| { @@ -396,7 +396,7 @@ fn config_get_file_setting() -> Result, Error> { ) // Add user configuration snippets .add_source( - glob("/etc/keylime-agent.conf.d/*") + glob("/etc/keylime/agent.conf.d/*") .map_err(Error::GlobPattern)? .filter_map(|entry| entry.ok()) .map(|path| {