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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
use serde::{Deserialize, Serialize};
/// Transport configuration for a connector instance.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "mode", rename_all = "snake_case")]
pub enum TransportConfig {
/// Use TLS transport.
///
/// Set `ca_cert_path` to verify the server against a custom CA bundle;
/// set `client_cert_path` + `client_key_path` to enable mutual TLS (mTLS).
Tls {
/// Optional PEM-encoded CA certificate file used to verify the server.
/// When absent the system trust store is used.
ca_cert_path: Option<String>,
/// Optional PEM-encoded client certificate file for mTLS authentication.
/// Must be paired with `client_key_path`.
client_cert_path: Option<String>,
/// Optional PEM-encoded client private key file for mTLS authentication.
/// Must be paired with `client_cert_path`.
client_key_path: Option<String>,
/// When true, accept invalid or unknown CA certificates.
///
/// This is intended only for local testing or tightly controlled
/// private environments where certificate distribution is not practical.
#[serde(default)]
allow_invalid_certificates: bool,
/// When true, skip TLS hostname verification.
///
/// This is intended only for local testing or tightly controlled
/// private environments where DNS/SAN validation is not practical.
#[serde(default)]
allow_invalid_hostnames: bool,
},
/// Use plaintext (unencrypted) transport.
///
/// # Security Warning
///
/// Plaintext transport transmits credentials and data in the clear.
/// Only use this for localhost or fully-trusted private-network deployments
/// (e.g., VPC-internal clusters) where all traffic is already isolated.
/// Do **not** use plaintext over public or shared networks.
Plaintext,
}
impl Default for TransportConfig {
fn default() -> Self {
Self::tls()
}
}
impl TransportConfig {
/// Construct a TLS transport configuration using the system trust store
/// and no client certificate (server-auth-only TLS).
pub fn tls() -> Self {
Self::Tls {
ca_cert_path: None,
client_cert_path: None,
client_key_path: None,
allow_invalid_certificates: false,
allow_invalid_hostnames: false,
}
}
/// Construct a TLS transport configuration with an optional CA bundle.
pub fn tls_with_ca_cert_path(ca_cert_path: Option<String>) -> Self {
Self::Tls {
ca_cert_path,
client_cert_path: None,
client_key_path: None,
allow_invalid_certificates: false,
allow_invalid_hostnames: false,
}
}
/// Construct a mutual TLS (mTLS) configuration.
///
/// Both `client_cert_path` and `client_key_path` are required.
/// `ca_cert_path` is optional and falls back to the system trust store.
pub fn mtls(
ca_cert_path: Option<String>,
client_cert_path: String,
client_key_path: String,
) -> Self {
Self::Tls {
ca_cert_path,
client_cert_path: Some(client_cert_path),
client_key_path: Some(client_key_path),
allow_invalid_certificates: false,
allow_invalid_hostnames: false,
}
}
/// Construct TLS transport that skips certificate and hostname validation.
///
/// Use only for local testing or tightly controlled private environments.
pub fn tls_insecure_skip_verify() -> Self {
Self::Tls {
ca_cert_path: None,
client_cert_path: None,
client_key_path: None,
allow_invalid_certificates: true,
allow_invalid_hostnames: true,
}
}
/// Construct a plaintext (unencrypted) transport configuration.
///
/// See the [`TransportConfig::Plaintext`] variant for security guidance.
pub const fn plaintext() -> Self {
Self::Plaintext
}
/// Return true when TLS transport is configured.
pub const fn is_tls(&self) -> bool {
matches!(self, Self::Tls { .. })
}
/// Return true when mutual TLS (mTLS) is configured (client cert + key both set).
pub fn is_mtls(&self) -> bool {
matches!(
self,
Self::Tls {
client_cert_path: Some(_),
client_key_path: Some(_),
..
}
)
}
/// Return the configured CA bundle path, if any.
pub fn ca_cert_path(&self) -> Option<&str> {
match self {
Self::Tls {
ca_cert_path: Some(path),
..
} => Some(path.as_str()),
_ => None,
}
}
/// Return true when TLS certificate verification is disabled.
pub const fn allow_invalid_certificates(&self) -> bool {
match self {
Self::Tls {
allow_invalid_certificates,
..
} => *allow_invalid_certificates,
Self::Plaintext => false,
}
}
/// Return true when TLS hostname verification is disabled.
pub const fn allow_invalid_hostnames(&self) -> bool {
match self {
Self::Tls {
allow_invalid_hostnames,
..
} => *allow_invalid_hostnames,
Self::Plaintext => false,
}
}
/// Return the configured client certificate path, if any.
pub fn client_cert_path(&self) -> Option<&str> {
match self {
Self::Tls {
client_cert_path: Some(path),
..
} => Some(path.as_str()),
_ => None,
}
}
/// Return the configured client private key path, if any.
pub fn client_key_path(&self) -> Option<&str> {
match self {
Self::Tls {
client_key_path: Some(path),
..
} => Some(path.as_str()),
_ => None,
}
}
/// Emit `tracing::warn!` events for any insecure TLS flags.
///
/// Call this once per connection attempt. When `allow_invalid_certificates`
/// or `allow_invalid_hostnames` is set, a structured warning is emitted so
/// that log-aggregation pipelines and alerting rules can detect accidental
/// production use of insecure TLS configuration.
pub fn warn_if_insecure(&self, source_label: &str) {
if self.allow_invalid_certificates() {
tracing::warn!(
target: "rustcdc::transport::security",
source = source_label,
flag = "allow_invalid_certificates",
"TLS certificate verification is disabled — do not use in production"
);
}
if self.allow_invalid_hostnames() {
tracing::warn!(
target: "rustcdc::transport::security",
source = source_label,
flag = "allow_invalid_hostnames",
"TLS hostname verification is disabled — do not use in production"
);
}
}
}
#[cfg(test)]
mod tests {
use super::TransportConfig;
#[test]
fn tls_defaults_to_strict_verification() {
let transport = TransportConfig::tls();
assert!(transport.is_tls());
assert!(!transport.allow_invalid_certificates());
assert!(!transport.allow_invalid_hostnames());
}
#[test]
fn tls_insecure_skip_verify_sets_insecure_flags() {
let transport = TransportConfig::tls_insecure_skip_verify();
assert!(transport.is_tls());
assert!(transport.allow_invalid_certificates());
assert!(transport.allow_invalid_hostnames());
}
}