Skip to main content

agp_config/tls/
client.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{path::Path, sync::Arc};
5
6use rustls::{
7    ClientConfig as RustlsClientConfig, DigitallySignedStruct, Error, RootCertStore,
8    SignatureScheme,
9    client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
10    version::{TLS12, TLS13},
11};
12use rustls_pki_types::pem::PemObject;
13use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
14use serde;
15use serde::Deserialize;
16use tracing::warn;
17
18use super::common::{Config, ConfigError, RustlsConfigLoader};
19use crate::component::configuration::{Configuration, ConfigurationError};
20
21#[derive(Debug, Deserialize, PartialEq, Clone)]
22pub struct TlsClientConfig {
23    /// The Config struct
24    #[serde(flatten, default)]
25    pub config: Config,
26
27    /// In gRPC and HTTP when set to true, this is used to disable the client transport security.
28    /// (optional, default false)
29    #[serde(default = "default_insecure")]
30    pub insecure: bool,
31
32    /// InsecureSkipVerify will enable TLS but not verify the server certificate.
33    #[serde(default = "default_insecure_skip_verify")]
34    pub insecure_skip_verify: bool,
35}
36
37impl Default for TlsClientConfig {
38    fn default() -> Self {
39        TlsClientConfig {
40            config: Config::default(),
41            insecure: default_insecure(),
42            insecure_skip_verify: default_insecure_skip_verify(),
43        }
44    }
45}
46
47fn default_insecure() -> bool {
48    false
49}
50
51fn default_insecure_skip_verify() -> bool {
52    false
53}
54
55// Cert verifier that skips verification
56// Implement a no-op verifier if needed for insecure connections
57#[derive(Debug)]
58struct NoVerifier;
59
60impl ServerCertVerifier for NoVerifier {
61    fn verify_server_cert(
62        &self,
63        _: &CertificateDer<'_>,
64        _: &[CertificateDer<'_>],
65        _: &ServerName<'_>,
66        _: &[u8],
67        _: UnixTime,
68    ) -> Result<ServerCertVerified, Error> {
69        warn!("Skipping server cert verification");
70        Ok(ServerCertVerified::assertion())
71    }
72
73    fn verify_tls12_signature(
74        &self,
75        _: &[u8],
76        _: &CertificateDer<'_>,
77        _: &DigitallySignedStruct,
78    ) -> Result<HandshakeSignatureValid, Error> {
79        warn!("Skipping server cert verification - TLS 1.2");
80        Ok(HandshakeSignatureValid::assertion())
81    }
82
83    fn verify_tls13_signature(
84        &self,
85        _: &[u8],
86        _: &CertificateDer<'_>,
87        _: &DigitallySignedStruct,
88    ) -> Result<HandshakeSignatureValid, Error> {
89        warn!("Skipping server cert verification - TLS 1.3");
90        Ok(HandshakeSignatureValid::assertion())
91    }
92
93    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
94        vec![
95            SignatureScheme::RSA_PKCS1_SHA1,
96            SignatureScheme::ECDSA_SHA1_Legacy,
97            SignatureScheme::RSA_PKCS1_SHA256,
98            SignatureScheme::ECDSA_NISTP256_SHA256,
99            SignatureScheme::RSA_PKCS1_SHA384,
100            SignatureScheme::ECDSA_NISTP384_SHA384,
101            SignatureScheme::RSA_PKCS1_SHA512,
102            SignatureScheme::ECDSA_NISTP521_SHA512,
103            SignatureScheme::RSA_PSS_SHA256,
104            SignatureScheme::RSA_PSS_SHA384,
105            SignatureScheme::RSA_PSS_SHA512,
106            SignatureScheme::ED25519,
107            SignatureScheme::ED448,
108        ]
109    }
110}
111
112// methods for ClientConfig to create a RustlsClientConfig from the config
113impl TlsClientConfig {
114    /// Create a new TlsClientConfig
115    pub fn new() -> Self {
116        TlsClientConfig::default()
117    }
118
119    /// Set insecure (disable certificate verification)
120    pub fn with_insecure_skip_verify(self, insecure_skip_verify: bool) -> Self {
121        TlsClientConfig {
122            insecure_skip_verify,
123            ..self
124        }
125    }
126
127    /// Set insecure (disable TLS)
128    pub fn with_insecure(self, insecure: bool) -> Self {
129        TlsClientConfig { insecure, ..self }
130    }
131
132    /// Set the CA file
133    pub fn with_ca_file(self, ca_file: &str) -> Self {
134        TlsClientConfig {
135            config: self.config.with_ca_file(ca_file),
136            ..self
137        }
138    }
139
140    /// Set the CA pem
141    pub fn with_ca_pem(self, ca_pem: &str) -> Self {
142        TlsClientConfig {
143            config: self.config.with_ca_pem(ca_pem),
144            ..self
145        }
146    }
147
148    /// Set if include system CA certs pool
149    pub fn with_include_system_ca_certs_pool(self, include_system_ca_certs_pool: bool) -> Self {
150        TlsClientConfig {
151            config: self
152                .config
153                .with_include_system_ca_certs_pool(include_system_ca_certs_pool),
154            ..self
155        }
156    }
157
158    /// Set the cert file (for client auth)
159    pub fn with_cert_file(self, cert_file: &str) -> Self {
160        TlsClientConfig {
161            config: self.config.with_cert_file(cert_file),
162            ..self
163        }
164    }
165
166    /// Set the cert pem (for client auth)
167    pub fn with_cert_pem(self, cert_pem: &str) -> Self {
168        TlsClientConfig {
169            config: self.config.with_cert_pem(cert_pem),
170            ..self
171        }
172    }
173
174    /// Set the key file (for client auth)
175    pub fn with_key_file(self, key_file: &str) -> Self {
176        TlsClientConfig {
177            config: self.config.with_key_file(key_file),
178            ..self
179        }
180    }
181
182    /// Set the key pem (for client auth)
183    pub fn with_key_pem(self, key_pem: &str) -> Self {
184        TlsClientConfig {
185            config: self.config.with_key_pem(key_pem),
186            ..self
187        }
188    }
189
190    /// Set the TLS version
191    pub fn with_tls_version(self, tls_version: &str) -> Self {
192        TlsClientConfig {
193            config: self.config.with_tls_version(tls_version),
194            ..self
195        }
196    }
197
198    /// Set the reload interval
199    pub fn with_reload_interval(self, reload_interval: Option<std::time::Duration>) -> Self {
200        TlsClientConfig {
201            config: self.config.with_reload_interval(reload_interval),
202            ..self
203        }
204    }
205
206    fn load_rustls_client_config(
207        &self,
208        root_store: RootCertStore,
209    ) -> Result<RustlsClientConfig, ConfigError> {
210        use ConfigError::*;
211
212        // Check TLS version
213        let tls_version = match self.config.tls_version.as_str() {
214            "tls1.2" => &TLS12,
215            "tls1.3" => &TLS13,
216            _ => {
217                // return an error if the tls version is invalid
218                return Err(InvalidTlsVersion(self.config.tls_version.clone()));
219            }
220        };
221
222        // create a client ConfigBuilder
223        let config_builder = RustlsClientConfig::builder_with_protocol_versions(&[tls_version])
224            .with_root_certificates(root_store);
225
226        // Check if enable client auth or not
227        let (cert, key) = match (
228            self.config.has_cert_file() && self.config.has_key_file(),
229            self.config.has_cert_pem() && self.config.has_key_pem(),
230        ) {
231            (true, true) => {
232                // If both cert_file and cert_pem are set, return an error
233                return Err(CannotUseBoth("cert".to_string()));
234            }
235            (false, false) => {
236                // If no client auth, return the config with client auth disabled
237                return Ok(config_builder.with_no_client_auth());
238            }
239            (true, false) => {
240                // If cert_file is set, load the cert and key from the file
241                let cert = CertificateDer::from_pem_file(Path::new(
242                    self.config.cert_file.as_ref().unwrap(),
243                ))
244                .map_err(InvalidPem)?;
245                let key =
246                    PrivateKeyDer::from_pem_file(Path::new(self.config.key_file.as_ref().unwrap()))
247                        .map_err(InvalidPem)?;
248                (cert, key)
249            }
250            (false, true) => {
251                // If cert_pem is set, load the cert and key from the memory
252                let cert = CertificateDer::from_pem_slice(
253                    self.config.cert_pem.as_ref().unwrap().as_bytes(),
254                )
255                .map_err(InvalidPem)?;
256                let key =
257                    PrivateKeyDer::from_pem_slice(self.config.key_pem.as_ref().unwrap().as_bytes())
258                        .map_err(InvalidPem)?;
259                (cert, key)
260            }
261        };
262
263        // Set the client auth cert and key
264        let client_config = config_builder
265            .with_client_auth_cert(vec![cert], key)
266            .map_err(ConfigBuilder)?;
267
268        // We are good to go
269        Ok(client_config)
270    }
271}
272
273// trait implementation
274impl RustlsConfigLoader<RustlsClientConfig> for TlsClientConfig {
275    fn load_rustls_config(&self) -> Result<Option<RustlsClientConfig>, ConfigError> {
276        if self.insecure && !self.config.has_ca() {
277            return Ok(None);
278        }
279
280        let root_store = self.config.load_ca_cert_pool()?;
281        let mut client_config = self.load_rustls_client_config(root_store)?;
282
283        // Set unsecure stuff
284        if self.insecure_skip_verify {
285            client_config
286                .dangerous()
287                .set_certificate_verifier(Arc::new(NoVerifier));
288        }
289
290        Ok(Some(client_config))
291    }
292}
293
294impl Configuration for TlsClientConfig {
295    fn validate(&self) -> Result<(), ConfigurationError> {
296        // TODO(msardara): validate the configuration
297        Ok(())
298    }
299}