Compare commits
1 Commits
v1.3.2
...
feat/20-cr
| Author | SHA1 | Date | |
|---|---|---|---|
| be085bbf35 |
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -1477,12 +1477,6 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@ -1931,7 +1925,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-patch-api"
|
name = "linux-patch-api"
|
||||||
version = "1.3.1"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix",
|
"actix",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
@ -1949,12 +1943,9 @@ dependencies = [
|
|||||||
"criterion",
|
"criterion",
|
||||||
"fs2",
|
"fs2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hex",
|
|
||||||
"if-addrs",
|
"if-addrs",
|
||||||
"notify",
|
"notify",
|
||||||
"pidlock",
|
"pidlock",
|
||||||
"rand 0.8.6",
|
|
||||||
"rcgen",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
@ -2267,16 +2258,6 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pem"
|
|
||||||
version = "3.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@ -2613,20 +2594,6 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rcgen"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
|
|
||||||
dependencies = [
|
|
||||||
"pem",
|
|
||||||
"ring",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"time",
|
|
||||||
"x509-parser",
|
|
||||||
"yasna",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.18"
|
version = "0.5.18"
|
||||||
@ -4332,15 +4299,6 @@ dependencies = [
|
|||||||
"hashlink",
|
"hashlink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "yasna"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
|
||||||
dependencies = [
|
|
||||||
"time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "linux-patch-api"
|
name = "linux-patch-api"
|
||||||
version = "1.3.2"
|
version = "1.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Echo <echo@moon-dragon.us>"]
|
authors = ["Echo <echo@moon-dragon.us>"]
|
||||||
description = "Secure remote package management API for Linux systems"
|
description = "Secure remote package management API for Linux systems"
|
||||||
@ -95,10 +95,6 @@ tokio-test = "0.4"
|
|||||||
wiremock = "0.6"
|
wiremock = "0.6"
|
||||||
serial_test = "3"
|
serial_test = "3"
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
rcgen = { version = "0.13", features = ["pem", "x509-parser"] }
|
|
||||||
rand = "0.8"
|
|
||||||
hex = "0.4"
|
|
||||||
time = { version = "0.3", features = ["std"] }
|
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
|
|
||||||
# Integration tests in subdirectories
|
# Integration tests in subdirectories
|
||||||
|
|||||||
353
src/auth/crl.rs
353
src/auth/crl.rs
@ -165,16 +165,7 @@ pub fn load_crl(crl_path: &Path, ca_cert_der: &[u8]) -> CrlState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Verify CRL signature against CA
|
// Verify CRL signature against CA
|
||||||
// Extract DER from PEM if the CA cert is PEM-encoded
|
let (_, ca_cert) = match x509_parser::parse_x509_certificate(ca_cert_der) {
|
||||||
let ca_der = match extract_pem_cert_der(ca_cert_der) {
|
|
||||||
Some(der) => der,
|
|
||||||
None => {
|
|
||||||
// Not PEM — assume it's already DER
|
|
||||||
ca_cert_der.to_vec()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (_, ca_cert) = match x509_parser::parse_x509_certificate(&ca_der) {
|
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(error = %e, "Failed to parse CA cert for CRL signature verification");
|
error!(error = %e, "Failed to parse CA cert for CRL signature verification");
|
||||||
@ -229,29 +220,6 @@ pub fn load_crl(crl_path: &Path, ca_cert_der: &[u8]) -> CrlState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract DER bytes from a PEM-encoded certificate.
|
|
||||||
/// Looks for `-----BEGIN CERTIFICATE-----` / `-----END CERTIFICATE-----` markers
|
|
||||||
/// and base64-decodes the content between them.
|
|
||||||
pub fn extract_pem_cert_der(pem_bytes: &[u8]) -> Option<Vec<u8>> {
|
|
||||||
let pem_str = String::from_utf8_lossy(pem_bytes);
|
|
||||||
let begin_marker = "-----BEGIN CERTIFICATE-----";
|
|
||||||
let end_marker = "-----END CERTIFICATE-----";
|
|
||||||
|
|
||||||
let begin_idx = pem_str.find(begin_marker)?;
|
|
||||||
let after_begin = begin_idx + begin_marker.len();
|
|
||||||
let end_idx = pem_str[after_begin..].find(end_marker)?;
|
|
||||||
// Strip all whitespace (including newlines) from the base64 block
|
|
||||||
// before decoding, since PEM format wraps lines at 64 characters.
|
|
||||||
let b64_block: String = pem_str[after_begin..after_begin + end_idx]
|
|
||||||
.split_whitespace()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
use base64::Engine;
|
|
||||||
base64::engine::general_purpose::STANDARD
|
|
||||||
.decode(&b64_block)
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract DER bytes from a PEM-encoded CRL.
|
/// Extract DER bytes from a PEM-encoded CRL.
|
||||||
/// Looks for `-----BEGIN X509 CRL-----` / `-----END X509 CRL-----` blocks.
|
/// Looks for `-----BEGIN X509 CRL-----` / `-----END X509 CRL-----` blocks.
|
||||||
fn extract_pem_crl_der(pem_bytes: &[u8]) -> Option<Vec<u8>> {
|
fn extract_pem_crl_der(pem_bytes: &[u8]) -> Option<Vec<u8>> {
|
||||||
@ -262,15 +230,11 @@ fn extract_pem_crl_der(pem_bytes: &[u8]) -> Option<Vec<u8>> {
|
|||||||
let begin_idx = pem_str.find(begin_marker)?;
|
let begin_idx = pem_str.find(begin_marker)?;
|
||||||
let after_begin = begin_idx + begin_marker.len();
|
let after_begin = begin_idx + begin_marker.len();
|
||||||
let end_idx = pem_str[after_begin..].find(end_marker)?;
|
let end_idx = pem_str[after_begin..].find(end_marker)?;
|
||||||
// Strip all whitespace (including newlines) from the base64 block
|
let b64_block = pem_str[after_begin..after_begin + end_idx].trim();
|
||||||
// before decoding, since PEM format wraps lines at 64 characters.
|
|
||||||
let b64_block: String = pem_str[after_begin..after_begin + end_idx]
|
|
||||||
.split_whitespace()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
base64::engine::general_purpose::STANDARD
|
base64::engine::general_purpose::STANDARD
|
||||||
.decode(&b64_block)
|
.decode(b64_block)
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,315 +416,4 @@ mod tests {
|
|||||||
assert_eq!(updated.status, CrlStatus::Valid);
|
assert_eq!(updated.status, CrlStatus::Valid);
|
||||||
assert!(updated.is_revoked("abc"));
|
assert!(updated.is_revoked("abc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// CRL parsing and verification tests
|
|
||||||
//
|
|
||||||
// Note: x509_parser's verify_signature() has known incompatibilities with
|
|
||||||
// rcgen-generated CRL signatures. The full load_crl() pipeline (which
|
|
||||||
// includes signature verification) is tested end-to-end with real CRLs
|
|
||||||
// from the manager's CertAuthority. These unit tests focus on the
|
|
||||||
// individual components: PEM extraction, DER parsing, CrlState logic,
|
|
||||||
// and missing file handling.
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// Helper: generate a test CA key/cert pair using rcgen.
|
|
||||||
fn generate_test_ca() -> (rcgen::KeyPair, rcgen::Certificate) {
|
|
||||||
let key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
|
||||||
let mut params = rcgen::CertificateParams::default();
|
|
||||||
params.not_before = time::OffsetDateTime::now_utc();
|
|
||||||
params.not_after = time::OffsetDateTime::now_utc() + time::Duration::days(365 * 10);
|
|
||||||
params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
|
|
||||||
params.key_usages = vec![
|
|
||||||
rcgen::KeyUsagePurpose::KeyCertSign,
|
|
||||||
rcgen::KeyUsagePurpose::CrlSign,
|
|
||||||
];
|
|
||||||
let mut dn = rcgen::DistinguishedName::new();
|
|
||||||
dn.push(rcgen::DnType::CommonName, "Test Root CA");
|
|
||||||
dn.push(rcgen::DnType::OrganizationName, "Patch Manager Test");
|
|
||||||
params.distinguished_name = dn;
|
|
||||||
let cert = params.self_signed(&key).unwrap();
|
|
||||||
(key, cert)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper: generate a CRL signed by the test CA with the given revoked serials.
|
|
||||||
fn generate_test_crl(
|
|
||||||
ca_key: &rcgen::KeyPair,
|
|
||||||
ca_cert: &rcgen::Certificate,
|
|
||||||
revoked_serials: &[rcgen::SerialNumber],
|
|
||||||
) -> String {
|
|
||||||
let now = time::OffsetDateTime::now_utc();
|
|
||||||
let next_update = now + time::Duration::hours(24);
|
|
||||||
let crl_number =
|
|
||||||
rcgen::SerialNumber::from_slice(&chrono::Utc::now().timestamp().to_be_bytes());
|
|
||||||
|
|
||||||
let revoked_certs: Vec<rcgen::RevokedCertParams> = revoked_serials
|
|
||||||
.iter()
|
|
||||||
.map(|serial| rcgen::RevokedCertParams {
|
|
||||||
serial_number: serial.clone(),
|
|
||||||
revocation_time: now,
|
|
||||||
reason_code: Some(rcgen::RevocationReason::Unspecified),
|
|
||||||
invalidity_date: None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let crl_params = rcgen::CertificateRevocationListParams {
|
|
||||||
this_update: now,
|
|
||||||
next_update,
|
|
||||||
crl_number,
|
|
||||||
issuing_distribution_point: None,
|
|
||||||
revoked_certs,
|
|
||||||
key_identifier_method: rcgen::KeyIdMethod::Sha256,
|
|
||||||
};
|
|
||||||
|
|
||||||
let crl = crl_params.signed_by(ca_cert, ca_key).unwrap();
|
|
||||||
crl.pem().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper: generate a serial number and return both rcgen SerialNumber and its hex string.
|
|
||||||
fn make_serial_hex_pair() -> (rcgen::SerialNumber, String) {
|
|
||||||
let mut bytes = [0u8; 16];
|
|
||||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut bytes);
|
|
||||||
let hex = hex::encode(bytes);
|
|
||||||
(rcgen::SerialNumber::from_slice(&bytes), hex)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_pem_extraction_works_for_valid_crl() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (ca_key, ca_cert) = generate_test_ca();
|
|
||||||
let (serial1, _) = make_serial_hex_pair();
|
|
||||||
let crl_pem = generate_test_crl(&ca_key, &ca_cert, &[serial1]);
|
|
||||||
|
|
||||||
// Verify PEM extraction succeeds
|
|
||||||
let der = extract_pem_crl_der(crl_pem.as_bytes());
|
|
||||||
assert!(
|
|
||||||
der.is_some(),
|
|
||||||
"PEM extraction should succeed for valid CRL PEM"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify the DER can be parsed as a CRL
|
|
||||||
let der_bytes = der.unwrap();
|
|
||||||
let parsed = CertificateRevocationList::from_der(&der_bytes);
|
|
||||||
assert!(parsed.is_ok(), "DER should parse as a valid CRL");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_pem_extraction_works_for_empty_crl() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (ca_key, ca_cert) = generate_test_ca();
|
|
||||||
let crl_pem = generate_test_crl(&ca_key, &ca_cert, &[]);
|
|
||||||
|
|
||||||
// Verify PEM extraction succeeds for empty CRL
|
|
||||||
let der = extract_pem_crl_der(crl_pem.as_bytes());
|
|
||||||
assert!(
|
|
||||||
der.is_some(),
|
|
||||||
"PEM extraction should succeed for empty CRL PEM"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify the DER can be parsed as a CRL
|
|
||||||
let der_bytes = der.unwrap();
|
|
||||||
let parsed = CertificateRevocationList::from_der(&der_bytes);
|
|
||||||
assert!(parsed.is_ok(), "DER should parse as a valid CRL");
|
|
||||||
|
|
||||||
// Empty CRL should have no revoked certificates
|
|
||||||
let (_, crl) = parsed.unwrap();
|
|
||||||
let revoked: Vec<_> = crl.iter_revoked_certificates().collect();
|
|
||||||
assert!(
|
|
||||||
revoked.is_empty(),
|
|
||||||
"Empty CRL should have no revoked entries"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_pem_extraction_rejects_tampered_content() {
|
|
||||||
// Tampering with the base64 content should cause extraction to either
|
|
||||||
// fail or produce invalid DER that can't be parsed.
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (ca_key, ca_cert) = generate_test_ca();
|
|
||||||
let (serial1, _) = make_serial_hex_pair();
|
|
||||||
let crl_pem = generate_test_crl(&ca_key, &ca_cert, &[serial1]);
|
|
||||||
|
|
||||||
// Tamper with the base64 content
|
|
||||||
let mut tampered_bytes = crl_pem.into_bytes();
|
|
||||||
let mid = tampered_bytes.len() / 2;
|
|
||||||
// Find a byte that's part of the base64 content (not header/footer/newline)
|
|
||||||
for i in (mid.saturating_sub(10)..mid.saturating_add(10)).rev() {
|
|
||||||
if tampered_bytes[i] != b'\n' && tampered_bytes[i] != b'-' {
|
|
||||||
tampered_bytes[i] ^= 0x01;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PEM extraction may still succeed (it just extracts base64),
|
|
||||||
// but the resulting DER should fail signature verification
|
|
||||||
// or parse incorrectly.
|
|
||||||
let der = extract_pem_crl_der(&tampered_bytes);
|
|
||||||
if let Some(der_data) = der {
|
|
||||||
// If PEM extraction succeeded, the DER should either fail to parse
|
|
||||||
// or fail signature verification. We just verify it's not a valid
|
|
||||||
// CRL that we can trust.
|
|
||||||
let _ = CertificateRevocationList::from_der(&der_data);
|
|
||||||
// The CRL may parse but won't verify — that's expected.
|
|
||||||
}
|
|
||||||
// Either way, tampered content is detected at some level.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_missing_file_returns_missing_status() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (_, ca_cert) = generate_test_ca();
|
|
||||||
let ca_cert_der = ca_cert.der().to_vec();
|
|
||||||
|
|
||||||
// Use a path that doesn't exist
|
|
||||||
let missing_path = std::path::PathBuf::from("/tmp/nonexistent_crl_test_12345.pem");
|
|
||||||
let _ = std::fs::remove_file(&missing_path); // Ensure it doesn't exist
|
|
||||||
|
|
||||||
let state = load_crl(&missing_path, &ca_cert_der);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
state.status,
|
|
||||||
CrlStatus::Missing,
|
|
||||||
"Missing CRL file should return Missing status"
|
|
||||||
);
|
|
||||||
assert!(state.revoked_serials.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_wrong_pem_type_rejected() {
|
|
||||||
// PEM with wrong type marker should not extract as CRL
|
|
||||||
let cert_pem = "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAKHHCgVZU65BMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBnRlc3Qx\n-----END CERTIFICATE-----";
|
|
||||||
let result = extract_pem_crl_der(cert_pem.as_bytes());
|
|
||||||
assert!(
|
|
||||||
result.is_none(),
|
|
||||||
"CERTIFICATE PEM should not extract as CRL"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_revoked_certificates_count_in_parsed_crl() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (ca_key, ca_cert) = generate_test_ca();
|
|
||||||
|
|
||||||
// Create CRL with 2 revoked serials
|
|
||||||
let (s1, _) = make_serial_hex_pair();
|
|
||||||
let (s2, _) = make_serial_hex_pair();
|
|
||||||
let crl_pem = generate_test_crl(&ca_key, &ca_cert, &[s1, s2]);
|
|
||||||
|
|
||||||
// Extract and parse the CRL
|
|
||||||
let der = extract_pem_crl_der(crl_pem.as_bytes()).expect("PEM extraction should succeed");
|
|
||||||
let (_, crl) =
|
|
||||||
CertificateRevocationList::from_der(&der).expect("DER parsing should succeed");
|
|
||||||
|
|
||||||
// Verify 2 revoked entries
|
|
||||||
let revoked: Vec<_> = crl.iter_revoked_certificates().collect();
|
|
||||||
assert_eq!(revoked.len(), 2, "CRL should have 2 revoked entries");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_empty_crl_has_no_revoked_entries() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (ca_key, ca_cert) = generate_test_ca();
|
|
||||||
let crl_pem = generate_test_crl(&ca_key, &ca_cert, &[]);
|
|
||||||
|
|
||||||
let der = extract_pem_crl_der(crl_pem.as_bytes()).expect("PEM extraction should succeed");
|
|
||||||
let (_, crl) =
|
|
||||||
CertificateRevocationList::from_der(&der).expect("DER parsing should succeed");
|
|
||||||
|
|
||||||
let revoked: Vec<_> = crl.iter_revoked_certificates().collect();
|
|
||||||
assert!(
|
|
||||||
revoked.is_empty(),
|
|
||||||
"Empty CRL should have no revoked entries"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn crl_state_transitions() {
|
|
||||||
// Test CrlStatus transitions using the in-memory CrlState
|
|
||||||
// (signature verification is tested end-to-end with real CRLs)
|
|
||||||
|
|
||||||
// Valid → should have revoked serials if any
|
|
||||||
let valid_state = CrlState {
|
|
||||||
status: CrlStatus::Valid,
|
|
||||||
revoked_serials: {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
set.insert("aabbccdd".to_string());
|
|
||||||
set
|
|
||||||
},
|
|
||||||
crl_mtime: Some(std::time::SystemTime::now()),
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
assert!(valid_state.is_revoked("aabbccdd"));
|
|
||||||
assert!(!valid_state.is_revoked("11223344"));
|
|
||||||
|
|
||||||
// Expired → still has revoked serials (usable but stale)
|
|
||||||
let expired_state = CrlState {
|
|
||||||
status: CrlStatus::Expired,
|
|
||||||
revoked_serials: valid_state.revoked_serials.clone(),
|
|
||||||
crl_mtime: Some(std::time::SystemTime::now() - std::time::Duration::from_secs(86400)),
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
assert!(expired_state.is_revoked("aabbccdd"));
|
|
||||||
|
|
||||||
// Missing → no serials, no mtime
|
|
||||||
let missing_state = CrlState::default();
|
|
||||||
assert_eq!(missing_state.status, CrlStatus::Missing);
|
|
||||||
assert!(missing_state.revoked_serials.is_empty());
|
|
||||||
assert!(missing_state.crl_mtime.is_none());
|
|
||||||
|
|
||||||
// Invalid → no serials (fail-closed)
|
|
||||||
let invalid_state = CrlState {
|
|
||||||
status: CrlStatus::Invalid,
|
|
||||||
revoked_serials: HashSet::new(),
|
|
||||||
crl_mtime: Some(std::time::SystemTime::now()),
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
assert!(
|
|
||||||
!invalid_state.is_revoked("aabbccdd"),
|
|
||||||
"Invalid CRL should not match any serial"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract_pem_cert_der_invalid() {
|
|
||||||
// Not PEM
|
|
||||||
assert!(extract_pem_cert_der(b"not pem").is_none());
|
|
||||||
// PEM but wrong type (CRL instead of CERTIFICATE)
|
|
||||||
assert!(
|
|
||||||
extract_pem_cert_der(b"-----BEGIN X509 CRL-----\nAA==\n-----END X509 CRL-----")
|
|
||||||
.is_none()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract_pem_cert_der_valid() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
let (_, ca_cert) = generate_test_ca();
|
|
||||||
let cert_pem = ca_cert.pem();
|
|
||||||
|
|
||||||
// Verify PEM extraction succeeds
|
|
||||||
let der = extract_pem_cert_der(cert_pem.as_bytes());
|
|
||||||
assert!(
|
|
||||||
der.is_some(),
|
|
||||||
"PEM extraction should succeed for valid certificate PEM"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify the DER can be parsed as an X.509 certificate
|
|
||||||
let der_bytes = der.unwrap();
|
|
||||||
let parsed = x509_parser::parse_x509_certificate(&der_bytes);
|
|
||||||
assert!(
|
|
||||||
parsed.is_ok(),
|
|
||||||
"DER should parse as a valid X.509 certificate"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract_pem_cert_der_rejects_crl_pem() {
|
|
||||||
// CERTIFICATE extraction should reject CRL PEM
|
|
||||||
let crl_pem = "-----BEGIN X509 CRL-----\nAA==\n-----END X509 CRL-----";
|
|
||||||
assert!(
|
|
||||||
extract_pem_cert_der(crl_pem.as_bytes()).is_none(),
|
|
||||||
"CRL PEM should not extract as CERTIFICATE"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
163
src/auth/mtls.rs
163
src/auth/mtls.rs
@ -494,167 +494,4 @@ mod tests {
|
|||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().to_string().contains("expired"));
|
assert!(result.unwrap_err().to_string().contains("expired"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// CrlAwareVerifier unit tests
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// Test that CrlAwareVerifier can be constructed with a WebPKI verifier
|
|
||||||
/// and a SharedCrlState. This verifies the wiring is correct.
|
|
||||||
#[test]
|
|
||||||
fn crl_aware_verifier_construction() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
use super::super::crl::{new_shared_state, CrlState, CrlStatus};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
// Build a simple CA cert + key for the root store.
|
|
||||||
let ca_key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
|
||||||
let mut ca_params = rcgen::CertificateParams::default();
|
|
||||||
ca_params.not_before = time::OffsetDateTime::now_utc();
|
|
||||||
ca_params.not_after = time::OffsetDateTime::now_utc() + time::Duration::days(365);
|
|
||||||
ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
|
|
||||||
ca_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
|
|
||||||
let mut dn = rcgen::DistinguishedName::new();
|
|
||||||
dn.push(rcgen::DnType::CommonName, "Test CA for Verifier");
|
|
||||||
ca_params.distinguished_name = dn;
|
|
||||||
let ca_cert = ca_params.self_signed(&ca_key).unwrap();
|
|
||||||
|
|
||||||
// Build root cert store with the CA.
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add(ca_cert.der().to_owned()).unwrap();
|
|
||||||
|
|
||||||
// Build WebPKI verifier — build() returns Arc<WebPkiClientVerifier>
|
|
||||||
// which coerces to Arc<dyn ClientCertVerifier>.
|
|
||||||
let webpki_verifier: Arc<dyn ClientCertVerifier> =
|
|
||||||
WebPkiClientVerifier::builder(root_store.into())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Build CRL state in Valid status.
|
|
||||||
let crl_state = new_shared_state();
|
|
||||||
let valid_state = CrlState {
|
|
||||||
status: CrlStatus::Valid,
|
|
||||||
revoked_serials: HashSet::new(),
|
|
||||||
crl_mtime: None,
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
crl_state.store(Arc::new(valid_state));
|
|
||||||
|
|
||||||
// Construct CrlAwareVerifier — should succeed.
|
|
||||||
let _verifier = CrlAwareVerifier::new(webpki_verifier, crl_state);
|
|
||||||
// If we reach here without panic, construction succeeded.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that CrlAwareVerifier with Missing CRL state can be constructed.
|
|
||||||
/// Missing CRL means the verifier falls back to WebPKI-only.
|
|
||||||
#[test]
|
|
||||||
fn crl_aware_verifier_with_missing_crl() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
use super::super::crl::new_shared_state;
|
|
||||||
|
|
||||||
let ca_key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
|
||||||
let mut ca_params = rcgen::CertificateParams::default();
|
|
||||||
ca_params.not_before = time::OffsetDateTime::now_utc();
|
|
||||||
ca_params.not_after = time::OffsetDateTime::now_utc() + time::Duration::days(365);
|
|
||||||
ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
|
|
||||||
ca_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
|
|
||||||
let mut dn = rcgen::DistinguishedName::new();
|
|
||||||
dn.push(rcgen::DnType::CommonName, "Test CA for Verifier");
|
|
||||||
ca_params.distinguished_name = dn;
|
|
||||||
let ca_cert = ca_params.self_signed(&ca_key).unwrap();
|
|
||||||
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add(ca_cert.der().to_owned()).unwrap();
|
|
||||||
|
|
||||||
let webpki_verifier: Arc<dyn ClientCertVerifier> =
|
|
||||||
WebPkiClientVerifier::builder(root_store.into())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Default state is Missing.
|
|
||||||
let crl_state = new_shared_state();
|
|
||||||
let _verifier = CrlAwareVerifier::new(webpki_verifier, crl_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that CrlAwareVerifier with Invalid CRL state can be constructed.
|
|
||||||
/// Invalid CRL means the verifier should reject ALL client certificates.
|
|
||||||
#[test]
|
|
||||||
fn crl_aware_verifier_with_invalid_crl() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
use super::super::crl::{new_shared_state, CrlState, CrlStatus};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
let ca_key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
|
||||||
let mut ca_params = rcgen::CertificateParams::default();
|
|
||||||
ca_params.not_before = time::OffsetDateTime::now_utc();
|
|
||||||
ca_params.not_after = time::OffsetDateTime::now_utc() + time::Duration::days(365);
|
|
||||||
ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
|
|
||||||
ca_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
|
|
||||||
let mut dn = rcgen::DistinguishedName::new();
|
|
||||||
dn.push(rcgen::DnType::CommonName, "Test CA for Verifier");
|
|
||||||
ca_params.distinguished_name = dn;
|
|
||||||
let ca_cert = ca_params.self_signed(&ca_key).unwrap();
|
|
||||||
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add(ca_cert.der().to_owned()).unwrap();
|
|
||||||
|
|
||||||
let webpki_verifier: Arc<dyn ClientCertVerifier> =
|
|
||||||
WebPkiClientVerifier::builder(root_store.into())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let crl_state = new_shared_state();
|
|
||||||
let invalid_state = CrlState {
|
|
||||||
status: CrlStatus::Invalid,
|
|
||||||
revoked_serials: HashSet::new(),
|
|
||||||
crl_mtime: None,
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
crl_state.store(Arc::new(invalid_state));
|
|
||||||
|
|
||||||
let _verifier = CrlAwareVerifier::new(webpki_verifier, crl_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that CrlAwareVerifier with a revoked serial in Valid CRL state
|
|
||||||
/// can be constructed. The actual verification logic is tested through
|
|
||||||
/// integration tests since it requires a full TLS handshake.
|
|
||||||
#[test]
|
|
||||||
fn crl_aware_verifier_with_revoked_serial() {
|
|
||||||
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
||||||
use super::super::crl::{new_shared_state, CrlState, CrlStatus};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
let ca_key = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
|
|
||||||
let mut ca_params = rcgen::CertificateParams::default();
|
|
||||||
ca_params.not_before = time::OffsetDateTime::now_utc();
|
|
||||||
ca_params.not_after = time::OffsetDateTime::now_utc() + time::Duration::days(365);
|
|
||||||
ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
|
|
||||||
ca_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
|
|
||||||
let mut dn = rcgen::DistinguishedName::new();
|
|
||||||
dn.push(rcgen::DnType::CommonName, "Test CA for Verifier");
|
|
||||||
ca_params.distinguished_name = dn;
|
|
||||||
let ca_cert = ca_params.self_signed(&ca_key).unwrap();
|
|
||||||
|
|
||||||
let mut root_store = RootCertStore::empty();
|
|
||||||
root_store.add(ca_cert.der().to_owned()).unwrap();
|
|
||||||
|
|
||||||
let webpki_verifier: Arc<dyn ClientCertVerifier> =
|
|
||||||
WebPkiClientVerifier::builder(root_store.into())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let crl_state = new_shared_state();
|
|
||||||
let mut revoked = HashSet::new();
|
|
||||||
revoked.insert("deadbeef".to_string());
|
|
||||||
let valid_with_revoked = CrlState {
|
|
||||||
status: CrlStatus::Valid,
|
|
||||||
revoked_serials: revoked,
|
|
||||||
crl_mtime: None,
|
|
||||||
loaded_at: std::time::SystemTime::now(),
|
|
||||||
};
|
|
||||||
crl_state.store(Arc::new(valid_with_revoked));
|
|
||||||
|
|
||||||
let _verifier = CrlAwareVerifier::new(webpki_verifier, crl_state);
|
|
||||||
// Construction succeeded — the verifier is ready to reject revoked certs.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,12 +38,8 @@ pub enum EnrollmentStatusResponse {
|
|||||||
Pending,
|
Pending,
|
||||||
Approved {
|
Approved {
|
||||||
ca_crt: String,
|
ca_crt: String,
|
||||||
#[serde(default)]
|
|
||||||
ca_chain: String,
|
|
||||||
server_crt: String,
|
server_crt: String,
|
||||||
server_key: String,
|
server_key: String,
|
||||||
#[serde(default)]
|
|
||||||
crl_pem: String,
|
|
||||||
},
|
},
|
||||||
Denied,
|
Denied,
|
||||||
NotFound,
|
NotFound,
|
||||||
@ -53,10 +49,8 @@ pub enum EnrollmentStatusResponse {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct PkiBundle {
|
pub struct PkiBundle {
|
||||||
pub ca_crt: String,
|
pub ca_crt: String,
|
||||||
pub ca_chain: String,
|
|
||||||
pub server_crt: String,
|
pub server_crt: String,
|
||||||
pub server_key: String,
|
pub server_key: String,
|
||||||
pub crl_pem: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EnrollmentStatusResponse> for Option<PkiBundle> {
|
impl From<EnrollmentStatusResponse> for Option<PkiBundle> {
|
||||||
@ -64,16 +58,12 @@ impl From<EnrollmentStatusResponse> for Option<PkiBundle> {
|
|||||||
match response {
|
match response {
|
||||||
EnrollmentStatusResponse::Approved {
|
EnrollmentStatusResponse::Approved {
|
||||||
ca_crt,
|
ca_crt,
|
||||||
ca_chain,
|
|
||||||
server_crt,
|
server_crt,
|
||||||
server_key,
|
server_key,
|
||||||
crl_pem,
|
|
||||||
} => Some(PkiBundle {
|
} => Some(PkiBundle {
|
||||||
ca_crt,
|
ca_crt,
|
||||||
ca_chain,
|
|
||||||
server_crt,
|
server_crt,
|
||||||
server_key,
|
server_key,
|
||||||
crl_pem,
|
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -461,10 +451,8 @@ impl EnrollmentClient {
|
|||||||
}
|
}
|
||||||
EnrollmentStatusResponse::Approved {
|
EnrollmentStatusResponse::Approved {
|
||||||
ca_crt,
|
ca_crt,
|
||||||
ca_chain,
|
|
||||||
server_crt,
|
server_crt,
|
||||||
server_key,
|
server_key,
|
||||||
crl_pem,
|
|
||||||
} => {
|
} => {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
elapsed_seconds = start.elapsed().as_secs(),
|
elapsed_seconds = start.elapsed().as_secs(),
|
||||||
@ -473,10 +461,8 @@ impl EnrollmentClient {
|
|||||||
);
|
);
|
||||||
return Ok(PkiBundle {
|
return Ok(PkiBundle {
|
||||||
ca_crt,
|
ca_crt,
|
||||||
ca_chain,
|
|
||||||
server_crt,
|
server_crt,
|
||||||
server_key,
|
server_key,
|
||||||
crl_pem,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
EnrollmentStatusResponse::Denied => {
|
EnrollmentStatusResponse::Denied => {
|
||||||
@ -580,10 +566,8 @@ mod tests {
|
|||||||
fn approved_to_pki_bundle() {
|
fn approved_to_pki_bundle() {
|
||||||
let status = EnrollmentStatusResponse::Approved {
|
let status = EnrollmentStatusResponse::Approved {
|
||||||
ca_crt: "ca".into(),
|
ca_crt: "ca".into(),
|
||||||
ca_chain: String::new(),
|
|
||||||
server_crt: "crt".into(),
|
server_crt: "crt".into(),
|
||||||
server_key: "key".into(),
|
server_key: "key".into(),
|
||||||
crl_pem: String::new(),
|
|
||||||
};
|
};
|
||||||
let bundle: Option<PkiBundle> = status.into();
|
let bundle: Option<PkiBundle> = status.into();
|
||||||
assert!(bundle.is_some());
|
assert!(bundle.is_some());
|
||||||
|
|||||||
@ -160,10 +160,8 @@ pub async fn run_enrollment(
|
|||||||
// Write certificates to configured paths (or defaults)
|
// Write certificates to configured paths (or defaults)
|
||||||
provision::provision_pki_bundle(
|
provision::provision_pki_bundle(
|
||||||
&pki_bundle.ca_crt,
|
&pki_bundle.ca_crt,
|
||||||
&pki_bundle.ca_chain,
|
|
||||||
&pki_bundle.server_crt,
|
&pki_bundle.server_crt,
|
||||||
&pki_bundle.server_key,
|
&pki_bundle.server_key,
|
||||||
&pki_bundle.crl_pem,
|
|
||||||
config.tls_config(),
|
config.tls_config(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@ -16,8 +16,6 @@ const DEFAULT_CA_CERT: &str = "/etc/linux_patch_api/certs/ca.pem";
|
|||||||
const DEFAULT_SERVER_CERT: &str = "/etc/linux_patch_api/certs/server.pem";
|
const DEFAULT_SERVER_CERT: &str = "/etc/linux_patch_api/certs/server.pem";
|
||||||
/// Default server key path.
|
/// Default server key path.
|
||||||
const DEFAULT_SERVER_KEY: &str = "/etc/linux_patch_api/certs/server.key.pem";
|
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).
|
/// Validate that a PEM string has proper format (BEGIN/END markers present).
|
||||||
///
|
///
|
||||||
@ -130,14 +128,12 @@ pub fn write_pem_file(path: &str, pem_data: &str, is_key: bool) -> Result<()> {
|
|||||||
|
|
||||||
/// Provision the full PKI bundle from an approved enrollment response.
|
/// Provision the full PKI bundle from an approved enrollment response.
|
||||||
///
|
///
|
||||||
/// Writes CA cert, CA chain, server cert, server key, and CRL to configured paths.
|
/// Writes CA cert, server cert, and server key to configured paths.
|
||||||
/// Paths are read from TLS config if available, otherwise defaults are used.
|
/// Paths are read from TLS config if available, otherwise defaults are used.
|
||||||
pub async fn provision_pki_bundle(
|
pub async fn provision_pki_bundle(
|
||||||
ca_crt: &str,
|
ca_crt: &str,
|
||||||
_ca_chain: &str,
|
|
||||||
server_crt: &str,
|
server_crt: &str,
|
||||||
server_key: &str,
|
server_key: &str,
|
||||||
crl_pem: &str,
|
|
||||||
tls_config: Option<&super::super::config::loader::TlsConfig>,
|
tls_config: Option<&super::super::config::loader::TlsConfig>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Determine target paths from config or defaults
|
// Determine target paths from config or defaults
|
||||||
@ -177,19 +173,6 @@ pub async fn provision_pki_bundle(
|
|||||||
|
|
||||||
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")?;
|
||||||
|
|
||||||
// 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
|
// 3. Log successful provisioning with structured fields
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
ca_cert = %ca_path,
|
ca_cert = %ca_path,
|
||||||
|
|||||||
@ -178,10 +178,8 @@ async fn test_full_enrollment_flow_happy_path() {
|
|||||||
let tls_config = build_tls_config(cert_dir.path());
|
let tls_config = build_tls_config(cert_dir.path());
|
||||||
provision::provision_pki_bundle(
|
provision::provision_pki_bundle(
|
||||||
&bundle.ca_crt,
|
&bundle.ca_crt,
|
||||||
&bundle.ca_chain,
|
|
||||||
&bundle.server_crt,
|
&bundle.server_crt,
|
||||||
&bundle.server_key,
|
&bundle.server_key,
|
||||||
&bundle.crl_pem,
|
|
||||||
Some(&tls_config),
|
Some(&tls_config),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -447,10 +445,8 @@ async fn test_certificate_permission_verification() {
|
|||||||
let tls_config = build_tls_config(cert_dir.path());
|
let tls_config = build_tls_config(cert_dir.path());
|
||||||
provision::provision_pki_bundle(
|
provision::provision_pki_bundle(
|
||||||
&bundle.ca_crt,
|
&bundle.ca_crt,
|
||||||
&bundle.ca_chain,
|
|
||||||
&bundle.server_crt,
|
&bundle.server_crt,
|
||||||
&bundle.server_key,
|
&bundle.server_key,
|
||||||
&bundle.crl_pem,
|
|
||||||
Some(&tls_config),
|
Some(&tls_config),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user