From 9e71aabf237692629ac6b5aa9b4982805b55c2ec Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Thu, 7 Mar 2024 17:50:29 +0100 Subject: [PATCH 1/6] feat(store): add interface to query the ovs by to2 and to0 This adds the required function signature to the trait which will query ovs in the trait implementors. Signed-off-by: Irene Diez --- store/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/store/src/lib.rs b/store/src/lib.rs index ed272fbb0..e5874a98f 100644 --- a/store/src/lib.rs +++ b/store/src/lib.rs @@ -197,6 +197,17 @@ pub trait Store: Send + Sync { 'life0: 'async_trait, Self: 'async_trait; + fn query_ovs_db_to2_performed_to0_less_than<'life0, 'async_trait>( + &'life0 self, + to2: bool, + to0_max: i64, + ) -> Pin< + Box, StoreError>> + 'async_trait + Send>, + > + where + 'life0: 'async_trait, + Self: 'async_trait; + fn store_data<'life0, 'async_trait>( &'life0 self, key: K, From 66959a839d0aac1115c3c99c68a130d869468788 Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Thu, 7 Mar 2024 17:51:07 +0100 Subject: [PATCH 2/6] feat(db): implement trait to query ovs by to0 and to2 Implements the `query_ovs_db_to2_performed_to0_less_than` trait in the Owner database types. Signed-off-by: Irene Diez --- store/src/db.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/store/src/db.rs b/store/src/db.rs index 552588455..2919c64b0 100644 --- a/store/src/db.rs +++ b/store/src/db.rs @@ -154,6 +154,14 @@ where Err(StoreError::MethodNotAvailable) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + _to2: bool, + _to0_max: i64, + ) -> Result, StoreError> { + Err(StoreError::MethodNotAvailable) + } + async fn store_data(&self, _key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::sqlite::SqliteManufacturerDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); @@ -338,6 +346,34 @@ where Ok(ret) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + to2: bool, + to0_max: i64, + ) -> Result, StoreError> { + let mut ret = vec![]; + let pool = fdo_db::sqlite::SqliteOwnerDB::get_conn_pool(); + let conn = &mut pool + .get() + .map_err(|e| StoreError::Database(format!("Error connecting to DB {e:?}")))?; + let db_ovs = fdo_db::sqlite::SqliteOwnerDB::select_ov_to2_performed_and_ov_to0_less_than( + false, to0_max, conn, + ) + .map_err(|e| { + StoreError::Database(format!( + "Error selecting OVs filering by to2 {to2} and to0 {to0_max}: {e:?}" + )) + })?; + for db_ov in db_ovs { + ret.push( + OwnershipVoucher::from_pem_or_raw(&db_ov.contents).map_err(|e| { + StoreError::Unspecified(format!("Error parsing OV contents from DB: {e:?}")) + })?, + ); + } + Ok(ret) + } + async fn store_data(&self, _key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::sqlite::SqliteOwnerDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); @@ -471,6 +507,14 @@ where Err(StoreError::MethodNotAvailable) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + _to2: bool, + _to0_max: i64, + ) -> Result, StoreError> { + Err(StoreError::MethodNotAvailable) + } + async fn store_data(&self, key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::sqlite::SqliteRendezvousDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); @@ -606,6 +650,14 @@ where Err(StoreError::MethodNotAvailable) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + _to2: bool, + _to0_max: i64, + ) -> Result, StoreError> { + Err(StoreError::MethodNotAvailable) + } + async fn store_data(&self, _key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::postgres::PostgresManufacturerDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); @@ -793,6 +845,35 @@ where Ok(ret) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + to2: bool, + to0_max: i64, + ) -> Result, StoreError> { + let mut ret = vec![]; + let pool = fdo_db::postgres::PostgresOwnerDB::get_conn_pool(); + let conn = &mut pool + .get() + .map_err(|e| StoreError::Database(format!("Error connecting to DB {e:?}")))?; + let db_ovs = + fdo_db::postgres::PostgresOwnerDB::select_ov_to2_performed_and_ov_to0_less_than( + false, to0_max, conn, + ) + .map_err(|e| { + StoreError::Database(format!( + "Error selecting OVs filering by to2 {to2} and to0 {to0_max}: {e:?}" + )) + })?; + for db_ov in db_ovs { + ret.push( + OwnershipVoucher::from_pem_or_raw(&db_ov.contents).map_err(|e| { + StoreError::Unspecified(format!("Error parsing OV contents from DB: {e:?}")) + })?, + ); + } + Ok(ret) + } + async fn store_data(&self, _key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::postgres::PostgresOwnerDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); @@ -926,6 +1007,14 @@ where Err(StoreError::MethodNotAvailable) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + _to2: bool, + _to0_max: i64, + ) -> Result, StoreError> { + Err(StoreError::MethodNotAvailable) + } + async fn store_data(&self, key: K, value: V) -> Result<(), StoreError> { let pool = fdo_db::postgres::PostgresRendezvousDB::get_conn_pool(); let conn = &mut pool.get().expect("Couldn't establish a connection"); From 55ef0754c0529bcb66b16d0a521b65d58e716e79 Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Thu, 7 Mar 2024 17:51:31 +0100 Subject: [PATCH 3/6] feat(directory): implement trait to query ovs by to0 and to2 In the directory this trait is not available, so we return the specific `StoreError::MethodNotAvailable` error. Signed-off-by: Irene Diez --- store/src/directory.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/store/src/directory.rs b/store/src/directory.rs index a7b31239d..71bc0d425 100644 --- a/store/src/directory.rs +++ b/store/src/directory.rs @@ -302,6 +302,14 @@ where Err(StoreError::MethodNotAvailable) } + async fn query_ovs_db_to2_performed_to0_less_than( + &self, + _to2: bool, + _to0_max: i64, + ) -> Result, StoreError> { + Err(StoreError::MethodNotAvailable) + } + async fn store_data(&self, key: K, value: V) -> Result<(), StoreError> { let finalpath = self.get_path(&key); let mut path = finalpath.clone(); From 09b510f6953861c54734b844f0dcdb0f3bc5f589 Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Thu, 15 Feb 2024 18:24:58 +0100 Subject: [PATCH 4/6] feat(owner): add OV re-registration window checks This adds the ability to trigger a rendezvous re-registration when an OV is close to its de-registration timeout. A re-registration window is added so that when the remaining registration period of the OV reaches that window, a re-registration is triggered. This feature is only available when using database storage drivers. Signed-off-by: Irene Diez --- owner-onboarding-server/src/main.rs | 143 +++++++++++++++++- .../configuration/owner_onboarding_server.rs | 8 + 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/owner-onboarding-server/src/main.rs b/owner-onboarding-server/src/main.rs index f1221aa78..307c8f609 100644 --- a/owner-onboarding-server/src/main.rs +++ b/owner-onboarding-server/src/main.rs @@ -27,9 +27,14 @@ use fdo_data_formats::{ publickey::PublicKey, types::{Guid, TO2AddressEntry}, }; -use fdo_store::{Store, StoreError}; + +use fdo_store::{Store, StoreConfig, StoreError}; use fdo_util::servers::{ - configuration::{owner_onboarding_server::OwnerOnboardingServerSettings, AbsolutePathBuf}, + configuration::{ + owner_onboarding_server::OwnerOnboardingServerSettings, + owner_onboarding_server::DEFAULT_REGISTRATION_PERIOD, + owner_onboarding_server::DEFAULT_RE_REGISTRATION_WINDOW, AbsolutePathBuf, + }, settings_for, OwnershipVoucherStoreMetadataKey, }; @@ -63,6 +68,13 @@ pub(crate) struct OwnerServiceUD { service_info_api_client: fdo_http_wrapper::client::JsonClient, owner_addresses: Vec, + + // How much time (s) OVs are going to be registered + ov_registration_period: u32, + // The time window (s) within which the re-registration will start + ov_re_registration_window: u32, + + window_check_enabled: bool, } pub(crate) type OwnerServiceUDT = Arc; @@ -73,7 +85,14 @@ fn load_private_key(path: &AbsolutePathBuf) -> Result> { } async fn _handle_report_to_rendezvous(udt: &OwnerServiceUDT, ov: &OwnershipVoucher) -> Result<()> { - match report_ov_to_rendezvous(ov, &udt.owner_addresses, &udt.owner_key).await { + match report_ov_to_rendezvous( + ov, + &udt.owner_addresses, + &udt.owner_key, + udt.ov_registration_period, + ) + .await + { Ok(wait_seconds) => { udt.ownership_voucher_store .store_metadata( @@ -138,10 +157,59 @@ async fn report_to_rendezvous(udt: OwnerServiceUDT) -> Result<()> { Ok(()) } +async fn check_registration_window(udt: &OwnerServiceUDT) -> Result<()> { + let now_plus_window = + time::OffsetDateTime::now_utc().unix_timestamp() + (udt.ov_re_registration_window as i64); + // these are the ovs whose registration time will end and we need to + // re-register them + let ovs = udt + .ownership_voucher_store + .query_ovs_db_to2_performed_to0_less_than(false, now_plus_window) + .await?; + for ov in ovs { + match report_ov_to_rendezvous( + &ov, + &udt.owner_addresses, + &udt.owner_key, + udt.ov_registration_period, + ) + .await + { + Ok(wait_seconds) => { + udt.ownership_voucher_store + .store_metadata( + ov.header().guid(), + &fdo_store::MetadataKey::Local( + OwnershipVoucherStoreMetadataKey::To0AcceptOwnerWaitSeconds, + ), + &time::Duration::new(wait_seconds.into(), 0), + ) + .await?; + if wait_seconds != udt.ov_registration_period { + log::warn!("OV({}): registered by rendezvous for {wait_seconds}s, as opposed to the requested {}s", + ov.header().guid().to_string(), udt.ov_registration_period); + if udt.ov_re_registration_window >= wait_seconds { + log::warn!("OV({}): re-registration won't be triggered (window: {}s, registration: {}s)", + ov.header().guid().to_string(), udt.ov_re_registration_window, udt.ov_registration_period); + } + } + } + Err(e) => { + log::warn!( + "OV({}): failed to report to rendezvous: {e}", + ov.header().guid().to_string() + ); + } + } + } + Ok(()) +} + async fn report_ov_to_rendezvous( ov: &OwnershipVoucher, owner_addresses: &[TO2AddressEntry], owner_key: &PKey, + registration_period: u32, ) -> Result { let ov_header = ov.header(); if ov_header.protocol_version() != ProtocolVersion::Version1_1 { @@ -193,8 +261,7 @@ async fn report_ov_to_rendezvous( }; // Build to0d and to1d - // TODO(runcom): 600 has to come from configuration - let to0d = TO0Data::new(ov.clone(), 600, hello_ack.nonce3().clone()) + let to0d = TO0Data::new(ov.clone(), registration_period, hello_ack.nonce3().clone()) .context("Error creating to0d")?; let to0d_vec = to0d.serialize_data().context("Error serializing TO0Data")?; let to0d_hash = @@ -242,14 +309,21 @@ async fn perform_maintenance(udt: OwnerServiceUDT) -> std::result::Result<(), &' #[allow(unused_must_use)] let (ov_res, ses_res, rtr_res) = tokio::join!(ov_maint, ses_maint, rtr_maint); + if udt.window_check_enabled { + let window_res = check_registration_window(&udt.clone()).await; + if let Err(e) = window_res { + log::warn!("Error during re-registration window check: {e:?}"); + } + } + if let Err(e) = ov_res { - log::warn!("Error during ownership voucher store maintenance: {:?}", e); + log::warn!("Error during ownership voucher store maintenance: {e:?}"); } if let Err(e) = ses_res { - log::warn!("Error during session store maintenance: {:?}", e); + log::warn!("Error during session store maintenance: {e:?}"); } if let Err(e) = rtr_res { - log::warn!("Error during report to rendezvous maintenance: {:?}", e) + log::warn!("Error during report to rendezvous maintenance: {e:?}") } } } @@ -335,6 +409,43 @@ async fn main() -> Result<()> { .context("Error converting owner public key to PK")? }; + // Voucher registration times + let ov_registration_period = match settings.ov_registration_period { + Some(value) => { + if value == 0 { + bail!("ov_registration_period cannot be 0"); + } + value + } + None => { + log::info!( + "Setting a default ov_registration_period of {DEFAULT_REGISTRATION_PERIOD} seconds" + ); + DEFAULT_REGISTRATION_PERIOD + } + }; + let ov_re_registration_window = match settings.ov_re_registration_window { + Some(value) => { + if value == 0 { + bail!("ov_re_registration_window cannot be 0"); + } else if value as u64 <= MAINTENANCE_INTERVAL { + bail!("this server performs checks every {MAINTENANCE_INTERVAL} seconds, please specify an ov_re_registration_window larger than that value"); + } + value + } + None => { + log::info!("Setting a default ov_re_registration_window of {DEFAULT_RE_REGISTRATION_WINDOW} seconds"); + DEFAULT_RE_REGISTRATION_WINDOW + } + }; + + if ov_re_registration_window >= ov_registration_period { + bail!( + "ov_re_registration_window ({ov_re_registration_window}) must be smaller than ov_registration_period ({ov_registration_period})"); + } else { + log::info!("Server configured with an OV registration period of {ov_registration_period} seconds, OV re-registration window set to {ov_re_registration_window} seconds") + } + // Initialize stores let ownership_voucher_store = settings .ownership_voucher_store_driver @@ -344,6 +455,16 @@ async fn main() -> Result<()> { .session_store_driver .initialize() .context("Error initializing session store")?; + + // the re-registration check is only available with DB store drivers + let window_check_enabled = match settings.ownership_voucher_store_driver { + StoreConfig::Directory { path: _ } => { + log::info!("OV re-registration window check disabled, this feature is only available with DB storage drivers"); + false + } + _ => true, + }; + let session_store = fdo_http_wrapper::server::SessionStore::new(session_store); // Generate a new Owner2 @@ -387,6 +508,12 @@ async fn main() -> Result<()> { // Owner addresses owner_addresses, + + // OV registration times + ov_registration_period, + ov_re_registration_window, + + window_check_enabled, }); // Initialize handlers diff --git a/util/src/servers/configuration/owner_onboarding_server.rs b/util/src/servers/configuration/owner_onboarding_server.rs index 810bf80d7..c5c065997 100644 --- a/util/src/servers/configuration/owner_onboarding_server.rs +++ b/util/src/servers/configuration/owner_onboarding_server.rs @@ -32,4 +32,12 @@ pub struct OwnerOnboardingServerSettings { pub owner_addresses: Vec, pub report_to_rendezvous_endpoint_enabled: bool, + + pub ov_registration_period: Option, + pub ov_re_registration_window: Option, } + +// 10 minutes +pub const DEFAULT_REGISTRATION_PERIOD: u32 = 600; +// ~1 minute +pub const DEFAULT_RE_REGISTRATION_WINDOW: u32 = 61; From e39130e463aba760a40cd31d28a410d3fc4c278a Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Thu, 7 Mar 2024 17:52:36 +0100 Subject: [PATCH 5/6] feat(aio): add ov registration window options This adds the configuration options for OV re-registration window checks in aio. Signed-off-by: Irene Diez --- admin-tool/src/aio/configure.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/admin-tool/src/aio/configure.rs b/admin-tool/src/aio/configure.rs index e26612822..4678e2a28 100644 --- a/admin-tool/src/aio/configure.rs +++ b/admin-tool/src/aio/configure.rs @@ -7,6 +7,8 @@ use std::{collections::BTreeMap, net::IpAddr, path::Path}; use fdo_store::StoreConfig; use fdo_util::servers::configuration::{ + owner_onboarding_server::DEFAULT_REGISTRATION_PERIOD, + owner_onboarding_server::DEFAULT_RE_REGISTRATION_WINDOW, serviceinfo_api_server::ServiceInfoSettings, AbsolutePathBuf, Bind, }; @@ -46,6 +48,11 @@ pub(super) struct Configuration { #[clap(long)] pub manufacturing_use_secp256r1: bool, + #[clap(long)] + ov_registration_period: Option, + #[clap(long)] + ov_re_registration_window: Option, + /// The hostname or IP address that clients should use to connect to the AIO components /// (if not specified, will be all IP addresses of the system). /// Note that this is not equal to the listen address, as the AIO components will always @@ -57,6 +64,7 @@ pub(super) struct Configuration { // and generate the intermediate data #[clap(skip)] pub contact_addresses: Vec, + #[clap(skip)] pub serviceinfo_api_auth_token: String, #[clap(skip)] @@ -88,6 +96,9 @@ impl Default for Configuration { manufacturing_disable_key_storage_tpm: true, manufacturing_use_secp256r1: false, + ov_registration_period: Some(DEFAULT_REGISTRATION_PERIOD), + ov_re_registration_window: Some(DEFAULT_RE_REGISTRATION_WINDOW), + contact_hostname: None, contact_addresses: vec![], @@ -339,6 +350,9 @@ fn generate_configs(aio_dir: &Path, config_args: &Configuration) -> Result<(), E .generate_owner_addresses() .context("Error generating owner addresses")?, report_to_rendezvous_endpoint_enabled: true, + + ov_registration_period: config_args.ov_registration_period, + ov_re_registration_window: config_args.ov_re_registration_window, }; write_config( aio_dir, From 626488d1cbc0230ce71ee52412b07039a3244fcd Mon Sep 17 00:00:00 2001 From: Irene Diez Date: Fri, 8 Mar 2024 17:02:18 +0100 Subject: [PATCH 6/6] feat(docs): explain re-registration window options Signed-off-by: Irene Diez --- HOWTO.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HOWTO.md b/HOWTO.md index 890765f41..fd59b882f 100644 --- a/HOWTO.md +++ b/HOWTO.md @@ -453,6 +453,8 @@ owner_addresses: - dns_name: fdo.example.com - ip_address: 192.168.122.1 report_to_rendezvous_endpoint_enabled: false +ov_registration_period: 600 +ov_re_registration_window: 61 bind: 0.0.0.0:8081 service_info_api_url: "http://localhost:8089/device_info" service_info_api_authentication: None @@ -512,6 +514,12 @@ Where: - `port`: connection port. - `report_to_rendezvous_endpoint_enabled`: whether reporting to the Rendezvous Server is enabled or not, boolean. +- `ov_registration_period`: optional value that sets how many seconds OVs are + going to be registered into the Rendezvous server. +- `ov_re_registration_window`: optional value that sets the minimum amount of + seconds left in the `ov_registration_period` for the Owner server to trigger + a re-registration within the Rendezvous server. This option can only be used + with database backends. ### `rendezvous-server.yml`