| Crates.io | mtls-tcp |
| lib.rs | mtls-tcp |
| version | 0.1.0 |
| created_at | 2026-01-01 15:40:19.839056+00 |
| updated_at | 2026-01-01 15:40:19.839056+00 |
| description | Raw TCP adapter for mTLS authentication with IP whitelisting |
| homepage | |
| repository | https://github.com/Crellsin/mtls-rs |
| max_upload_size | |
| id | 2016348 |
| size | 47,618 |
Raw TCP adapter for mTLS authentication with IP whitelisting.
mtls-tcp provides a low-level TCP adapter for integrating mTLS (mutual TLS) authentication and IP whitelisting into your raw TCP applications. It's designed for protocols that operate directly over TCP, such as custom binary protocols, legacy systems, or when you need maximum control over the network layer.
tokio for high-performance asynchronous I/OAdd to your Cargo.toml:
[dependencies]
mtls-tcp = "0.1.0"
use mtls_tcp::TcpServer;
use mtls_core::validator::ConnectionValidator;
use mtls_core::config::ServerConfig;
use std::path::Path;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure mTLS server
let server_config = ServerConfig::new(
Path::new("certs/server.crt"),
Path::new("certs/server.key"),
Path::new("certs/ca.crt"),
);
// Create connection validator
let validator = ConnectionValidator::create_for_server(server_config)?;
// Create TCP server
let server = TcpServer::new("127.0.0.1:8443", validator).await?;
println!("TCP server listening on 127.0.0.1:8443");
// Accept and handle connections
loop {
let mut connection = server.accept().await?;
tokio::spawn(async move {
let mut buffer = [0; 1024];
// Read from client
let n = match connection.read(&mut buffer).await {
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("Failed to read from socket: {}", e);
return;
}
};
// Echo back to client
if let Err(e) = connection.write_all(&buffer[0..n]).await {
eprintln!("Failed to write to socket: {}", e);
}
});
}
}
use mtls_tcp::TcpClient;
use mtls_core::validator::ConnectionValidator;
use mtls_core::config::ClientConfig;
use std::path::Path;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure mTLS client
let client_config = ClientConfig::new(
Path::new("certs/client.crt"),
Path::new("certs/client.key"),
)
.with_ca_cert_path(Path::new("certs/ca.crt"));
// Create connection validator
let validator = ConnectionValidator::create_for_client(client_config)?;
// Create TCP client and connect
let mut client = TcpClient::connect("127.0.0.1:8443", validator).await?;
println!("Connected to server");
// Send data
let message = b"Hello, mTLS TCP server!";
client.write_all(message).await?;
// Read response
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
println!("Received: {}", String::from_utf8_lossy(&buffer[0..n]));
Ok(())
}
use mtls_core::config::ServerConfig;
use ipnetwork::IpNetwork;
let server_config = ServerConfig::new(
Path::new("server.crt"),
Path::new("server.key"),
Path::new("ca.crt"),
)
.with_client_ipv4_whitelist(vec![
IpNetwork::new("192.168.1.0".parse()?, 24)?,
IpNetwork::new("10.0.0.0".parse()?, 8)?,
])
.with_require_client_auth(true);
let validator = ConnectionValidator::create_for_server(server_config)?;
use mtls_core::config::ClientConfig;
let client_config = ClientConfig::new(
Path::new("client.crt"),
Path::new("client.key"),
)
.with_ca_cert_path(Path::new("ca.crt"))
.with_verify_server(true);
let validator = ConnectionValidator::create_for_client(client_config)?;
tokio for non-blocking I/OYou can integrate mtls-tcp with custom protocols:
use mtls_tcp::{TcpServer, TcpClient};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
// Custom protocol handler
async fn handle_protocol<R: AsyncReadExt + Unpin, W: AsyncWriteExt + Unpin>(
mut reader: R,
mut writer: W,
) -> Result<(), Box<dyn std::error::Error>> {
// Read protocol header
let mut header = [0; 4];
reader.read_exact(&mut header).await?;
// Process based on header
// ... custom protocol logic ...
// Write response
writer.write_all(b"OK").await?;
Ok(())
}
// Use with mtls-tcp
let server = TcpServer::new("127.0.0.1:8443", validator).await?;
loop {
let connection = server.accept().await?;
tokio::spawn(async move {
let (reader, writer) = tokio::io::split(connection);
handle_protocol(reader, writer).await
});
}
For high-performance applications, you can implement connection pooling:
use mtls_tcp::TcpClient;
use std::sync::Arc;
use tokio::sync::Mutex;
struct ConnectionPool {
validator: Arc<ConnectionValidator>,
connections: Mutex<Vec<TcpClient>>,
max_size: usize,
}
impl ConnectionPool {
async fn get_connection(&self, addr: &str) -> Result<TcpClient, Box<dyn std::error::Error>> {
let mut connections = self.connections.lock().await;
// Try to get an existing connection
while let Some(conn) = connections.pop() {
// Check if connection is still usable
// (implementation depends on your requirements)
return Ok(conn);
}
// Create new connection
let conn = TcpClient::connect(addr, self.validator.clone()).await?;
Ok(conn)
}
async fn return_connection(&self, conn: TcpClient) {
let mut connections = self.connections.lock().await;
if connections.len() < self.max_size {
connections.push(conn);
}
}
}
Common error scenarios:
Test your mTLS TCP applications:
#[cfg(test)]
mod tests {
use super::*;
use tokio::net::TcpStream;
#[tokio::test]
async fn test_echo_server() {
// Start server in background
let server_handle = tokio::spawn(async {
start_server().await.unwrap();
});
// Give server time to start
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
// Create client and test
let validator = create_test_validator().await.unwrap();
let mut client = TcpClient::connect("127.0.0.1:8443", validator).await.unwrap();
// Test echo
let message = b"test";
client.write_all(message).await.unwrap();
let mut buffer = [0; 4];
client.read_exact(&mut buffer).await.unwrap();
assert_eq!(&buffer, message);
// Cleanup
server_handle.abort();
}
}
tokio runtimeDual-licensed under either:
at your option.
Contributions are welcome! Please see the main project repository for contribution guidelines.