Skip to content

Commit 631210e

Browse files
committed
Add mTLS support for native-tls
1 parent 040a05e commit 631210e

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

.github/workflows/test.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ jobs:
4242
- tcp://localhost:9440?skip_verify=true
4343
# we don't need skip_verify when we pass CA cert
4444
- tcp://localhost:9440?ca_certificate=/tmp/ca.pem
45-
# auth via mTLS
46-
- tcp://tls@localhost:9440?ca_certificate=/tmp/ca.pem&certificate_file=/tmp/client.crt&private_key_file=/tmp/client.key
45+
# mTLS
46+
include:
47+
- feature: tls-native-tls
48+
database_url: tcp://tls@localhost:9440?ca_certificate=/tmp/ca.pem&client_tls_identity=/tmp/client.p12
49+
- feature: tls-rustls
50+
database_url: tcp://tls@localhost:9440?ca_certificate=/tmp/ca.pem&certificate_file=/tmp/client.crt&private_key_file=/tmp/client.key
4751
runs-on: ubuntu-latest
4852
env:
4953
# NOTE: not all tests "secure" aware, so let's define DATABASE_URL explicitly
@@ -54,11 +58,12 @@ jobs:
5458
CH_SSL_PRIVATE_KEY: /tmp/server.key
5559
CH_SSL_CLIENT_PRIVATE_KEY: /tmp/client.key
5660
CH_SSL_CLIENT_CERTIFICATE: /tmp/client.crt
61+
CH_SSL_CLIENT_P12: /tmp/client.p12
5762
steps:
5863
- uses: actions/checkout@v3
5964
- name: Generate TLS certificates
6065
run: |
61-
extras/ci/generate_certs.sh "$CH_SSL_CA_CERTIFICATE" "$CH_SSL_CERTIFICATE" "$CH_SSL_PRIVATE_KEY" "$CH_SSL_CLIENT_CERTIFICATE" "$CH_SSL_CLIENT_PRIVATE_KEY"
66+
extras/ci/generate_certs.sh "$CH_SSL_CA_CERTIFICATE" "$CH_SSL_CERTIFICATE" "$CH_SSL_PRIVATE_KEY" "$CH_SSL_CLIENT_CERTIFICATE" "$CH_SSL_CLIENT_PRIVATE_KEY" "$CH_SSL_CLIENT_P12"
6267
# server needs access to those
6368
chmod 644 "$CH_SSL_CERTIFICATE" "$CH_SSL_PRIVATE_KEY" "$CH_SSL_CA_CERTIFICATE"
6469
# NOTE:

extras/ci/generate_certs.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ key=${1-:"$CH_SSL_PRIVATE_KEY"} && shift
66

77
client_crt=${1-:"$CH_SSL_CLIENT_CERTIFICATE"} && shift
88
client_key=${1-:"$CH_SSL_CLIENT_PRIVATE_KEY"} && shift
9+
client_p12=${1-:"$CH_SSL_CLIENT_P12"} && shift
910

1011
ca_key=${ca_crt/.pem/.key}
1112
csr=${key/.key/.csr}
@@ -47,3 +48,5 @@ openssl genrsa -out "$client_key" 2048
4748
openssl req -new -key "$client_key" -out "$client_csr" -subj "/C=US/ST=DevState/O=DevOrg/CN=MyClient"
4849
openssl x509 -req -in "$client_csr" -CA "$ca_crt" -CAkey "$ca_key" -CAcreateserial -out "$client_crt" -days 3650 -sha256 -extfile "$client_ext"
4950
openssl verify -CAfile "$ca_crt" "$client_crt"
51+
52+
openssl pkcs12 -export -inkey "$client_key" -in "$client_crt" -certfile "$ca_crt" -out "$client_p12" -passout pass:

src/connecting_stream.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl State {
104104
State::Tcp(TcpState::Fail(Some(conn_error)))
105105
}
106106

107-
#[cfg(feature = "_tls")]
107+
#[cfg(feature = "tls-rustls")]
108108
fn tls_err(e: TlsError) -> Self {
109109
State::Tls(TlsState::Fail(Some(ConnectionError::TlsError(e))))
110110
}
@@ -240,6 +240,9 @@ impl ConnectingStream {
240240
let native_cert = native_tls::Certificate::from(certificate);
241241
builder.add_root_certificate(native_cert);
242242
}
243+
if let Some(identity) = options.tls_identity.clone() {
244+
builder.identity(identity.into());
245+
}
243246

