Skip to content

Commit b4d9c1c

Browse files
Fix TLS CA certificate not being applied to the HTTP client
The CA certificate from --tls-ca-cert-file was being loaded into a rustls::RootCertStore but never applied to the reqwest HTTP client builder. This caused certificate validation to fail with UnknownIssuer errors when connecting to servers with custom CA certificates.
1 parent 477622f commit b4d9c1c

File tree

3 files changed

+26
-96
lines changed

3 files changed

+26
-96
lines changed

.github/workflows/ci.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ jobs:
174174
echo "Checking if TLS port 15671 is listening..."
175175
docker exec rabbitmq-tls rabbitmq-diagnostics listeners | grep -E "15671|ssl" || echo "Note: TLS listener output"
176176
177+
- name: Debug TLS certificates
178+
run: |
179+
echo "=== CA Certificate ==="
180+
openssl x509 -in tests/tls/certs/ca_certificate.pem -noout -subject -issuer
181+
echo "=== Server Certificate ==="
182+
openssl x509 -in tests/tls/certs/server_certificate.pem -noout -subject -issuer
183+
echo "=== Verify server cert against CA ==="
184+
openssl verify -CAfile tests/tls/certs/ca_certificate.pem tests/tls/certs/server_certificate.pem
185+
echo "=== Test TLS connection with curl ==="
186+
curl -v --cacert tests/tls/certs/ca_certificate.pem https://localhost:15671/api/overview -u guest:guest 2>&1 | head -30 || true
187+
echo "=== Certificate file permissions ==="
188+
ls -la tests/tls/certs/
189+
177190
- name: Configure broker
178191
run: |
179192
docker exec rabbitmq-tls rabbitmqctl add_vhost / || true

src/errors.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,6 @@ pub enum CommandRunError {
3939
},
4040
#[error("Run into an I/O error when loading a file: {0}")]
4141
IoError(io::Error),
42-
#[error(
43-
"Local TLS certificate file at {local_path} does not exist, cannot be read or passed as a PEM file: {cause}"
44-
)]
45-
CertificateStoreRejectedCertificate {
46-
local_path: String,
47-
cause: rustls::Error,
48-
},
4942
#[error("TLS certificate file at {local_path} does not exist or is not readable")]
5043
CertificateFileNotFound { local_path: String },
5144
#[error(

src/main.rs

