forked from kube-rs/kube
/
tls.rs
164 lines (139 loc) · 5.21 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use crate::Result;
use super::Config;
impl Config {
/// Create `native_tls::TlsConnector`
#[cfg(feature = "native-tls")]
pub fn native_tls_connector(&self) -> Result<tokio_native_tls::native_tls::TlsConnector> {
self::native_tls::native_tls_connector(
self.identity_pem.as_ref(),
self.root_cert.as_ref(),
self.accept_invalid_certs,
)
}
/// Create `rustls::ClientConfig`
#[cfg(feature = "rustls-tls")]
pub fn rustls_tls_client_config(&self) -> Result<rustls::ClientConfig> {
self::rustls_tls::rustls_client_config(
self.identity_pem.as_ref(),
self.root_cert.as_ref(),
self.accept_invalid_certs,
)
}
}
#[cfg(feature = "native-tls")]
mod native_tls {
use tokio_native_tls::native_tls::{Certificate, Identity, TlsConnector};
use crate::{Error, Result};
const IDENTITY_PASSWORD: &str = " ";
/// Create `native_tls::TlsConnector`.
pub fn native_tls_connector(
identity_pem: Option<&Vec<u8>>,
root_cert: Option<&Vec<Vec<u8>>>,
accept_invalid: bool,
) -> Result<TlsConnector> {
let mut builder = TlsConnector::builder();
if let Some(pem) = identity_pem {
let identity = pkcs12_from_pem(pem, IDENTITY_PASSWORD)?;
builder.identity(
Identity::from_pkcs12(&identity, IDENTITY_PASSWORD)
.map_err(|e| Error::SslError(format!("{}", e)))?,
);
}
if let Some(ders) = root_cert {
for der in ders {
builder.add_root_certificate(
Certificate::from_der(&der).map_err(|e| Error::SslError(format!("{}", e)))?,
);
}
}
if accept_invalid {
builder.danger_accept_invalid_certs(true);
}
let connector = builder.build().map_err(|e| Error::SslError(format!("{}", e)))?;
Ok(connector)
}
// TODO Replace this with pure Rust implementation to avoid depending on openssl on macOS and Win
fn pkcs12_from_pem(pem: &[u8], password: &str) -> Result<Vec<u8>> {
use openssl::{pkcs12::Pkcs12, pkey::PKey, x509::X509};
let x509 = X509::from_pem(&pem)?;
let pkey = PKey::private_key_from_pem(&pem)?;
let p12 = Pkcs12::builder().build(password, "kubeconfig", &pkey, &x509)?;
let der = p12.to_der()?;
Ok(der)
}
}
#[cfg(feature = "rustls-tls")]
mod rustls_tls {
use std::sync::Arc;
use tokio_rustls::{
rustls::{self, Certificate, ClientConfig, ServerCertVerified, ServerCertVerifier},
webpki::DNSNameRef,
};
use crate::{Error, Result};
/// Create `rustls::ClientConfig`.
pub fn rustls_client_config(
identity_pem: Option<&Vec<u8>>,
root_cert: Option<&Vec<Vec<u8>>>,
accept_invalid: bool,
) -> Result<ClientConfig> {
use rustls::internal::pemfile;
use std::io::Cursor;
// Based on code from `reqwest`
let mut client_config = ClientConfig::new();
if let Some(buf) = identity_pem {
let (key, certs) = {
let mut pem = Cursor::new(buf);
let certs = pemfile::certs(&mut pem)
.map_err(|_| Error::SslError("No valid certificate was found".into()))?;
pem.set_position(0);
let mut sk = pemfile::pkcs8_private_keys(&mut pem)
.and_then(|pkcs8_keys| {
if pkcs8_keys.is_empty() {
Err(())
} else {
Ok(pkcs8_keys)
}
})
.or_else(|_| {
pem.set_position(0);
pemfile::rsa_private_keys(&mut pem)
})
.map_err(|_| Error::SslError("No valid private key was found".into()))?;
if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
(sk, certs)
} else {
return Err(Error::SslError("private key or certificate not found".into()));
}
};
client_config
.set_single_client_cert(certs, key)
.map_err(|e| Error::SslError(format!("{}", e)))?;
}
if let Some(ders) = root_cert {
for der in ders {
client_config
.root_store
.add(&Certificate(der.to_owned()))
.map_err(|e| Error::SslError(format!("{}", e)))?;
}
}
if accept_invalid {
client_config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
}
Ok(client_config)
}
struct NoCertificateVerification {}
impl ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_roots: &rustls::RootCertStore,
_presented_certs: &[rustls::Certificate],
_dns_name: DNSNameRef<'_>,
_ocsp: &[u8],
) -> Result<ServerCertVerified, rustls::TLSError> {
Ok(ServerCertVerified::assertion())
}
}
}