Skip to content

Commit e21852a

Browse files
feat: Integrate TLS on client and server side for secure connection
Add required crates rustls, tokio-rustls, webpki-roots and rustls-pemfile Implement functions to handle tls accepter and connector on server and client side Update serve config file to take file paths for fullchain.pem and privkey.pem Implement functions on server side to parse pem files Keep TLS optional on server side On Client side try to upgrade server for 10 seconds and fallback to non tls server
1 parent e17957e commit e21852a

12 files changed

Lines changed: 211 additions & 96 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ${{ matrix.os }}
1616
strategy:
1717
matrix:
18-
os: [ubuntu-latest, ubuntu-22.04-arm64, macos-latest, windows-latest]
18+
os: [ubuntu-latest, ubuntu-latest-arm64, macos-latest, windows-latest]
1919

2020
steps:
2121
- name: Checkout

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ rsa = { version = "0.9.8", features = ["serde", "sha2"] }
1111
ssh-key = "0.6.7"
1212
hex = "0.4.3"
1313
bincode = "2.0.1"
14+
tokio-rustls = "0.26.2"
15+
rustls = "0.23.31"

client/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ ratatui = "0.29.0"
1919
color-eyre = "0.6.5"
2020
chrono = "0.4.41"
2121
tui-textarea = "0.7.0"
22+
tokio-rustls.workspace = true
23+
rustls.workspace = true
24+
webpki-roots = "1.0.2"

