diff --git a/pgdog/src/net/tls.rs b/pgdog/src/net/tls.rs index ee5f0ecc3..4976ec52d 100644 --- a/pgdog/src/net/tls.rs +++ b/pgdog/src/net/tls.rs @@ -140,7 +140,29 @@ pub fn reload() -> Result<(), Error> { } fn build_acceptor(cert: &Path, key: &Path, client_ca: Option<&Path>) -> Result { - let pem = CertificateDer::from_pem_file(cert)?; + // Read the whole PEM file so intermediate certificates in a chain are kept. + let certs = CertificateDer::pem_file_iter(cert) + .map_err(|e| { + Error::Io(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Failed to read TLS certificate file: {}", e), + )) + })? + .collect::, _>>() + .map_err(|e| { + Error::Io(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Failed to parse TLS certificates: {}", e), + )) + })?; + + if certs.is_empty() { + return Err(Error::Io(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "No valid certificates found in TLS certificate file", + ))); + } + let key = PrivateKeyDer::from_pem_file(key)?; let builder = rustls::ServerConfig::builder(); @@ -151,7 +173,7 @@ fn build_acceptor(cert: &Path, key: &Path, client_ca: Option<&Path>) -> Result builder.with_no_client_auth(), } - .with_single_cert(vec![pem], key)?; + .with_single_cert(certs, key)?; ACCEPTOR_BUILD_COUNT.fetch_add(1, Ordering::SeqCst); @@ -552,6 +574,29 @@ mod tests { crate::config::set(crate::config::ConfigAndUsers::default()).unwrap(); } + #[test] + fn acceptor_loads_certificate_chain() { + crate::logger(); + + super::test_reset_acceptor(); + + let cert = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tls/chain_cert.pem"); + let key = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tls/chain_key.pem"); + + let mut cfg = crate::config::ConfigAndUsers::default(); + cfg.config.general.tls_certificate = Some(cert); + cfg.config.general.tls_private_key = Some(key); + + crate::config::set(cfg).unwrap(); + super::reload().expect("acceptor builds from a certificate chain"); + + super::acceptor().expect("acceptor installed"); + assert_eq!(super::test_acceptor_build_count(), 1); + + super::test_reset_acceptor(); + crate::config::set(crate::config::ConfigAndUsers::default()).unwrap(); + } + #[tokio::test] async fn test_connector_with_verify_mode() { crate::logger(); diff --git a/pgdog/tests/tls/chain_cert.pem b/pgdog/tests/tls/chain_cert.pem new file mode 100644 index 000000000..354c5e0d6 --- /dev/null +++ b/pgdog/tests/tls/chain_cert.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIUXx9X2TQwMbkvDHSWEf93yHbip6kwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNcGdkb2ctdGVzdC1jYTAeFw0yNjA1MTkwNjM3MzlaFw0z +NjA1MTYwNjM3MzlaMBoxGDAWBgNVBAMMD3BnZG9nLXRlc3QtbGVhZjCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKCFKDDOQ/0nlvLf+EEbc/yrOKIp2QJS +aA9WMOcUvLJCWafyu7f+GJKowt5kyUrT1RfyU2efsOg50fkKC/0u/9unQFdHnvPT +81IntgpdBOSAo/sRi4mBMoTWfy1dSYNfNTY7JCKBZOidngyXQbMXermad83oJ0rj +kzODJ4yVgFL91Gee5fAht1mo1HixK6pAXHBpVoDsxJeBu2x7WPm543zyBCECSrCh +AH7y1a2Yd3xGfccPz/6CHk5GfZQvMDI2MyO+Jc7nE5oeD859xfMKYY27htiOJi+j +vvjhciMz9cVSa7+LlsAp1RNbUFx5wKOJvpLMAKK8Sbyxs/2aaIU+gQcCAwEAAaNC +MEAwHQYDVR0OBBYEFCPKckmaYRL3Ix0b0M61TLuUw7nVMB8GA1UdIwQYMBaAFL72 +gOQr8LORRZ+eu+QZHjwYFkp4MA0GCSqGSIb3DQEBCwUAA4IBAQBcsUpLr3XNsjjy +nKTAh07hYQQxfMvx0cj16FSdEDBIqctz40IQIQHM9rCq/O/SQ5TKoNjmva01KOub +IoxPszmzPLTupFUbVJT8b5v1rUBiApeCxfcnysTLGjrgRoe15kr31O2AwhQyZRLC +OHE8EovZsKPi6KZ9Fhuvtra3xIYwWavRsGKELi6a1IRoU2rqv9tKTNXKkxNBXtdN +vnISbqLAjLgDUZXu2BzjvqOSvw88hOCgByJOTRSeFaI0s+6Ao/qJoTmNU/zux6eY +Ca9Fd48RPNd0yqzCckGBaSeOHbKAxEUCc6r/QvNRzHIC92cgC3fxFFu4y/cfCrCD +WUpGkbRg +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIUdHo1CzFySpqL5/K5LAY3IdX60pkwDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNcGdkb2ctdGVzdC1jYTAeFw0yNjA1MTkwNjM3MzlaFw0z +NjA1MTYwNjM3MzlaMBgxFjAUBgNVBAMMDXBnZG9nLXRlc3QtY2EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNf+6NF6fQoX7QxKTnz50ZZXpYRyWMowpd +73T5ks+DKVC0oRZ+Tvm8xEPvO+f3mhDyYGQV2GB/2722yA57N39yKUQ5UkqjM/LJ +92nC1QHkaDB/QhUTXvN+o0KwobBk0QVgyGBBXbDrF2Rv9fUheGlifl8Qn61MDh5+ +h6JjNOIwXW3WUdBig5dwRWmQ85/TVgkIEGKuTnpc+UaVD8CN3exCurKSZxGpr5p6 +U34kZGmLzkODKimSft5R2cgqsUFJa1COM2AUIgV7uTe9Fi7O9pn88GJDvwu1Q+b5 +2IufeUU1HaQ+Qt3G9WevgTlbq+xCHkK/FtlY4AcNKuR9oPp+c6yDAgMBAAGjUzBR +MB0GA1UdDgQWBBS+9oDkK/CzkUWfnrvkGR48GBZKeDAfBgNVHSMEGDAWgBS+9oDk +K/CzkUWfnrvkGR48GBZKeDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQBwP2GM2ZRjQSkaGT/Q6UtVzb4fmN7dtkPgu6Nbccf6HjToEzwJ0q4a2BxK +Mj9EXkzkRn/L5Tax5JRNjJJPFtGUuiiKwYX7WMyzcg3lNXKKqO6MN8qPMN8JHg+D +DAYRrzOWpHIhi17NYCAMZXpG8eilC9W7WR3X68eSWMAi36YH4GW29yXI+fvxgRcc +NgvBlvljSK1XP18pTxEq9n2fS5qjhNXdF9FX+HvWcWDj2T4EQJhml76W0pz0/FzW +28lFL4O0TG6Qs54SQKdLjU0ajgN4otJGze7xL7Nl8zULxgdoQWTDmN91fL1vOkc1 +/wywO3ZTpK/M6ggHeZSsq42X7a2L +-----END CERTIFICATE----- diff --git a/pgdog/tests/tls/chain_key.pem b/pgdog/tests/tls/chain_key.pem new file mode 100644 index 000000000..fb5365ae3 --- /dev/null +++ b/pgdog/tests/tls/chain_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCghSgwzkP9J5by +3/hBG3P8qziiKdkCUmgPVjDnFLyyQlmn8ru3/hiSqMLeZMlK09UX8lNnn7DoOdH5 +Cgv9Lv/bp0BXR57z0/NSJ7YKXQTkgKP7EYuJgTKE1n8tXUmDXzU2OyQigWTonZ4M +l0GzF3q5mnfN6CdK45MzgyeMlYBS/dRnnuXwIbdZqNR4sSuqQFxwaVaA7MSXgbts +e1j5ueN88gQhAkqwoQB+8tWtmHd8Rn3HD8/+gh5ORn2ULzAyNjMjviXO5xOaHg/O +fcXzCmGNu4bYjiYvo7744XIjM/XFUmu/i5bAKdUTW1BcecCjib6SzACivEm8sbP9 +mmiFPoEHAgMBAAECggEABNQsotc5pV5xBIpnSfIc+Z9e+0XeANtgxQFXRERT7trf +HpXHPuA2dgb8obsPcSBRO0CvcKEVrc67ec3rEWk+CioMXfPKHNSl77VBHSuyL3uN +M0rSU0cfMW7QcZlqUjjlfWWnDyCsW/EN5ZwRPmV47Cdw7jFLKU4SSgzqfiirLRPo +gBcjHYRoCerRekZAONIQUKDHoJCX2AnOhRN6tfsNEwKBaWwfPV/WQLpwRKTSevkL +SC3mcXhTfjuo1v22tsE+b0u4uGX0nvhaYTpj42/dpOyMUh/HZR+d3cV3TU7kB/rh +SCk8nYDzt68iOYVJWTaXu0rJxj18o0LUNFbQbQjSpQKBgQDTx+SERxaeaSSxzVF/ +7sujmebKgHiq8YOUC/SPBdi2R+nhm48mLtzRAt1/dMnExpCIqwTViTS7ayRo8yUZ +dZtZl/pst6gAw+CykpWtKk0KAYv5CyZ1DCao980kTRdmLk4/laYpPj+o2hFayV4P +1Uaim81+4IzNnuOwoPMjV3DI8wKBgQDCCUdOrp/E0cKbaxFvu2s5sIQzqKVj599O +isr87JLvQrh7WSqZoeT0yKuS+y6C85TQAh0018JBo0q224exQ0Pj7cgBiCDhgN3Q +Vfiz2RFYOWGUVmJdl7QDlHszcO7f2q4MNxLW6i0EV5A4XNRKOVTXSfdKoiS5HSjp +sdUz7bCsnQKBgE2THQuvFoP0M7LFaKMaD5Hf4AAxSwIi8XWbY3u4QoeSlANF/guY +gt401HnJh9cCfRLywhSN67jzKCUhW2eWAs+wsdR8+Zc+KOKAez8xDYMqyeDxyqZ5 +Svf9vTEc5ANT1unHoVPeL2dkaZL9y+y0zOjndUCSdEElgHXEnwtFDlUtAoGBAL4B +uAIkne9uONNYlUqEjQuA/vRXQR4EAXg4EW57e6cv/tdLCZxIHJxXpSvZLEKvpBnM ++Cy221LLRG6rqsS6ydB5GFHnq8snFg7dIR18gq+SASvxK4Ha/DxFOh+1hzVOPwFX +LOunhU2rMV8ACCJm4d2wYKiE/tEG7PlrWwvuxgNBAoGAN5GHHnCyEYEj2Gho7kJE +T6ulCz/TfHf0aSDmA5q5m1GHcVdFAfQpdz/MGvTZlbLgZTMvH4AnuwQSJUI6WBp2 +DG4Po3qcpFjmvyAVVgGyox6Ei64MGNg/BCn4DosFYmyQ1QbjHGbkX1iqUCPwFMI6 +DI9OcpWlvWncmuezF9PEehw= +-----END PRIVATE KEY-----