Added fallback mTLS enrollment port to handle OTS authentication

This commit is contained in:
2026-05-15 16:33:53 -03:00
parent 7fa4a7c411
commit ed039035df

View File

@@ -7,6 +7,9 @@ use uuid::Uuid;
use super::connector::connect_mtls_from_pem; use super::connector::connect_mtls_from_pem;
use crate::tcp::transport::TransportStream; use crate::tcp::transport::TransportStream;
const DEFAULT_MTLS_SERVER_PORT: &str = "8089";
const DEFAULT_ENROLL_PATH: &str = "/Marti/api/tls/signClient/v2";
#[derive(Deserialize)] #[derive(Deserialize)]
struct EnrollmentResponse { struct EnrollmentResponse {
#[serde(rename = "signedCert")] #[serde(rename = "signedCert")]
@@ -27,6 +30,23 @@ fn extract_tag_value(xml: &str, tag_name: &str) -> Option<String> {
Some(xml[start..end].trim().to_string()) Some(xml[start..end].trim().to_string())
} }
fn normalize_certificate_pem(certificate: &str) -> String {
let trimmed = certificate.trim();
if trimmed.contains("-----BEGIN CERTIFICATE-----") {
if trimmed.ends_with('\n') {
trimmed.to_string()
} else {
format!("{}\n", trimmed)
}
} else {
wrap_pem_body(
trimmed,
"-----BEGIN CERTIFICATE-----",
"-----END CERTIFICATE-----",
)
}
}
fn wrap_pem_body(base64_body: &str, begin: &str, end: &str) -> String { fn wrap_pem_body(base64_body: &str, begin: &str, end: &str) -> String {
let mut wrapped = String::new(); let mut wrapped = String::new();
let normalized = base64_body.trim().replace(['\r', '\n'], ""); let normalized = base64_body.trim().replace(['\r', '\n'], "");
@@ -89,10 +109,20 @@ fn fetch_enrollment_config(host: &str, enroll_port: &str) -> Result<EnrollmentCo
.text() .text()
.map_err(|e| format!("failed to read config response from {}: {}", url, e))?; .map_err(|e| format!("failed to read config response from {}: {}", url, e))?;
let server_port = extract_tag_value(&response_text, "serverPort") let server_port = extract_tag_value(&response_text, "serverPort").unwrap_or_else(|| {
.ok_or_else(|| "missing serverPort in /Marti/api/tls/config response".to_string())?; info!(
let enroll_path = extract_tag_value(&response_text, "enrollPath") "Enrollment config did not include serverPort; using default TAK mTLS port {}",
.ok_or_else(|| "missing enrollPath in /Marti/api/tls/config response".to_string())?; DEFAULT_MTLS_SERVER_PORT
);
DEFAULT_MTLS_SERVER_PORT.to_string()
});
let enroll_path = extract_tag_value(&response_text, "enrollPath").unwrap_or_else(|| {
info!(
"Enrollment config did not include enrollPath; using default TAK enrollment path {}",
DEFAULT_ENROLL_PATH
);
DEFAULT_ENROLL_PATH.to_string()
});
info!( info!(
"Enrollment config received: server_port={} enroll_path={}", "Enrollment config received: server_port={} enroll_path={}",
@@ -173,14 +203,11 @@ fn enroll_client_certificate(
enrollment.ca0.len() enrollment.ca0.len()
); );
let cert_pem = wrap_pem_body( let ca_cert_pem = normalize_certificate_pem(&enrollment.ca0);
&enrollment.signed_cert, let cert_pem = normalize_certificate_pem(&enrollment.signed_cert);
"-----BEGIN CERTIFICATE-----",
"-----END CERTIFICATE-----",
);
let key_pem = key_pair.serialize_pem(); let key_pem = key_pair.serialize_pem();
Ok((enrollment.ca0, cert_pem, key_pem)) Ok((ca_cert_pem, cert_pem, key_pem))
} }
pub fn enroll_and_connect( pub fn enroll_and_connect(
@@ -223,3 +250,52 @@ pub fn enroll_and_connect(
&client_key_pem, &client_key_pem,
) )
} }
#[cfg(test)]
mod tests {
use super::{extract_tag_value, normalize_certificate_pem, wrap_pem_body};
#[test]
fn extracts_tak_enrollment_config_tag_values() {
let xml = "<tlsConfig><serverPort>8089</serverPort><enrollPath>/Marti/api/tls/signClient/v2</enrollPath></tlsConfig>";
assert_eq!(
extract_tag_value(xml, "serverPort").as_deref(),
Some("8089")
);
assert_eq!(
extract_tag_value(xml, "enrollPath").as_deref(),
Some("/Marti/api/tls/signClient/v2")
);
}
#[test]
fn missing_config_tag_values_are_none_for_opentakserver_config() {
let xml =
"<tlsConfig><nameEntries><nameEntry name=\"OpenTAKServer\"/></nameEntries></tlsConfig>";
assert!(extract_tag_value(xml, "serverPort").is_none());
assert!(extract_tag_value(xml, "enrollPath").is_none());
}
#[test]
fn normalizes_base64_certificate_body_to_pem() {
let pem = normalize_certificate_pem("QUJDREVGRw==");
assert_eq!(
pem,
wrap_pem_body(
"QUJDREVGRw==",
"-----BEGIN CERTIFICATE-----",
"-----END CERTIFICATE-----"
)
);
}
#[test]
fn preserves_existing_certificate_pem() {
let pem = "-----BEGIN CERTIFICATE-----\nQUJDREVGRw==\n-----END CERTIFICATE-----\n";
assert_eq!(normalize_certificate_pem(pem), pem);
}
}