Skip to content

Commit

Permalink
Add more parsing options for ca certs, and tests for them.
Browse files Browse the repository at this point in the history
  • Loading branch information
acw committed Mar 14, 2024
1 parent 920a02f commit f98f9cf
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 16 deletions.
67 changes: 53 additions & 14 deletions lib/src/config/backends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,7 @@ mod deserialization {

let ca_certs = toml
.remove("ca_certificate")
.map(|ca_cert| match ca_cert {
Value::String(ca_cert) if !ca_cert.trim().is_empty() => {
let mut cursor = std::io::Cursor::new(ca_cert);
rustls_pemfile::certs(&mut cursor)
.map_err(|_| BackendConfigError::InvalidCACertEntry)
.map(|mut x| {
x.drain(..)
.map(|c| rustls::Certificate(c))
.collect::<Vec<rustls::Certificate>>()
})
}
Value::String(_) => Err(BackendConfigError::EmptyCACert),
_ => Err(BackendConfigError::InvalidCACertEntry),
})
.map(parse_ca_cert_section)
.unwrap_or_else(|| Ok(vec![]))?;

let grpc = toml
Expand Down Expand Up @@ -172,4 +159,56 @@ mod deserialization {
})
}
}

fn parse_ca_cert_section(
ca_cert: Value,
) -> Result<Vec<rustls::Certificate>, BackendConfigError> {
match ca_cert {
Value::String(ca_cert) if !ca_cert.trim().is_empty() => {
let mut cursor = std::io::Cursor::new(ca_cert);
rustls_pemfile::certs(&mut cursor)
.map_err(|e| BackendConfigError::InvalidCACertEntry(format!("Couldn't process certificate: {}", e)))
.map(|mut x| {
x.drain(..)
.map(rustls::Certificate)
.collect::<Vec<rustls::Certificate>>()
})
}
Value::String(_) => Err(BackendConfigError::EmptyCACert),

Value::Array(array) => {
let mut result = vec![];

for item in array.into_iter() {
let mut current = parse_ca_cert_section(item)?;
result.append(&mut current);
}

Ok(result)
}

Value::Table(mut table) => {
match table.remove("file") {
None => match table.remove("value") {
None => Err(BackendConfigError::InvalidCACertEntry("'ca_certificate' was a dictionary without a 'file' or 'value' field".to_string())),
Some(strval @ Value::String(_)) => parse_ca_cert_section(strval),
Some(_) => Err(BackendConfigError::InvalidCACertEntry("invalid format for 'value' field".to_string())),
},
Some(Value::String(x)) => {
if !table.is_empty() {
return Err(BackendConfigError::InvalidCACertEntry(format!("unknown ca_certificate keys: {:?}", table.keys().collect::<Vec<_>>())));
}

let data = std::fs::read_to_string(&x)
.map_err(|e| BackendConfigError::InvalidCACertEntry(format!("{}", e)))?;
parse_ca_cert_section(Value::String(data))
}

Some(_) => Err(BackendConfigError::InvalidCACertEntry("invalid format for file reference".to_string())),
}
}

_ => Err(BackendConfigError::InvalidCACertEntry("unknown format for 'ca_certificates' field; should be a certificate string, a dictionary with a file reference, or an array of the previous".to_string())),
}
}
}
83 changes: 83 additions & 0 deletions lib/src/config/unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,4 +1132,87 @@ Qq7tJAMLnPnAdAUousI0RDcLpB8adGkhZH66lL4oV9U+aQ0dA0oiqSKZtMoHeWbr
.expect("no blåhaj :(");
assert!(!shark_backend.ca_certs.is_empty());
}

#[test]
fn reads_file_path_ca_certs() {
let ca_backend = format!(
r#"
[backends]
[backends.dog]
url = "http://localhost:7676/dog-mocks"
[backends."shark.server"]
url = "http://localhost:7676/shark-mocks"
override_host = "somehost.com"
ca_certificate.file = {:?}
"#,
concat!(env!("CARGO_MANIFEST_DIR"), "/../test-fixtures/data/ca.pem")
);

let with_ca = read_local_server_config(&ca_backend).expect("can parse backends with ca");
let dog_backend = with_ca.backends.0.get("dog").expect("fetch failed :(");
assert!(dog_backend.ca_certs.is_empty());
let shark_backend = with_ca
.backends
.0
.get("shark.server")
.expect("no blåhaj :(");
assert!(!shark_backend.ca_certs.is_empty());
}

