//! Self-enrollment module for linux_patch_api daemon. //! //! Handles secure registration with the patch manager, including //! identity extraction (machine-id, FQDN, IPs, OS details) and //! mTLS enrollment via the manager API. pub mod client; pub mod identity; pub mod provision; use anyhow::{Context, Result}; /// Re-export key types for ergonomic access from parent modules. pub use client::{ EnrollmentClient, EnrollmentRequest, EnrollmentResponse, EnrollmentStatusResponse, PkiBundle, }; /// Re-export identity extraction functions. pub use identity::{ get_fqdn, get_ip_addresses, get_ip_for_interface, get_machine_id, get_os_details, get_primary_ip, get_route_source_ip, is_container_bridge, is_link_local, }; /// Run the full enrollment flow against the manager at the given URL. /// /// # Phases /// 1. **Registration** - POST machine identity to manager, receive polling token /// 2. **Polling** - Poll manager for approval with configurable interval/max attempts /// 3. **Provisioning** - Write PKI bundle to disk (certs/keys) and append manager IP to whitelist /// /// # Errors /// Returns Err on registration failure, polling timeout, denial, user interruption, /// PKI provisioning failure, or whitelist update failure. pub async fn run_enrollment(manager_url: &str, config: &super::AppConfig) -> Result<()> { // Extract IP reporting overrides from enrollment config let (report_interface, report_ip) = config .enrollment .as_ref() .map(|e| (e.report_interface.clone(), e.report_ip.clone())) .unwrap_or((None, None)); let client = EnrollmentClient::with_ip_overrides(manager_url, report_interface, report_ip); // Phase 1: Registration tracing::info!( manager_url = manager_url, "Starting enrollment - registration phase" ); let response = client.register().await?; tracing::info!("Registration successful - received polling token"); // Get polling config (use defaults if not set) let interval = config .enrollment .as_ref() .map(|e| e.polling_interval_seconds) .unwrap_or(60); let max_attempts = config .enrollment .as_ref() .map(|e| e.max_poll_attempts) .unwrap_or(1440); // Phase 2: Polling tracing::info!( interval_seconds = interval, max_attempts = max_attempts, "Starting enrollment - polling phase" ); let pki_bundle = client .poll_for_approval(&response.polling_token, interval, max_attempts) .await?; // Phase 3: PKI provisioning & whitelist update tracing::info!("Enrollment approved - starting PKI provisioning phase"); // Write certificates to configured paths (or defaults) provision::provision_pki_bundle( &pki_bundle.ca_crt, &pki_bundle.server_crt, &pki_bundle.server_key, config.tls_config(), ) .await?; tracing::info!("PKI bundle written to disk"); // Resolve manager hostname to IP and append to whitelist let manager_ip = client .manager_ip() .await .context("Failed to resolve manager IP - cannot update whitelist")?; provision::append_manager_to_whitelist(&manager_ip, config.whitelist_path()).await?; tracing::info!(manager_ip = %manager_ip, "Manager IP appended to whitelist"); tracing::info!("Enrollment complete - PKI and whitelist configured"); Ok(()) }