client/src/main.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use null_talk_client::{
33
handlers::handle_client,
44
types::{LogLevel, LogMessage},
55
ui::run_terminal,
6-
utils::configure_client,
6+
utils::{configure_client, try_tls_handshake},
77
};
88
use std::env;
99
use tokio::net::TcpStream;
@@ -17,7 +17,7 @@ async fn main() {
1717

1818
// Create TCP connection thread
1919
let tcp = tokio::spawn(async move {
20-
let addr = {
20+
let (addr, host_name) = {
2121
let config_lock = data::CLIENT_CONFIG.lock().await;
2222
let config = match config_lock.as_ref() {
2323
Some(cfg) => cfg,
@@ -27,19 +27,52 @@ async fn main() {
2727
}
2828
};
2929

30-
format!("{}:{}", &config.hostname, &config.port)
30+
(
31+
format!("{}:{}", &config.hostname, &config.port),
32+
config.hostname.clone(),
33+
)
3134
};
3235

3336
match TcpStream::connect(addr.clone()).await {
3437
Ok(stream) => {
3538
LogMessage::log(
3639
LogLevel::INFO,
37-
format!("Successfully connected to {}", addr.clone()),
38-
5,
40+
format!("Establishing a secure TLS connection..."),
41+
0,
3942
)
4043
.await;
41-
42-
handle_client(Box::new(stream)).await;
44+
let result = try_tls_handshake(host_name, stream).await;
45+
match result {
46+
Some(tls_stream) => {
47+
LogMessage::log(
48+
LogLevel::INFO,
49+
format!("Successfully connected to {}", addr),
50+
5,
51+
)
52+
.await;
53+
handle_client(tls_stream).await;
54+
}
55+
None => match TcpStream::connect(addr.clone()).await {
56+
Ok(plain_stream) => {
57+
LogMessage::log(
58+
LogLevel::INFO,
59+
format!("Successfully connected to {}, TLS not enabled", addr),
60+
5,
61+
)
62+
.await;
63+
handle_client(Box::new(plain_stream)).await;
64+
}
65+
Err(_) => {
66+
LogMessage::log(
67+
LogLevel::ERROR,
68+
format!("Failed to connect to {}", addr),
69+
0,
70+
)
71+
.await;
72+
return;
73+
}
74+
},
75+
};
4376
}
4477
Err(_) => {
4578
LogMessage::log(LogLevel::ERROR, format!("Failed to connect to {}", addr), 0).await;

client/src/utils/net.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
use crate::data::CLIENT_CONFIG;
1+
use crate::{
2+
data::CLIENT_CONFIG,
3+
types::{LogLevel, LogMessage},
4+
};
25
use common::{
3-
net::{HandshakePacket, StreamReader, StreamWriter},
6+
net::{AsyncStream, HandshakePacket, StreamReader, StreamWriter},
47
utils::{enc as encutils, net as netutils},
58
};
69
use rsa::RsaPrivateKey;
10+
use rustls::{ClientConfig, pki_types::ServerName};
11+
use std::sync::Arc;
12+
use tokio::{
13+
net::TcpStream,
14+
time::{Duration, timeout},
15+
};
16+
use tokio_rustls::TlsConnector;
717

818
/// ### Perform the initial handshake with the server.
9-
///
19+
///
1020
/// This function handles the entire handshake process, including
1121
/// sending the initial handshake packet, receiving the server's
1222
/// response, and completing the handshake by establishing a
@@ -83,3 +93,58 @@ pub async fn perform_handshake(
8393

8494
Ok(session_key)
8595
}
96+
97+
pub async fn create_tls_connector() -> Result<TlsConnector, Box<dyn std::error::Error + Send + Sync>>
98+
{
99+
// Load the root certificates from the webpki-roots crate
100+
let mut root_store = rustls::RootCertStore::empty();
101+
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
102+
103+
// Create the client configuration
104+
let config = ClientConfig::builder()
105+
.with_root_certificates(root_store)
106+
.with_no_client_auth();
107+
108+
// Create the async TLS connector
109+
let connector = TlsConnector::from(Arc::new(config));
110+
Ok(connector)
111+
}
112+
113+
pub async fn try_tls_handshake(
114+
host_name: String,
115+
stream: TcpStream,
116+
) -> Option<Box<dyn AsyncStream>> {
117+
// Convert the hostname string to a ServerName
118+
let domain = match ServerName::try_from(host_name) {
119+
Ok(name) => name,
120+
Err(_) => return None,
121+
};
122+
123+
let tls_connector = match create_tls_connector().await {
124+
Ok(connector) => connector,
125+
Err(e) => {
126+
LogMessage::log(
127+
LogLevel::ERROR,
128+
format!("Failed to create TLS connector: {}", e),
129+
0,
130+
)
131+
.await;
132+
return None;
133+
}
134+
};
135+
136+
let timeout_duration = Duration::from_secs(10); // 10-second timeout
137+
138+
// The handshake future
139+
let handshake_future = tls_connector.connect(domain, stream);
140+
141+
// Apply the timeout
142+
let result = timeout(timeout_duration, handshake_future).await;
143+
144+
let tls = match result {
145+
Ok(Ok(tls)) => tls,
146+
_ => return None,
147+
};
148+
149+
Some(Box::new(tls))
150+
}

client/src/utils/tls.rs

Whitespace-only changes.

server/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ common = { path = "../common" }
1212
config.workspace = true
1313
hex.workspace = true
1414
rsa.workspace = true
15-
rustls = "0.23.31"
16-
rustls-pemfile = "2.2.0"
1715
serde = { workspace = true, features = ["derive"] }
1816
tokio = { workspace = true, features = ["full"] }
19-
tokio-rustls = "0.26.2"
2017
uuid = { version = "1.18.0", features = ["v4"] }
18+
rustls.workspace = true
19+
rustls-pemfile = "2.2.0"
20+
tokio-rustls.workspace = true

server/Config.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
host = "0.0.0.0"
21
port = 8080
32

4-
[tls]
5-
enabled = false
6-
cert_path = "/etc/letsencrypt/live/viveksahani.com/fullchain.pem"
7-
key_path = "/etc/letsencrypt/live/viveksahani.com/privkey.pem"
3+
# [tls]
4+
# enabled = false
5+
# cert_path = "/etc/letsencrypt/live/viveksahani.com/fullchain.pem"
6+
# key_path = "/etc/letsencrypt/live/viveksahani.com/privkey.pem"

server/src/config.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,15 @@ use std::{env, error::Error, path::PathBuf};
44

55
#[derive(Debug, Deserialize, Clone)]
66
pub struct TLSConfig {
7-
pub enabled: bool,
8-
pub cert_path: Option<String>,
9-
pub key_path: Option<String>,
7+
pub cert_path: String,
8+
pub key_path: String,
109
}
1110

1211
/// Configuration for the server
1312
#[derive(Debug, Deserialize, Clone)]
1413
pub struct ServerConfig {
15-
/// Hostname or IP address of the server
16-
pub host: String,
17-
18-
/// Port number for the server
14+
// pub domain: String,
1915
pub port: u16,
20-
21-
/// TLS configuration
2216
pub tls: Option<TLSConfig>
2317
}
2418

@@ -52,6 +46,6 @@ impl ServerConfig {
5246

5347
/// Get the server address as a string
5448
pub fn get_addr(&self) -> String {
55-
format!("{}:{}", self.host, self.port)
49+
format!("0.0.0.0:{}", self.port)
5650
}
5751
}

0 commit comments

Comments
 (0)