#[test]
fn reads_multiple_ca_certs() {
let ca_backend = format!(
r#"
[backends]
[backends.dog]
url = "http://localhost:7676/dog-mocks"
[backends."shark.server"]
url = "http://localhost:7676/shark-mocks"
override_host = "somehost.com"
[[backends."shark.server".ca_certificate]]
file = {:?}
[[backends."shark.server".ca_certificate]]
file = {:?}
[[backends."shark.server".ca_certificate]]
value = '''
-----BEGIN CERTIFICATE-----
MIIDqTCCApGgAwIBAgIUDXDr/2fouphqlB8iJASenWOr/XwwDQYJKoZIhvcNAQEL
BQAwZDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBk9yZWdvbjERMA8GA1UEBwwIUG9y
dGxhbmQxEDAOBgNVBAoMB1ZpY2Vyb3kxHzAdBgkqhkiG9w0BCQEWEGF3aWNrQGZh
c3RseS5jb20wHhcNMjMwNzI3MDAwODU5WhcNMzMwNzI0MDAwODU5WjBkMQswCQYD
VQQGEwJVUzEPMA0GA1UECAwGT3JlZ29uMREwDwYDVQQHDAhQb3J0bGFuZDEQMA4G
A1UECgwHVmljZXJveTEfMB0GCSqGSIb3DQEJARYQYXdpY2tAZmFzdGx5LmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxXdG4C6yEeLTtFPOXWTv1N
eEeJMLcAoupB9u3x0PYT+w+0ruAympviqGbEiyZL/qMKLYenLiQO+72VCISW5qfB
ZoCpwDxBon5TDUZ98JU93nVRml7uOg25G+KTs3aeJt6+rFDPNaNyxVcKgCuURB4y
mwgosLUvxoEffFnHlURETLN4aSGQ6TLp8YEJp4EudTVo/l+kdhm6sLZMBkmUxnnl
muEc8ePAr1igYchz2tbcWRjzxoUOuEdoKaW2OCElNObt2WYPWzHs+6p1K8+KyTRY
/pVOFtA43nuWmk++UHFthBAw9IqBuO0FMJr4SULnKfiTh5E9F+nZ0Q/1nfzzsAMC
AwEAAaNTMFEwHQYDVR0OBBYEFGYM6HhP8yZ17eXw5nOfQ971u1l9MB8GA1UdIwQY
MBaAFGYM6HhP8yZ17eXw5nOfQ971u1l9MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAFmFkUodKTXeT683GEj4SoiMbDL8d3x+Vc+kvLPC2Jloru4R
Qo0USu3eJZjNxKjmPbLii8gzf5ZmZHdytWQ+5irYjXBHrE9tPgmpavhM+0otpnUd
vYosnfwv/aQEIiqeMkpqzbSKvb2I+TVpAC1xb6qbYE95tnsX/KEdAoJ/SAcZLGYQ
LKGTjz3eKlgUWy69uwzHXkie8hxDVRlyA7cFY4AAqsLhL2KQPWtMT7fRKrVKfLYd
Qq7tJAMLnPnAdAUousI0RDcLpB8adGkhZH66lL4oV9U+aQ0dA0oiqSKZtMoHeWbr
/L0ti7ZOfxOxRRCzt8KdLo/kGNTfAz+74P0MY80=
-----END CERTIFICATE-----
'''
"#,
concat!(env!("CARGO_MANIFEST_DIR"), "/../test-fixtures/data/ca.pem"),
concat!(env!("CARGO_MANIFEST_DIR"), "/../test-fixtures/data/ca.pem")
);

let with_ca = read_local_server_config(&ca_backend).expect("can parse backends with ca");
let dog_backend = with_ca.backends.0.get("dog").expect("fetch failed :(");
assert!(dog_backend.ca_certs.is_empty());
let shark_backend = with_ca
.backends
.0
.get("shark.server")
.expect("no blåhaj :(");
assert_eq!(3, shark_backend.ca_certs.len());
}
}
4 changes: 2 additions & 2 deletions lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,8 @@ pub enum BackendConfigError {
#[error("'ca_certificate' field is empty")]
EmptyCACert,

#[error("'ca_certificate' field was not a string")]
InvalidCACertEntry,
#[error("'ca_certificate' field was invalid: {0}")]
InvalidCACertEntry(String),

#[error("'use_sni' field was not a boolean")]
InvalidUseSniEntry,
Expand Down

0 comments on commit f98f9cf

Please sign in to comment.