Line data Source code
1 : use std::env;
2 : use std::io::Cursor;
3 : use std::path::PathBuf;
4 : use std::sync::Arc;
5 :
6 : use anyhow::{Context, bail};
7 : use rustls::crypto::ring;
8 :
9 : /// We use an internal certificate authority when establishing a TLS connection with compute.
10 0 : fn load_internal_certs(store: &mut rustls::RootCertStore) -> anyhow::Result<()> {
11 0 : let Some(ca_file) = env::var_os("NEON_INTERNAL_CA_FILE") else {
12 0 : return Ok(());
13 : };
14 0 : let ca_file = PathBuf::from(ca_file);
15 :
16 0 : let ca = std::fs::read(&ca_file)
17 0 : .with_context(|| format!("could not read CA from {}", ca_file.display()))?;
18 :
19 0 : for cert in rustls_pemfile::certs(&mut Cursor::new(&*ca)) {
20 0 : store
21 0 : .add(cert.context("could not parse internal CA certificate")?)
22 0 : .context("could not parse internal CA certificate")?;
23 : }
24 :
25 0 : Ok(())
26 0 : }
27 :
28 : /// For console redirect proxy, we need to establish a connection to compute via pg-sni-router.
29 : /// pg-sni-router needs TLS and uses a Let's Encrypt signed certificate, so we
30 : /// load certificates from our native store.
31 0 : fn load_native_certs(store: &mut rustls::RootCertStore) -> anyhow::Result<()> {
32 0 : let der_certs = rustls_native_certs::load_native_certs();
33 0 :
34 0 : if !der_certs.errors.is_empty() {
35 0 : bail!("could not parse certificates: {:?}", der_certs.errors);
36 0 : }
37 0 :
38 0 : store.add_parsable_certificates(der_certs.certs);
39 0 :
40 0 : Ok(())
41 0 : }
42 :
43 0 : fn load_compute_certs() -> anyhow::Result<Arc<rustls::RootCertStore>> {
44 0 : let mut store = rustls::RootCertStore::empty();
45 0 : load_native_certs(&mut store)?;
46 0 : load_internal_certs(&mut store)?;
47 0 : Ok(Arc::new(store))
48 0 : }
49 :
50 : /// Loads the root certificates and constructs a client config suitable for connecting to the neon compute.
51 : /// This function is blocking.
52 0 : pub fn compute_client_config_with_root_certs() -> anyhow::Result<rustls::ClientConfig> {
53 0 : Ok(
54 0 : rustls::ClientConfig::builder_with_provider(Arc::new(ring::default_provider()))
55 0 : .with_safe_default_protocol_versions()
56 0 : .expect("ring should support the default protocol versions")
57 0 : .with_root_certificates(load_compute_certs()?)
58 0 : .with_no_client_auth(),
59 : )
60 0 : }
61 :
62 : #[cfg(test)]
63 28 : pub fn compute_client_config_with_certs(
64 28 : certs: impl IntoIterator<Item = rustls::pki_types::CertificateDer<'static>>,
65 28 : ) -> rustls::ClientConfig {
66 28 : let mut store = rustls::RootCertStore::empty();
67 28 : store.add_parsable_certificates(certs);
68 28 :
69 28 : rustls::ClientConfig::builder_with_provider(Arc::new(ring::default_provider()))
70 28 : .with_safe_default_protocol_versions()
71 28 : .expect("ring should support the default protocol versions")
72 28 : .with_root_certificates(store)
73 28 : .with_no_client_auth()
74 28 : }
|