summaryrefslogtreecommitdiff
path: root/packet-detector/src/bin/tls_server.rs
diff options
context:
space:
mode:
authorroot <root@sg2.noml.ch>2025-12-29 22:18:04 +0800
committerroot <root@sg2.noml.ch>2025-12-29 22:18:04 +0800
commit1951b063d7ec6d6e8db8a0b5074c73f887749208 (patch)
tree6ece8dfce605fbff6eca6be4bbeb5d7904417bbb /packet-detector/src/bin/tls_server.rs
initial commitmain
Diffstat (limited to 'packet-detector/src/bin/tls_server.rs')
-rw-r--r--packet-detector/src/bin/tls_server.rs295
1 files changed, 295 insertions, 0 deletions
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}