Lines changed: 13 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
use clap::{ArgMatches, crate_name, crate_version};
1919
use errors::CommandRunError;
20-
use reqwest::{Identity, tls::Version as TlsVersion};
20+
use reqwest::{Certificate, Identity, tls::Version as TlsVersion};
2121
use std::path::{Path, PathBuf};
2222
use std::time::Duration;
2323
use std::{fs, process};
@@ -47,12 +47,10 @@ use rabbitmq_http_client::blocking_api::{Client as GenericAPIClient, ClientBuild
4747
use rabbitmq_http_client::commons::PolicyTarget;
4848
use reqwest::blocking::Client as HTTPClient;
4949
use rustls::crypto::CryptoProvider;
50-
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
50+
use rustls::pki_types::PrivateKeyDer;
5151

5252
type APIClient = GenericAPIClient<String, String, String>;
5353

54-
type CertificateChain = Vec<CertificateDer<'static>>;
55-
5654
fn main() {
5755
let pre_flight_settings = if pre_flight::is_non_interactive() {
5856
PreFlightSettings::non_interactive()
@@ -217,58 +215,10 @@ fn build_http_client(
217215
.danger_accept_invalid_certs(disable_peer_verification)
218216
.danger_accept_invalid_hostnames(disable_peer_verification);
219217

220-
// local certificate store
221-
let mut store = rustls::RootCertStore::empty();
222-
223218
if let Some(ca_certs_path) = ca_certs_path_opt {
224-
let ca_certs_path_str = ca_certs_path.to_string_lossy();
225-
226-
// Load CA certificates with improved error handling
227-
let ca_certs = load_certs(&ca_certs_path_str).map_err(|err| {
228-
// Add context about this being a CA certificate bundle
229-
match err {
230-
CommandRunError::CertificateFileNotFound { local_path } => {
231-
CommandRunError::CertificateFileNotFound {
232-
local_path: format!("CA certificate bundle at {}", local_path),
233-
}
234-
}
235-
CommandRunError::CertificateFileEmpty { local_path } => {
236-
CommandRunError::CertificateFileEmpty {
237-
local_path: format!("CA certificate bundle at {}", local_path),
238-
}
239-
}
240-
CommandRunError::CertificateFileInvalidPem {
241-
local_path,
242-
details,
243-
} => CommandRunError::CertificateFileInvalidPem {
244-
local_path: format!("CA certificate bundle at {}", local_path),
245-
details,
246-
},
247-
other => other,
248-
}
249-
})?;
250-
251-
for (index, cert) in ca_certs.into_iter().enumerate() {
252-
store.add(cert).map_err(|err| {
253-
let readable_path = ca_cert_pem_file
254-
.clone()
255-
.unwrap()
256-
.to_string_lossy()
257-
.to_string();
258-
259-
// Provide more context about which certificate failed
260-
let detailed_path = if index == 0 {
261-
readable_path
262-
} else {
263-
format!("{} (certificate #{} in bundle)", readable_path, index + 1)
264-
};
265-
266-
CommandRunError::CertificateStoreRejectedCertificate {
267-
local_path: detailed_path,
268-
cause: err,
269-
}
270-
})?;
271-
}
219+
let ca_certs_path_str = ca_certs_path.to_string_lossy().to_string();
220+
let cert = load_ca_certificate(&ca_certs_path_str)?;
221+
builder = builder.add_root_certificate(cert);
272222
}
273223

274224
// --tls-cert-file, --tls-key-file
@@ -357,45 +307,19 @@ fn validate_certificate_file(path: &str) -> Result<(), CommandRunError> {
357307
Ok(())
358308
}
359309

360-
fn load_certs(filename: &str) -> Result<CertificateChain, CommandRunError> {
310+
fn load_ca_certificate(filename: &str) -> Result<Certificate, CommandRunError> {
361311
validate_certificate_file(filename)?;
362312

363-
let results = CertificateDer::pem_file_iter(filename).map_err(|err| {
364-
let readable_path = filename.to_string();
365-
let details = match err {
366-
rustls::pki_types::pem::Error::NoItemsFound => {
367-
"Invalid PEM format or structure".to_string()
368-
}
369-
rustls::pki_types::pem::Error::IllegalSectionStart { .. } => {
370-
"Invalid PEM format or structure".to_string()
371-
}
372-
rustls::pki_types::pem::Error::MissingSectionEnd { .. } => {
373-
"Invalid PEM format or structure".to_string()
374-
}
375-
_ => format!("Failed to load a PEM file at {}: {}", filename, err),
376-
};
377-
CommandRunError::CertificateFileInvalidPem {
378-
local_path: readable_path,
379-
details,
380-
}
313+
let pem_data = fs::read(filename).map_err(|_| CommandRunError::CertificateFileNotFound {
314+
local_path: filename.to_string(),
381315
})?;
382316

383-
let certs = results
384-
.map(|result| {
385-
result.map_err(|err| CommandRunError::CertificateFileInvalidPem {
386-
local_path: filename.to_string(),
387-
details: format!("Failed to parse certificate: {}", err),
388-
})
389-
})
390-
.collect::<Result<CertificateChain, CommandRunError>>()?;
391-
392-
if certs.is_empty() {
393-
return Err(CommandRunError::CertificateFileEmpty {
317+
Certificate::from_pem(&pem_data).map_err(|err| {
318+
CommandRunError::CertificateFileCouldNotBeLoaded1 {
394319
local_path: filename.to_string(),
395-
});
396-
}
397-
398-
Ok(certs)
320+
cause: err,
321+
}
322+
})
399323
}
400324

401325
#[allow(dead_code)]

0 commit comments

Comments
 (0)