summaryrefslogtreecommitdiff
path: root/packet-detector/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'packet-detector/src/bin')
-rw-r--r--packet-detector/src/bin/tls_client.rs51
-rw-r--r--packet-detector/src/bin/tls_server.rs295
2 files changed, 346 insertions, 0 deletions
diff --git a/packet-detector/src/bin/tls_client.rs b/packet-detector/src/bin/tls_client.rs
new file mode 100644
index 0000000..6098172
--- /dev/null
+++ b/packet-detector/src/bin/tls_client.rs
@@ -0,0 +1,51 @@
1//! mTLS test client
2
3use std::sync::Arc;
4use packet_detector::tls_util::LoggingVerifier;
5use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
6use rustls::version::TLS12;
7use rustls::ClientConfig;
8use tokio::io::{AsyncReadExt, AsyncWriteExt};
9use tokio::net::TcpStream;
10use tokio_rustls::TlsConnector;
11
12const CERT: &str = "client_cert.pem";
13const KEY: &str = "client_key.pem";
14
15fn load_creds() -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn std::error::Error>> {
16 let cert_pem = std::fs::read_to_string(CERT)?;
17 let key_pem = std::fs::read_to_string(KEY)?;
18 let certs = rustls_pemfile::certs(&mut cert_pem.as_bytes()).collect::<Result<Vec<_>, _>>()?;
19 let key = rustls_pemfile::private_key(&mut key_pem.as_bytes())?.ok_or("No key")?;
20 Ok((certs, key))
21}
22
23#[tokio::main]
24async fn main() -> Result<(), Box<dyn std::error::Error>> {
25 rustls::crypto::ring::default_provider().install_default().ok();
26
27 let host = std::env::args().nth(1).unwrap_or_else(|| "127.0.0.1".into());
28 let port: u16 = std::env::args().nth(2).and_then(|s| s.parse().ok()).unwrap_or(8443);
29
30 let (certs, key) = load_creds()?;
31 println!("Connecting to {}:{} with client cert", host, port);
32
33 let config = ClientConfig::builder_with_protocol_versions(&[&TLS12])
34 .dangerous()
35 .with_custom_certificate_verifier(Arc::new(LoggingVerifier))
36 .with_client_auth_cert(certs, key)?;
37
38 let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
39 let mut tls = TlsConnector::from(Arc::new(config))
40 .connect(ServerName::try_from(host.clone())?, stream).await?;
41 println!("TLS handshake complete!");
42
43 let req = format!("GET / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", host);
44 tls.write_all(req.as_bytes()).await?;
45
46 let mut resp = Vec::new();
47 tls.read_to_end(&mut resp).await?;
48 println!("\n{}", String::from_utf8_lossy(&resp));
49
50 Ok(())
51}
diff --git a/packet-detector/src/bin/tls_server.rs b/packet-detector/src/bin/tls_server.rs
new file mode 100644
index 0000000..7f68062
--- /dev/null
+++ b/packet-detector/src/bin/tls_server.rs
@@ -0,0 +1,295 @@
1//! TLS 1.2 Test Server with Client Tracking (mTLS Enabled)
2//!
3//! A mutual TLS (mTLS) HTTPS server for testing the eBPF TLS certificate validator.
4//! Requires client certificates signed by the CA for authentication.
5//! Tracks connected clients and displays their status.
6
7use std::collections::HashMap;
8use std::env;
9use std::fs;
10use std::net::SocketAddr;
11use std::path::Path;
12use std::sync::Arc;
13
14use packet_detector::tls_util::{dn, parse_pem};
15use rcgen::{BasicConstraints, CertificateParams, IsCa, Issuer, KeyPair, KeyUsagePurpose, SanType};
16use rustls::pki_types::{CertificateDer, PrivateKeyDer};
17use rustls::server::{ServerConfig, WebPkiClientVerifier};
18use rustls::version::TLS12;
19use rustls::RootCertStore;
20use tokio::io::{AsyncReadExt, AsyncWriteExt};
21use tokio::net::TcpListener;
22use tokio::sync::RwLock;
23use tokio_rustls::TlsAcceptor;
24
25const DEFAULT_PORT: u16 = 8443;
26const CA_CERT_PATH: &str = "ca_cert.pem";
27const CA_KEY_PATH: &str = "ca_key.pem";
28const SERVER_CERT_PATH: &str = "server_cert.pem";
29const SERVER_KEY_PATH: &str = "server_key.pem";
30const CLIENT_CERT_PATH: &str = "client_cert.pem";
31const CLIENT_KEY_PATH: &str = "client_key.pem";
32
33#[derive(Clone, Debug, serde::Serialize)]
34struct Client {
35 ip: String,
36 connected_at: chrono::DateTime<chrono::Utc>,
37 #[serde(skip)]
38 last_seen: chrono::DateTime<chrono::Utc>,
39 requests: u64,
40}
41
42#[derive(Default)]
43struct State {
44 clients: HashMap<String, Client>,
45 connections: u64,
46 requests: u64,
47}
48
49/// Generate a CA certificate for signing server and client certificates
50fn generate_ca_certificate() -> Result<(String, String, KeyPair), Box<dyn std::error::Error>> {
51 println!("Generating CA certificate...");
52
53 let mut params = CertificateParams::default();
54 params.distinguished_name = dn("eBPF Test CA", "Zero Trust Network");
55 params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
56 params.key_usages = vec![
57 KeyUsagePurpose::KeyCertSign,
58 KeyUsagePurpose::CrlSign,
59 ];
60
61 let key_pair = KeyPair::generate()?;
62 let cert = params.self_signed(&key_pair)?;
63
64 Ok((cert.pem(), key_pair.serialize_pem(), key_pair))
65}
66
67/// Generate a server certificate signed by the CA
68fn generate_server_certificate(
69 ca_cert_pem: &str,
70 ca_key_pem: &str,
71) -> Result<(String, String), Box<dyn std::error::Error>> {
72 println!("Generating server certificate signed by CA...");
73
74 let ca_key = KeyPair::from_pem(ca_key_pem)?;
75 let issuer = Issuer::from_ca_cert_pem(ca_cert_pem, ca_key)?;
76
77 let mut params = CertificateParams::default();
78 params.distinguished_name = dn("localhost", "eBPF Test Server");
79 params.subject_alt_names = vec![
80 SanType::DnsName("localhost".try_into()?),
81 SanType::IpAddress(std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1))),
82 ];
83 params.key_usages = vec![
84 KeyUsagePurpose::DigitalSignature,
85 KeyUsagePurpose::KeyEncipherment,
86 ];
87
88 let key_pair = KeyPair::generate()?;
89 let cert = params.signed_by(&key_pair, &issuer)?;
90
91 Ok((cert.pem(), key_pair.serialize_pem()))
92}
93
94/// Generate a client certificate signed by the CA
95fn generate_client_certificate(
96 client_name: &str,
97 ca_cert_pem: &str,
98 ca_key_pem: &str,
99) -> Result<(String, String), Box<dyn std::error::Error>> {
100 println!("Generating client certificate for: {}", client_name);
101
102 let ca_key = KeyPair::from_pem(ca_key_pem)?;
103 let issuer = Issuer::from_ca_cert_pem(ca_cert_pem, ca_key)?;
104
105 let mut params = CertificateParams::default();
106 params.distinguished_name = dn(client_name, "eBPF Test Client");
107 params.key_usages = vec![
108 KeyUsagePurpose::DigitalSignature,
109 ];
110
111 let key_pair = KeyPair::generate()?;
112 let cert = params.signed_by(&key_pair, &issuer)?;
113
114 Ok((cert.pem(), key_pair.serialize_pem()))
115}
116
117/// PKI setup result
118struct PkiSetup {
119 ca_cert_pem: String,
120 server_cert: Vec<CertificateDer<'static>>,
121 server_key: PrivateKeyDer<'static>,
122}
123
124/// Load or generate the full PKI (CA, server cert, client cert)
125fn setup_pki() -> Result<PkiSetup, Box<dyn std::error::Error>> {
126 let ca_cert_pem: String;
127 let ca_key_pem: String;
128
129 // Load or generate CA
130 if Path::new(CA_CERT_PATH).exists() && Path::new(CA_KEY_PATH).exists() {
131 println!("Loading existing CA from {} and {}", CA_CERT_PATH, CA_KEY_PATH);
132 ca_cert_pem = fs::read_to_string(CA_CERT_PATH)?;
133 ca_key_pem = fs::read_to_string(CA_KEY_PATH)?;
134 } else {
135 println!("Generating new PKI infrastructure...");
136 let (cert, key, _) = generate_ca_certificate()?;
137 fs::write(CA_CERT_PATH, &cert)?;
138 fs::write(CA_KEY_PATH, &key)?;
139 println!("Saved CA to {} and {}", CA_CERT_PATH, CA_KEY_PATH);
140 ca_cert_pem = cert;
141 ca_key_pem = key;
142 }
143
144 // Load or generate server certificate
145 let server_cert_pem: String;
146 let server_key_pem: String;
147
148 if Path::new(SERVER_CERT_PATH).exists() && Path::new(SERVER_KEY_PATH).exists() {
149 println!("Loading existing server certificate...");
150 server_cert_pem = fs::read_to_string(SERVER_CERT_PATH)?;
151 server_key_pem = fs::read_to_string(SERVER_KEY_PATH)?;
152 } else {
153 let (cert, key) = generate_server_certificate(&ca_cert_pem, &ca_key_pem)?;
154 fs::write(SERVER_CERT_PATH, &cert)?;
155 fs::write(SERVER_KEY_PATH, &key)?;
156 println!("Saved server cert to {} and {}", SERVER_CERT_PATH, SERVER_KEY_PATH);
157 server_cert_pem = cert;
158 server_key_pem = key;
159 }
160
161 // Load or generate client certificate
162 if !Path::new(CLIENT_CERT_PATH).exists() || !Path::new(CLIENT_KEY_PATH).exists() {
163 let (cert, key) = generate_client_certificate("test-client", &ca_cert_pem, &ca_key_pem)?;
164 fs::write(CLIENT_CERT_PATH, &cert)?;
165 fs::write(CLIENT_KEY_PATH, &key)?;
166 println!("Saved client cert to {} and {}", CLIENT_CERT_PATH, CLIENT_KEY_PATH);
167 }
168
169 // Parse certificates
170 let server_certs = parse_pem(&server_cert_pem)?;
171
172 let server_key = rustls_pemfile::private_key(&mut server_key_pem.as_bytes())?
173 .ok_or("No server private key found")?;
174
175 Ok(PkiSetup {
176 ca_cert_pem,
177 server_cert: server_certs,
178 server_key,
179 })
180}
181
182fn parse_request(data: &[u8]) -> (&str, Option<String>) {
183 let req = std::str::from_utf8(data).unwrap_or("");
184 let first_line = req.lines().next().unwrap_or("");
185
186 // Check X-Client-Name header
187 let client = req.lines()
188 .find(|l| l.to_lowercase().starts_with("x-client-name:"))
189 .map(|l| l.split(':').nth(1).unwrap_or("").trim().to_string())
190 .or_else(|| {
191 // Check ?client= query param
192 first_line.find("client=").map(|i| {
193 let rest = &first_line[i + 7..];
194 rest[..rest.find(|c| c == '&' || c == ' ').unwrap_or(rest.len())].to_string()
195 })
196 });
197
198 (first_line, client)
199}
200
201async fn handle_connection(
202 mut stream: tokio_rustls::server::TlsStream<tokio::net::TcpStream>,
203 peer: SocketAddr,
204 state: Arc<RwLock<State>>,
205) {
206 state.write().await.connections += 1;
207
208 let mut buf = vec![0u8; 4096];
209 let n = match stream.read(&mut buf).await {
210 Ok(0) => return,
211 Ok(n) => n,
212 Err(_) => return,
213 };
214
215 let (path, client_name) = parse_request(&buf[..n]);
216
217 // Track client if name provided
218 if let Some(name) = &client_name {
219 let mut s = state.write().await;
220 s.requests += 1;
221 let now = chrono::Utc::now();
222 s.clients.entry(name.clone())
223 .and_modify(|c| { c.last_seen = now; c.requests += 1; })
224 .or_insert(Client {
225 ip: peer.ip().to_string(),
226 connected_at: now,
227 last_seen: now,
228 requests: 1,
229 });
230 }
231
232 // Route request
233 let (ctype, body) = if path.contains("/status") || path.contains("/clients") {
234 let s = state.read().await;
235 ("application/json", serde_json::json!({
236 "clients": s.clients.len(),
237 "connections": s.connections,
238 "requests": s.requests,
239 "list": &s.clients
240 }).to_string())
241 } else if path.contains("/register") {
242 ("application/json", serde_json::json!({
243 "status": "ok",
244 "client": client_name.as_deref().unwrap_or("unknown")
245 }).to_string())
246 } else {
247 ("text/plain", format!("TLS Server OK - {} clients", state.read().await.clients.len()))
248 };
249
250 let resp = format!(
251 "HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
252 ctype, body.len(), body
253 );
254 let _ = stream.write_all(resp.as_bytes()).await;
255 let _ = stream.shutdown().await;
256}
257
258
259#[tokio::main]
260async fn main() -> Result<(), Box<dyn std::error::Error>> {
261 rustls::crypto::ring::default_provider().install_default().ok();
262
263 let port: u16 = env::args().nth(1).and_then(|s| s.parse().ok()).unwrap_or(DEFAULT_PORT);
264
265 let pki = setup_pki()?;
266
267 // Build root store with CA
268 let mut root_store = RootCertStore::empty();
269 for cert in parse_pem(&pki.ca_cert_pem)? {
270 root_store.add(cert)?;
271 }
272
273 let verifier = WebPkiClientVerifier::builder(Arc::new(root_store)).build()?;
274 let config = ServerConfig::builder_with_protocol_versions(&[&TLS12])
275 .with_client_cert_verifier(verifier)
276 .with_single_cert(pki.server_cert, pki.server_key)?;
277
278 let acceptor = TlsAcceptor::from(Arc::new(config));
279 let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], port))).await?;
280 let state = Arc::new(RwLock::new(State::default()));
281
282 println!("mTLS server on :{} (endpoints: /, /status, /register)", port);
283 println!("Test: curl -k --tlsv1.2 --cert {} --key {} https://127.0.0.1:{}", CLIENT_CERT_PATH, CLIENT_KEY_PATH, port);
284
285 loop {
286 let Ok((stream, peer)) = listener.accept().await else { continue };
287 let acceptor = acceptor.clone();
288 let state = state.clone();
289 tokio::spawn(async move {
290 if let Ok(tls) = acceptor.accept(stream).await {
291 handle_connection(tls, peer, state).await;
292 }
293 });
294 }
295}