244247
Self {
245248
state: State::tls_wait(Box::pin(async move {

src/types/options.rs

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,27 +197,59 @@ impl From<PrivateKey> for rustls::pki_types::PrivateKeyDer<'static> {
197197
rustls::pki_types::PrivateKeyDer::clone_key(value.0.as_ref())
198198
}
199199
}
200-
201-
#[cfg(feature = "_tls")]
200+
#[cfg(feature = "tls-rustls")]
202201
impl fmt::Debug for PrivateKey {
203202
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204203
write!(f, "[Private Key]")
205204
}
206205
}
207-
208-
#[cfg(feature = "_tls")]
206+
#[cfg(feature = "tls-rustls")]
209207
impl PartialEq for PrivateKey {
210208
fn eq(&self, _other: &Self) -> bool {
211209
true
212210
}
213211
}
214-
215-
#[cfg(feature = "_tls")]
212+
#[cfg(feature = "tls-rustls")]
216213
pub fn load_private_key(file: &str) -> Result<PrivateKey> {
217214
let data = fs::read(file)?;
218215
PrivateKey::from_pem(&data)
219216
}
220217

218+
/// Identity for mTLS for native-tls
219+
#[cfg(feature = "tls-native-tls")]
220+
#[derive(Clone)]
221+
pub struct TlsIdentity(Arc<native_tls::Identity>);
222+
#[cfg(feature = "tls-native-tls")]
223+
impl TlsIdentity {
224+
pub fn from_pkcs12_der(der: &[u8]) -> Result<Self> {
225+
// FIXME: support password
226+
Ok(Self(Arc::new(native_tls::Identity::from_pkcs12(der, "").map_err(|_| UrlError::Invalid)?)))
227+
}
228+
}
229+
#[cfg(feature = "tls-native-tls")]
230+
impl From<TlsIdentity> for native_tls::Identity {
231+
fn from(value: TlsIdentity) -> Self {
232+
value.0.as_ref().clone()
233+
}
234+
}
235+
#[cfg(feature = "tls-native-tls")]
236+
impl fmt::Debug for TlsIdentity {
237+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238+
write!(f, "[Private Key]")
239+
}
240+
}
241+
#[cfg(feature = "tls-native-tls")]
242+
impl PartialEq for TlsIdentity {
243+
fn eq(&self, _other: &Self) -> bool {
244+
true
245+
}
246+
}
247+
#[cfg(feature = "tls-native-tls")]
248+
pub fn load_tls_identity(file: &str) -> Result<TlsIdentity> {
249+
let data = fs::read(file)?;
250+
TlsIdentity::from_pkcs12_der(&data)
251+
}
252+
221253
#[derive(Clone, PartialEq, Debug)]
222254
pub enum SettingType {
223255
String(String),
@@ -348,8 +380,11 @@ pub struct Options {
348380
pub(crate) certificate_file: Option<Certificate>,
349381

350382
/// Private key for certificate.
351-
#[cfg(feature = "_tls")]
383+
/// TODO: merge private_key_file + certificate_file into client_tls_identity
384+
#[cfg(feature = "tls-rustls")]
352385
pub(crate) private_key_file: Option<PrivateKey>,
386+
#[cfg(feature = "tls-native-tls")]
387+
pub(crate) tls_identity: Option<TlsIdentity>,
353388

354389
/// Query settings
355390
pub(crate) settings: HashMap<String, SettingValue>,
@@ -408,8 +443,10 @@ impl Default for Options {
408443
ca_certificate: None,
409444
#[cfg(feature = "_tls")]
410445
certificate_file: None,
411-
#[cfg(feature = "_tls")]
446+
#[cfg(feature = "tls-rustls")]
412447
private_key_file: None,
448+
#[cfg(feature = "tls-native-tls")]
449+
tls_identity: None,
413450
settings: HashMap::new(),
414451
alt_hosts: Vec::new(),
415452
}
@@ -570,12 +607,18 @@ impl Options {
570607
=> certificate_file: Option<Certificate>
571608
}
572609

573-
#[cfg(feature = "_tls")]
610+
#[cfg(feature = "tls-rustls")]
574611
property! {
575612
/// Private key for certificate.
576613
=> private_key_file: Option<PrivateKey>
577614
}
578615

616+
#[cfg(feature = "tls-native-tls")]
617+
property! {
618+
/// Identity for mTLS.
619+
=> tls_identity: Option<TlsIdentity>
620+
}
621+
579622
property! {
580623
/// Query settings
581624
=> settings: HashMap<String, SettingValue>
@@ -671,8 +714,10 @@ where
671714
"ca_certificate" => options.ca_certificate = Some(parse_param(key, value, load_certificate)?),
672715
#[cfg(feature = "_tls")]
673716
"certificate_file" => options.certificate_file = Some(parse_param(key, value, load_certificate)?),
674-
#[cfg(feature = "_tls")]
717+
#[cfg(feature = "tls-rustls")]
675718
"private_key_file" => options.private_key_file = Some(parse_param(key, value, load_private_key)?),
719+
#[cfg(feature = "tls-native-tls")]
720+
"tls_identity" => options.tls_identity = Some(parse_param(key, value, load_tls_identity)?),
676721
"alt_hosts" => options.alt_hosts = parse_param(key, value, parse_hosts)?,
677722
_ => {
678723
let value = SettingType::String(value.to_string());

0 commit comments

Comments
 (0)