Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04a16ab862 | |||
| 624f9017b3 | |||
| 70f2666c2e |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1931,7 +1931,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-patch-api"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-rt",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "linux-patch-api"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
edition = "2021"
|
||||
authors = ["Echo <echo@moon-dragon.us>"]
|
||||
description = "Secure remote package management API for Linux systems"
|
||||
|
||||
@ -38,8 +38,12 @@ pub enum EnrollmentStatusResponse {
|
||||
Pending,
|
||||
Approved {
|
||||
ca_crt: String,
|
||||
#[serde(default)]
|
||||
ca_chain: String,
|
||||
server_crt: String,
|
||||
server_key: String,
|
||||
#[serde(default)]
|
||||
crl_pem: String,
|
||||
},
|
||||
Denied,
|
||||
NotFound,
|
||||
@ -49,8 +53,10 @@ pub enum EnrollmentStatusResponse {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PkiBundle {
|
||||
pub ca_crt: String,
|
||||
pub ca_chain: String,
|
||||
pub server_crt: String,
|
||||
pub server_key: String,
|
||||
pub crl_pem: String,
|
||||
}
|
||||
|
||||
impl From<EnrollmentStatusResponse> for Option<PkiBundle> {
|
||||
@ -58,12 +64,16 @@ impl From<EnrollmentStatusResponse> for Option<PkiBundle> {
|
||||
match response {
|
||||
EnrollmentStatusResponse::Approved {
|
||||
ca_crt,
|
||||
ca_chain,
|
||||
server_crt,
|
||||
server_key,
|
||||
crl_pem,
|
||||
} => Some(PkiBundle {
|
||||
ca_crt,
|
||||
ca_chain,
|
||||
server_crt,
|
||||
server_key,
|
||||
crl_pem,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
@ -451,8 +461,10 @@ impl EnrollmentClient {
|
||||
}
|
||||
EnrollmentStatusResponse::Approved {
|
||||
ca_crt,
|
||||
ca_chain,
|
||||
server_crt,
|
||||
server_key,
|
||||
crl_pem,
|
||||
} => {
|
||||
tracing::info!(
|
||||
elapsed_seconds = start.elapsed().as_secs(),
|
||||
@ -461,8 +473,10 @@ impl EnrollmentClient {
|
||||
);
|
||||
return Ok(PkiBundle {
|
||||
ca_crt,
|
||||
ca_chain,
|
||||
server_crt,
|
||||
server_key,
|
||||
crl_pem,
|
||||
});
|
||||
}
|
||||
EnrollmentStatusResponse::Denied => {
|
||||
@ -566,8 +580,10 @@ mod tests {
|
||||
fn approved_to_pki_bundle() {
|
||||
let status = EnrollmentStatusResponse::Approved {
|
||||
ca_crt: "ca".into(),
|
||||
ca_chain: String::new(),
|
||||
server_crt: "crt".into(),
|
||||
server_key: "key".into(),
|
||||
crl_pem: String::new(),
|
||||
};
|
||||
let bundle: Option<PkiBundle> = status.into();
|
||||
assert!(bundle.is_some());
|
||||
|
||||
@ -160,8 +160,10 @@ pub async fn run_enrollment(
|
||||
// Write certificates to configured paths (or defaults)
|
||||
provision::provision_pki_bundle(
|
||||
&pki_bundle.ca_crt,
|
||||
&pki_bundle.ca_chain,
|
||||
&pki_bundle.server_crt,
|
||||
&pki_bundle.server_key,
|
||||
&pki_bundle.crl_pem,
|
||||
config.tls_config(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -16,6 +16,8 @@ const DEFAULT_CA_CERT: &str = "/etc/linux_patch_api/certs/ca.pem";
|
||||
const DEFAULT_SERVER_CERT: &str = "/etc/linux_patch_api/certs/server.pem";
|
||||
/// Default server key path.
|
||||
const DEFAULT_SERVER_KEY: &str = "/etc/linux_patch_api/certs/server.key.pem";
|
||||
/// Default CRL path.
|
||||
const DEFAULT_CRL_PATH: &str = "/etc/linux_patch_api/certs/crl.pem";
|
||||
|
||||
/// Validate that a PEM string has proper format (BEGIN/END markers present).
|
||||
///
|
||||
@ -128,12 +130,14 @@ pub fn write_pem_file(path: &str, pem_data: &str, is_key: bool) -> Result<()> {
|
||||
|
||||
/// Provision the full PKI bundle from an approved enrollment response.
|
||||
///
|
||||
/// Writes CA cert, server cert, and server key to configured paths.
|
||||
/// Writes CA cert, CA chain, server cert, server key, and CRL to configured paths.
|
||||
/// Paths are read from TLS config if available, otherwise defaults are used.
|
||||
pub async fn provision_pki_bundle(
|
||||
ca_crt: &str,
|
||||
_ca_chain: &str,
|
||||
server_crt: &str,
|
||||
server_key: &str,
|
||||
crl_pem: &str,
|
||||
tls_config: Option<&super::super::config::loader::TlsConfig>,
|
||||
) -> Result<()> {
|
||||
// Determine target paths from config or defaults
|
||||
@ -173,6 +177,19 @@ pub async fn provision_pki_bundle(
|
||||
|
||||
write_pem_file(&key_path, server_key, true).context("Failed to write server key")?;
|
||||
|
||||
// Write CRL if provided (non-empty)
|
||||
let crl_path = if let Some(tls) = tls_config {
|
||||
tls.crl_path.clone()
|
||||
} else {
|
||||
DEFAULT_CRL_PATH.to_string()
|
||||
};
|
||||
if !crl_pem.trim().is_empty() {
|
||||
write_pem_file(&crl_path, crl_pem, false).context("Failed to write CRL")?;
|
||||
tracing::info!(path = %crl_path, "CRL written from enrollment bundle");
|
||||
} else {
|
||||
tracing::info!("No CRL in enrollment bundle — agent will fetch on refresh cycle");
|
||||
}
|
||||
|
||||
// 3. Log successful provisioning with structured fields
|
||||
tracing::info!(
|
||||
ca_cert = %ca_path,
|
||||
|
||||
@ -178,8 +178,10 @@ async fn test_full_enrollment_flow_happy_path() {
|
||||
let tls_config = build_tls_config(cert_dir.path());
|
||||
provision::provision_pki_bundle(
|
||||
&bundle.ca_crt,
|
||||
&bundle.ca_chain,
|
||||
&bundle.server_crt,
|
||||
&bundle.server_key,
|
||||
&bundle.crl_pem,
|
||||
Some(&tls_config),
|
||||
)
|
||||
.await
|
||||
@ -445,8 +447,10 @@ async fn test_certificate_permission_verification() {
|
||||
let tls_config = build_tls_config(cert_dir.path());
|
||||
provision::provision_pki_bundle(
|
||||
&bundle.ca_crt,
|
||||
&bundle.ca_chain,
|
||||
&bundle.server_crt,
|
||||
&bundle.server_key,
|
||||
&bundle.crl_pem,
|
||||
Some(&tls_config),
|
||||
)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user