fix: apply cargo fmt to resolve CI formatting failures
Some checks failed
CI/CD Pipeline / Code Format (push) Successful in 4s
CI/CD Pipeline / Clippy Lints (push) Failing after 44s
CI/CD Pipeline / Enrollment Tests (push) Has been skipped
CI/CD Pipeline / Verify Enrollment CLI Flag (push) Has been skipped
CI/CD Pipeline / All Unit Tests (push) Successful in 1m15s
CI/CD Pipeline / Build Debian Package (push) Has been skipped
CI/CD Pipeline / Build Debian Package (Ubuntu 22.04) (push) Has been skipped
CI/CD Pipeline / Build RPM Package (push) Has been skipped
CI/CD Pipeline / Build Alpine Package (push) Has been skipped
CI/CD Pipeline / Build Arch Package (push) Has been skipped
CI/CD Pipeline / Security Audit (push) Successful in 4s
Some checks failed
CI/CD Pipeline / Code Format (push) Successful in 4s
CI/CD Pipeline / Clippy Lints (push) Failing after 44s
CI/CD Pipeline / Enrollment Tests (push) Has been skipped
CI/CD Pipeline / Verify Enrollment CLI Flag (push) Has been skipped
CI/CD Pipeline / All Unit Tests (push) Successful in 1m15s
CI/CD Pipeline / Build Debian Package (push) Has been skipped
CI/CD Pipeline / Build Debian Package (Ubuntu 22.04) (push) Has been skipped
CI/CD Pipeline / Build RPM Package (push) Has been skipped
CI/CD Pipeline / Build Alpine Package (push) Has been skipped
CI/CD Pipeline / Build Arch Package (push) Has been skipped
CI/CD Pipeline / Security Audit (push) Successful in 4s
Format all enrollment module source files and tests per rustfmt standards. Resolves Gitea CI workflow cargo fmt check failures.
This commit is contained in:
@ -94,23 +94,32 @@ impl WhitelistManager {
|
||||
|
||||
// Parse to validate - must be IPv4 or CIDR, no hostnames in auto-append
|
||||
let parsed_entry = if let Some((ip_str, prefix_str)) = entry_str.split_once('/') {
|
||||
let ip: Ipv4Addr = ip_str.parse()
|
||||
let ip: Ipv4Addr = ip_str
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid IP in CIDR notation: {}", entry_str))?;
|
||||
let prefix: u8 = prefix_str.parse()
|
||||
let prefix: u8 = prefix_str
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid prefix in CIDR notation: {}", entry_str))?;
|
||||
if prefix > 32 {
|
||||
anyhow::bail!("Invalid CIDR prefix (must be 0-32): {}", entry_str);
|
||||
}
|
||||
WhitelistEntry::Cidr { network: ip, prefix }
|
||||
WhitelistEntry::Cidr {
|
||||
network: ip,
|
||||
prefix,
|
||||
}
|
||||
} else {
|
||||
let ip: Ipv4Addr = entry_str.parse()
|
||||
let ip: Ipv4Addr = entry_str
|
||||
.parse()
|
||||
.with_context(|| format!("Invalid IPv4 address: {}", entry_str))?;
|
||||
WhitelistEntry::Ip(ip)
|
||||
};
|
||||
|
||||
// 2. Check for duplicate in current in-memory state
|
||||
{
|
||||
let entries = self.entries.read().map_err(|e| anyhow::anyhow!("Failed to acquire whitelist read lock: {}", e))?;
|
||||
let entries = self
|
||||
.entries
|
||||
.read()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to acquire whitelist read lock: {}", e))?;
|
||||
for existing in entries.iter() {
|
||||
if *existing == parsed_entry {
|
||||
info!(
|
||||
@ -133,11 +142,16 @@ impl WhitelistManager {
|
||||
.open(&lock_path)
|
||||
.with_context(|| format!("Failed to create lock file: {}", lock_path))?;
|
||||
|
||||
lock_file.lock_exclusive().context("Failed to acquire exclusive whitelist lock")?;
|
||||
lock_file
|
||||
.lock_exclusive()
|
||||
.context("Failed to acquire exclusive whitelist lock")?;
|
||||
|
||||
// Double-check for duplicates after acquiring lock (concurrent append scenario)
|
||||
{
|
||||
let entries = self.entries.read().map_err(|e| anyhow::anyhow!("Failed to acquire whitelist read lock: {}", e))?;
|
||||
let entries = self
|
||||
.entries
|
||||
.read()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to acquire whitelist read lock: {}", e))?;
|
||||
for existing in entries.iter() {
|
||||
if *existing == parsed_entry {
|
||||
info!(
|
||||
@ -154,9 +168,12 @@ impl WhitelistManager {
|
||||
|
||||
// 4. Read current whitelist YAML or create empty config
|
||||
let mut config = if Path::new(&self.config_path).exists() {
|
||||
self.load_config().context("Failed to load existing whitelist for append")?
|
||||
self.load_config()
|
||||
.context("Failed to load existing whitelist for append")?
|
||||
} else {
|
||||
WhitelistConfig { entries: Vec::new() }
|
||||
WhitelistConfig {
|
||||
entries: Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
// 5. Append new entry to allowed_ips list
|
||||
@ -168,8 +185,9 @@ impl WhitelistManager {
|
||||
// Ensure parent directory exists
|
||||
if let Some(parent) = config_path.parent() {
|
||||
if !parent.exists() {
|
||||
fs::create_dir_all(parent)
|
||||
.with_context(|| format!("Failed to create whitelist directory: {}", parent.display()))?;
|
||||
fs::create_dir_all(parent).with_context(|| {
|
||||
format!("Failed to create whitelist directory: {}", parent.display())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,28 +200,35 @@ impl WhitelistManager {
|
||||
.create_new(true)
|
||||
.truncate(true)
|
||||
.open(&temp_path)
|
||||
.with_context(|| format!("Failed to create temp whitelist file: {}", temp_path.display()))?;
|
||||
|
||||
file.write_all(yaml_content.as_bytes())
|
||||
.with_context(|| format!("Failed to write whitelist data to: {}", temp_path.display()))?;
|
||||
file.flush()
|
||||
.with_context(|| format!("Failed to flush whitelist data to: {}", temp_path.display()))?;
|
||||
|
||||
// Atomic rename
|
||||
fs::rename(&temp_path, config_path)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to atomically rename whitelist temp file {} to {}",
|
||||
temp_path.display(),
|
||||
config_path.display()
|
||||
"Failed to create temp whitelist file: {}",
|
||||
temp_path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
file.write_all(yaml_content.as_bytes()).with_context(|| {
|
||||
format!("Failed to write whitelist data to: {}", temp_path.display())
|
||||
})?;
|
||||
file.flush().with_context(|| {
|
||||
format!("Failed to flush whitelist data to: {}", temp_path.display())
|
||||
})?;
|
||||
|
||||
// Atomic rename
|
||||
fs::rename(&temp_path, config_path).with_context(|| {
|
||||
format!(
|
||||
"Failed to atomically rename whitelist temp file {} to {}",
|
||||
temp_path.display(),
|
||||
config_path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
// Release lock explicitly before reload (drop happens at end of scope)
|
||||
drop(lock_file);
|
||||
|
||||
// 7. Reload in-memory state
|
||||
self.reload().context("Failed to reload whitelist after append")?;
|
||||
self.reload()
|
||||
.context("Failed to reload whitelist after append")?;
|
||||
|
||||
// 8. Log audit event
|
||||
tracing::info!(
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::signal::unix::{SignalKind, signal as unix_signal};
|
||||
use tokio::signal::unix::{signal as unix_signal, SignalKind};
|
||||
|
||||
use crate::enroll::identity;
|
||||
|
||||
@ -99,7 +99,7 @@ impl EnrollmentClient {
|
||||
.expect("Failed to parse manager URL");
|
||||
|
||||
match parsed.scheme() {
|
||||
"http" | "https" => {}, // Allowed schemes
|
||||
"http" | "https" => {} // Allowed schemes
|
||||
other => panic!(
|
||||
"Invalid manager URL scheme '{}' — only 'http' and 'https' are allowed. \
|
||||
Refused dangerous scheme to prevent SSRF/path traversal.",
|
||||
@ -139,12 +139,11 @@ impl EnrollmentClient {
|
||||
/// - `Err` if URL parsing fails or DNS resolution yields no results
|
||||
pub async fn manager_ip(&self) -> Result<String> {
|
||||
// Parse URL to extract host using url crate for RFC-compliant parsing
|
||||
let parsed = url::Url::parse(&self.manager_url).with_context(|| {
|
||||
format!("Failed to parse manager URL '{}'", self.manager_url)
|
||||
})?;
|
||||
let host_str = parsed.host_str().with_context(|| {
|
||||
format!("Manager URL '{}' has no host component", self.manager_url)
|
||||
})?;
|
||||
let parsed = url::Url::parse(&self.manager_url)
|
||||
.with_context(|| format!("Failed to parse manager URL '{}'", self.manager_url))?;
|
||||
let host_str = parsed
|
||||
.host_str()
|
||||
.with_context(|| format!("Manager URL '{}' has no host component", self.manager_url))?;
|
||||
|
||||
// Check if already an IP address using url::Host parsing
|
||||
if let Ok(url::Host::Ipv4(addr)) = url::Host::parse(host_str) {
|
||||
@ -287,9 +286,8 @@ impl EnrollmentClient {
|
||||
.await
|
||||
.context("Failed to read status response body")?;
|
||||
|
||||
let status: EnrollmentStatusResponse =
|
||||
serde_json::from_str(&body)
|
||||
.context("Invalid status response — malformed JSON from manager")?;
|
||||
let status: EnrollmentStatusResponse = serde_json::from_str(&body)
|
||||
.context("Invalid status response — malformed JSON from manager")?;
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
@ -336,7 +334,11 @@ impl EnrollmentClient {
|
||||
max_attempts: u32,
|
||||
) -> Result<PkiBundle> {
|
||||
// Enforce hard limits
|
||||
let effective_interval = if interval_seconds == 0 { 60 } else { interval_seconds };
|
||||
let effective_interval = if interval_seconds == 0 {
|
||||
60
|
||||
} else {
|
||||
interval_seconds
|
||||
};
|
||||
let effective_max = match max_attempts {
|
||||
0 => 1440,
|
||||
n if n > 1440 => 1440,
|
||||
@ -417,7 +419,11 @@ impl EnrollmentClient {
|
||||
attempts = attempt,
|
||||
"Enrollment approved — received PKI bundle from manager"
|
||||
);
|
||||
return Ok(PkiBundle { ca_crt, server_crt, server_key });
|
||||
return Ok(PkiBundle {
|
||||
ca_crt,
|
||||
server_crt,
|
||||
server_key,
|
||||
});
|
||||
}
|
||||
EnrollmentStatusResponse::Denied => {
|
||||
tracing::warn!(
|
||||
@ -444,20 +450,22 @@ impl EnrollmentClient {
|
||||
total_seconds = total_seconds,
|
||||
"Enrollment polling timed out after maximum attempts"
|
||||
);
|
||||
Err(anyhow!("Enrollment timed out after {} hours ({}/{} attempts)",
|
||||
total_seconds / 3600, effective_max, effective_max))
|
||||
Err(anyhow!(
|
||||
"Enrollment timed out after {} hours ({}/{} attempts)",
|
||||
total_seconds / 3600,
|
||||
effective_max,
|
||||
effective_max
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a SIGINT (Ctrl+C) signal receiver.
|
||||
fn setup_sigint() -> Result<tokio::signal::unix::Signal> {
|
||||
unix_signal(SignalKind::interrupt())
|
||||
.context("Failed to create SIGINT signal handler")
|
||||
unix_signal(SignalKind::interrupt()).context("Failed to create SIGINT signal handler")
|
||||
}
|
||||
|
||||
/// Create a SIGTERM signal receiver.
|
||||
fn setup_sigterm() -> Result<tokio::signal::unix::Signal> {
|
||||
unix_signal(SignalKind::terminate())
|
||||
.context("Failed to create SIGTERM signal handler")
|
||||
unix_signal(SignalKind::terminate()).context("Failed to create SIGTERM signal handler")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -66,8 +66,7 @@ pub fn get_fqdn() -> Result<String> {
|
||||
|
||||
/// Collect all non-loopback IPv4 addresses from network interfaces.
|
||||
pub fn get_ip_addresses() -> Result<Vec<String>> {
|
||||
let ifaces = if_addrs::get_if_addrs()
|
||||
.context("Failed to enumerate network interfaces")?;
|
||||
let ifaces = if_addrs::get_if_addrs().context("Failed to enumerate network interfaces")?;
|
||||
|
||||
let mut addrs: Vec<String> = ifaces
|
||||
.iter()
|
||||
@ -105,16 +104,28 @@ pub fn get_os_details() -> Result<serde_json::Value> {
|
||||
let unquoted = value.trim().trim_matches('"').trim_matches('\'');
|
||||
match key {
|
||||
"NAME" => {
|
||||
details.insert("distro".into(), serde_json::Value::String(unquoted.to_string()));
|
||||
details.insert(
|
||||
"distro".into(),
|
||||
serde_json::Value::String(unquoted.to_string()),
|
||||
);
|
||||
}
|
||||
"VERSION_ID" => {
|
||||
details.insert("version".into(), serde_json::Value::String(unquoted.to_string()));
|
||||
details.insert(
|
||||
"version".into(),
|
||||
serde_json::Value::String(unquoted.to_string()),
|
||||
);
|
||||
}
|
||||
"ID_LIKE" => {
|
||||
details.insert("id_like".into(), serde_json::Value::String(unquoted.to_string()));
|
||||
details.insert(
|
||||
"id_like".into(),
|
||||
serde_json::Value::String(unquoted.to_string()),
|
||||
);
|
||||
}
|
||||
"VERSION_CODENAME" => {
|
||||
details.insert("codename".into(), serde_json::Value::String(unquoted.to_string()));
|
||||
details.insert(
|
||||
"codename".into(),
|
||||
serde_json::Value::String(unquoted.to_string()),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -123,7 +134,10 @@ pub fn get_os_details() -> Result<serde_json::Value> {
|
||||
} else {
|
||||
// Fallback for systems without os-release (very rare)
|
||||
details.insert("distro".into(), serde_json::Value::String("unknown".into()));
|
||||
details.insert("version".into(), serde_json::Value::String("unknown".into()));
|
||||
details.insert(
|
||||
"version".into(),
|
||||
serde_json::Value::String("unknown".into()),
|
||||
);
|
||||
}
|
||||
|
||||
// Kernel version via uname -r
|
||||
@ -159,6 +173,9 @@ mod tests {
|
||||
#[test]
|
||||
fn os_details_contains_kernel() {
|
||||
let details = get_os_details().expect("Failed to get OS details");
|
||||
assert!(details.get("kernel").is_some(), "OS details must contain kernel version");
|
||||
assert!(
|
||||
details.get("kernel").is_some(),
|
||||
"OS details must contain kernel version"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,8 +12,7 @@ use anyhow::{Context, Result};
|
||||
|
||||
/// Re-export key types for ergonomic access from parent modules.
|
||||
pub use client::{
|
||||
EnrollmentClient, EnrollmentRequest, EnrollmentResponse,
|
||||
EnrollmentStatusResponse, PkiBundle,
|
||||
EnrollmentClient, EnrollmentRequest, EnrollmentResponse, EnrollmentStatusResponse, PkiBundle,
|
||||
};
|
||||
/// Re-export identity extraction functions.
|
||||
pub use identity::{get_fqdn, get_ip_addresses, get_machine_id, get_os_details};
|
||||
@ -40,10 +39,16 @@ pub async fn run_enrollment(manager_url: &str, config: &super::AppConfig) -> Res
|
||||
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);
|
||||
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!(
|
||||
@ -51,7 +56,9 @@ pub async fn run_enrollment(manager_url: &str, config: &super::AppConfig) -> Res
|
||||
max_attempts = max_attempts,
|
||||
"Starting enrollment - polling phase"
|
||||
);
|
||||
let pki_bundle = client.poll_for_approval(&response.polling_token, interval, max_attempts).await?;
|
||||
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");
|
||||
@ -62,13 +69,15 @@ pub async fn run_enrollment(manager_url: &str, config: &super::AppConfig) -> Res
|
||||
&pki_bundle.server_crt,
|
||||
&pki_bundle.server_key,
|
||||
config.tls_config(),
|
||||
).await?;
|
||||
)
|
||||
.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",
|
||||
)?;
|
||||
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");
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
//! PKI provisioning module for self-enrollment.
|
||||
//! Handles certificate extraction, validation, and secure file writing.
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use crate::auth::WhitelistManager;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
@ -71,8 +71,9 @@ pub fn write_pem_file(path: &str, pem_data: &str, is_key: bool) -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(parent)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(parent, perms)
|
||||
.with_context(|| format!("Failed to set permissions on: {}", parent.display()))?;
|
||||
fs::set_permissions(parent, perms).with_context(|| {
|
||||
format!("Failed to set permissions on: {}", parent.display())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,14 +108,13 @@ pub fn write_pem_file(path: &str, pem_data: &str, is_key: bool) -> Result<()> {
|
||||
.with_context(|| format!("Failed to flush PEM data to: {}", temp_path.display()))?;
|
||||
|
||||
// Atomic rename to target path
|
||||
fs::rename(&temp_path, path)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to atomically rename {} to {}",
|
||||
temp_path.display(),
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
fs::rename(&temp_path, path).with_context(|| {
|
||||
format!(
|
||||
"Failed to atomically rename {} to {}",
|
||||
temp_path.display(),
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
tracing::info!(
|
||||
path = %path.display(),
|
||||
@ -138,7 +138,11 @@ pub async fn provision_pki_bundle(
|
||||
) -> Result<()> {
|
||||
// Determine target paths from config or defaults
|
||||
let (ca_path, cert_path, key_path) = if let Some(tls) = tls_config {
|
||||
(tls.ca_cert.clone(), tls.server_cert.clone(), tls.server_key.clone())
|
||||
(
|
||||
tls.ca_cert.clone(),
|
||||
tls.server_cert.clone(),
|
||||
tls.server_key.clone(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
DEFAULT_CA_CERT.to_string(),
|
||||
@ -148,10 +152,8 @@ pub async fn provision_pki_bundle(
|
||||
};
|
||||
|
||||
// 1. Validate all three PEM strings before any writes
|
||||
validate_pem(ca_crt, "CERTIFICATE")
|
||||
.context("CA certificate validation failed")?;
|
||||
validate_pem(server_crt, "CERTIFICATE")
|
||||
.context("Server certificate validation failed")?;
|
||||
validate_pem(ca_crt, "CERTIFICATE").context("CA certificate validation failed")?;
|
||||
validate_pem(server_crt, "CERTIFICATE").context("Server certificate validation failed")?;
|
||||
|
||||
// Server key can be PRIVATE KEY (PKCS#8), RSA PRIVATE KEY (PKCS#1), or EC PRIVATE KEY
|
||||
let key_valid = validate_pem(server_key, "PRIVATE KEY").is_ok()
|
||||
@ -165,14 +167,11 @@ pub async fn provision_pki_bundle(
|
||||
}
|
||||
|
||||
// 2. Write to configured paths (atomic writes)
|
||||
write_pem_file(&ca_path, ca_crt, false)
|
||||
.context("Failed to write CA certificate")?;
|
||||
write_pem_file(&ca_path, ca_crt, false).context("Failed to write CA certificate")?;
|
||||
|
||||
write_pem_file(&cert_path, server_crt, false)
|
||||
.context("Failed to write server certificate")?;
|
||||
write_pem_file(&cert_path, server_crt, false).context("Failed to write server certificate")?;
|
||||
|
||||
write_pem_file(&key_path, server_key, true)
|
||||
.context("Failed to write server key")?;
|
||||
write_pem_file(&key_path, server_key, true).context("Failed to write server key")?;
|
||||
|
||||
// 3. Log successful provisioning with structured fields
|
||||
tracing::info!(
|
||||
@ -198,11 +197,19 @@ pub async fn append_manager_to_whitelist(manager_ip: &str, whitelist_path: &str)
|
||||
}
|
||||
|
||||
// Create or load WhitelistManager and call append_entry
|
||||
let mut manager = WhitelistManager::new(whitelist_path)
|
||||
.with_context(|| format!("Failed to initialize whitelist manager for path: {}", whitelist_path))?;
|
||||
let mut manager = WhitelistManager::new(whitelist_path).with_context(|| {
|
||||
format!(
|
||||
"Failed to initialize whitelist manager for path: {}",
|
||||
whitelist_path
|
||||
)
|
||||
})?;
|
||||
|
||||
manager.append_entry(ip_or_cidr)
|
||||
.with_context(|| format!("Failed to append manager IP '{}' to whitelist at: {}", ip_or_cidr, whitelist_path))?;
|
||||
manager.append_entry(ip_or_cidr).with_context(|| {
|
||||
format!(
|
||||
"Failed to append manager IP '{}' to whitelist at: {}",
|
||||
ip_or_cidr, whitelist_path
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -343,7 +350,8 @@ mod tests {
|
||||
let dir = tempdir().expect("failed to create temp dir");
|
||||
let target_path = dir.path().join("cert.pem");
|
||||
let cert1 = sample_certificate();
|
||||
let cert2 = "-----BEGIN CERTIFICATE-----\nNEWCERTDATA\n-----END CERTIFICATE-----".to_string();
|
||||
let cert2 =
|
||||
"-----BEGIN CERTIFICATE-----\nNEWCERTDATA\n-----END CERTIFICATE-----".to_string();
|
||||
|
||||
// Write initial file
|
||||
write_pem_file(target_path.to_str().unwrap(), &cert1, false).expect("initial write failed");
|
||||
@ -352,7 +360,10 @@ mod tests {
|
||||
write_pem_file(target_path.to_str().unwrap(), &cert2, false).expect("second write failed");
|
||||
|
||||
let backup_path = format!("{}.bak", target_path.display());
|
||||
assert!(std::path::Path::new(&backup_path).exists(), "Backup file should exist");
|
||||
assert!(
|
||||
std::path::Path::new(&backup_path).exists(),
|
||||
"Backup file should exist"
|
||||
);
|
||||
|
||||
// Original content in backup
|
||||
let backup_content = fs::read_to_string(&backup_path).expect("failed to read backup");
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@ -23,8 +23,8 @@ use tracing::{error, info, warn};
|
||||
|
||||
use linux_patch_api::api::{configure_api_routes, configure_health_route};
|
||||
use linux_patch_api::auth::{mtls, MtlsMiddleware, WhitelistManager};
|
||||
use linux_patch_api::packages::create_backend;
|
||||
use linux_patch_api::enroll;
|
||||
use linux_patch_api::packages::create_backend;
|
||||
use linux_patch_api::{init_logging, AppConfig, JobManager};
|
||||
|
||||
/// Linux Patch API CLI arguments
|
||||
@ -42,7 +42,10 @@ struct Args {
|
||||
verbose: bool,
|
||||
|
||||
/// Enroll with manager at URL (skips mTLS startup, runs enrollment flow only)
|
||||
#[arg(long, help = "Enroll with manager at URL (skips mTLS startup, runs enrollment flow only)")]
|
||||
#[arg(
|
||||
long,
|
||||
help = "Enroll with manager at URL (skips mTLS startup, runs enrollment flow only)"
|
||||
)]
|
||||
enroll: Option<String>,
|
||||
}
|
||||
|
||||
@ -78,7 +81,10 @@ async fn main() -> Result<()> {
|
||||
|
||||
// Handle enrollment mode - runs before server startup
|
||||
if let Some(ref manager_url) = args.enroll {
|
||||
info!(manager_url = manager_url, "Enrollment mode activated - running enrollment flow before server startup");
|
||||
info!(
|
||||
manager_url = manager_url,
|
||||
"Enrollment mode activated - running enrollment flow before server startup"
|
||||
);
|
||||
match enroll::run_enrollment(manager_url, &config).await {
|
||||
Ok(()) => {
|
||||
info!("Enrollment complete - proceeding to server startup");
|
||||
|
||||
Reference in New Issue
Block a user