-
-
Notifications
You must be signed in to change notification settings - Fork 606
/
tls.rs
139 lines (122 loc) · 4.13 KB
/
tls.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
use std::sync::Arc;
use rustls::{ClientConfig, RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError};
use webpki::DNSNameRef;
#[derive(Default)]
pub struct Config {
pub root_ca_certs: Option<Vec<u8>>,
pub mtls: Option<MtlsConfig>,
pub certificate_check: CertificateCheck,
}
impl Config {
pub fn new_without_mtls(root_ca_certs: Option<Vec<u8>>) -> Self {
Self {
root_ca_certs,
mtls: None,
certificate_check: CertificateCheck::Enabled,
}
}
}
impl TryFrom<Config> for ClientConfig {
type Error = String;
/// Create a rust-tls `ClientConfig` from root CA certs, falling back to the rust-tls-native-certs
/// crate if specific root CA certs were not given.
fn try_from(config: Config) -> Result<Self, Self::Error> {
let mut tls_config = ClientConfig::new();
// Must set HTTP/2 as ALPN protocol otherwise cannot connect over TLS to gRPC servers.
// Unfortunately, this is not a default value and, moreover, Tonic does not provide
// any helper function to encapsulate this knowledge.
tls_config.set_protocols(&[Vec::from("h2")]);
// Add the root store.
match config.root_ca_certs {
Some(pem_bytes) => {
let mut reader = std::io::Cursor::new(pem_bytes);
tls_config
.root_store
.add_pem_file(&mut reader)
.map_err(|_| {
"Unexpected state when adding PEM file from `--remote-ca-certs-path`. Please \
check that it points to a valid file."
.to_owned()
})?;
}
None => {
tls_config.root_store =
rustls_native_certs::load_native_certs().map_err(|(_maybe_store, e)| {
format!(
"Could not discover root CA cert files to use TLS with remote caching and remote \
execution. Consider setting `--remote-ca-certs-path` instead to explicitly point to \
the correct PEM file.\n\n{}",
e
)
})?;
}
}
if let Some(MtlsConfig { key, cert_chain }) = config.mtls {
tls_config
.set_single_client_cert(
cert_chain_from_pem_bytes(cert_chain)?,
der_key_from_pem_bytes(key)?,
)
.map_err(|err| format!("Error creating MTLS config: {:?}", err))?;
}
if let CertificateCheck::DangerouslyDisabled = config.certificate_check {
tls_config
.dangerous()
.set_certificate_verifier(Arc::new(NoVerifier));
}
Ok(tls_config)
}
}
pub struct MtlsConfig {
/// PEM bytes of the private key used for MTLS.
pub key: Vec<u8>,
/// PEM bytes of the certificate used for MTLS.
pub cert_chain: Vec<u8>,
}
pub enum CertificateCheck {
Enabled,
DangerouslyDisabled,
}
impl Default for CertificateCheck {
fn default() -> Self {
Self::Enabled
}
}
struct NoVerifier;
impl ServerCertVerifier for NoVerifier {
fn verify_server_cert(
&self,
_roots: &RootCertStore,
_presented_certs: &[rustls::Certificate],
_dns_name: DNSNameRef,
_ocsp_response: &[u8],
) -> Result<ServerCertVerified, TLSError> {
Ok(ServerCertVerified::assertion())
}
}
fn cert_chain_from_pem_bytes(cert_chain: Vec<u8>) -> Result<Vec<rustls::Certificate>, String> {
rustls_pemfile::certs(&mut cert_chain.as_slice())
.and_then(|certs| {
certs
.into_iter()
.map(|cert| Ok(rustls::Certificate(cert)))
.collect::<Result<Vec<_>, _>>()
})
.map_err(|err| format!("Failed to parse certificates from PEM file {:?}", err))
}
fn der_key_from_pem_bytes(pem_bytes: Vec<u8>) -> Result<rustls::PrivateKey, String> {
let key = rustls_pemfile::read_one(&mut pem_bytes.as_slice())
.map_err(|err| format!("Failed to read PEM file: {:?}", err))?
.ok_or_else(|| "No private key found in PEM file".to_owned())?;
use rustls_pemfile::Item;
let key = match key {
Item::RSAKey(bytes) => bytes,
Item::PKCS8Key(bytes) => bytes,
Item::X509Certificate(_) => {
return Err("Found certificate in PEM file but expected private key".to_owned())
}
};
Ok(rustls::PrivateKey(key))
}