tenseleyflow/hyprkvm / 34378cc

Browse files

feat: integrate TLS with backwards compatibility

- Add tls.enabled config (default: false) for opt-in encryption
- Add per-neighbor tls override for mixed TLS/non-TLS environments
- Generate certificates on startup when TLS enabled
- Use TLS for server binding and outgoing connections based on config
- Print certificate fingerprint at startup for TOFU verification
- Clean up unused import warnings in network module
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
34378cc01370f9935c07fdfb3ffa94c76235c008
Parents
ce63a95
Tree
c874474

5 changed files

StatusFile+-
M hyprkvm-daemon/src/config/mod.rs 16 1
M hyprkvm-daemon/src/main.rs 69 5
M hyprkvm-daemon/src/network/mod.rs 2 0
M hyprkvm-daemon/src/network/tls.rs 2 0
M hyprkvm-daemon/src/network/transport.rs 1 2
hyprkvm-daemon/src/config/mod.rsmodified
@@ -138,6 +138,10 @@ impl Default for NetworkConfig {
138138
 
139139
 #[derive(Debug, Clone, Serialize, Deserialize)]
140140
 pub struct TlsConfig {
141
+    /// Enable TLS encryption (default: false for backwards compatibility)
142
+    #[serde(default)]
143
+    pub enabled: bool,
144
+
141145
     /// Path to certificate file
142146
     #[serde(default = "default_cert_path")]
143147
     pub cert_path: String,
@@ -145,6 +149,10 @@ pub struct TlsConfig {
145149
     /// Path to private key file
146150
     #[serde(default = "default_key_path")]
147151
     pub key_path: String,
152
+
153
+    /// Enable TOFU (Trust On First Use) for unknown peers
154
+    #[serde(default = "default_true")]
155
+    pub tofu: bool,
148156
 }
149157
 
150158
 fn default_cert_path() -> String {
@@ -166,8 +174,10 @@ fn default_key_path() -> String {
166174
 impl Default for TlsConfig {
167175
     fn default() -> Self {
168176
         Self {
177
+            enabled: false, // Default to false for backwards compatibility
169178
             cert_path: default_cert_path(),
170179
             key_path: default_key_path(),
180
+            tofu: true,
171181
         }
172182
     }
173183
 }
@@ -209,8 +219,13 @@ pub struct NeighborConfig {
209219
     /// Address (ip:port)
210220
     pub address: SocketAddr,
211221
 
212
-    /// Pre-trusted certificate fingerprint (optional)
222
+    /// Pre-trusted certificate fingerprint (optional, for TLS pinning)
223
+    #[serde(default, skip_serializing_if = "Option::is_none")]
213224
     pub fingerprint: Option<String>,
225
+
226
+    /// Override TLS setting for this neighbor (uses global setting if None)
227
+    #[serde(default, skip_serializing_if = "Option::is_none")]
228
+    pub tls: Option<bool>,
214229
 }
215230
 
216231
 #[derive(Debug, Clone, Serialize, Deserialize)]
hyprkvm-daemon/src/main.rsmodified
@@ -275,10 +275,41 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
275275
     let peers: Arc<RwLock<HashMap<Direction, network::FramedConnection>>> =
276276
         Arc::new(RwLock::new(HashMap::new()));
277277
 
278
+    // TLS setup (if enabled)
279
+    let tls_enabled = config.network.tls.enabled;
280
+    if tls_enabled {
281
+        info!("TLS is enabled, ensuring certificates exist...");
282
+        let cert_path = std::path::Path::new(&config.network.tls.cert_path);
283
+        let key_path = std::path::Path::new(&config.network.tls.key_path);
284
+
285
+        if let Err(e) = network::ensure_certificate(cert_path, key_path, &config.machines.self_name) {
286
+            anyhow::bail!("Failed to setup TLS certificates: {}", e);
287
+        }
288
+
289
+        // Print certificate fingerprint for users to share
290
+        match network::get_cert_fingerprint(cert_path) {
291
+            Ok(fp) => {
292
+                info!("Certificate fingerprint: {}", fp);
293
+                info!("Share this fingerprint with peers for secure verification");
294
+            }
295
+            Err(e) => {
296
+                tracing::warn!("Could not read certificate fingerprint: {}", e);
297
+            }
298
+        }
299
+    } else {
300
+        info!("TLS is disabled (plain TCP mode for backwards compatibility)");
301
+    }
302
+
278303
     // Start network server
279304
     let listen_addr: SocketAddr = format!("0.0.0.0:{}", config.network.listen_port).parse()?;
280
-    let server = network::Server::bind(listen_addr).await?;
281
-    info!("Listening for connections on {}", server.local_addr());
305
+    let server = if tls_enabled {
306
+        let cert_path = std::path::Path::new(&config.network.tls.cert_path);
307
+        let key_path = std::path::Path::new(&config.network.tls.key_path);
308
+        network::Server::bind_tls(listen_addr, cert_path, key_path).await?
309
+    } else {
310
+        network::Server::bind(listen_addr).await?
311
+    };
312
+    info!("Listening for connections on {} (TLS: {})", server.local_addr(), tls_enabled);
282313
 
283314
     // Spawn task to accept incoming connections
284315
     let machine_name = config.machines.self_name.clone();
@@ -355,6 +386,13 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
355386
         let direction = neighbor.direction;
356387
         let peers_clone = peers.clone();
357388
         let machine_name = config.machines.self_name.clone();
389
+        let neighbor_name = neighbor.name.clone();
390
+
391
+        // Determine if TLS should be used for this neighbor
392
+        // Per-neighbor override takes precedence over global setting
393
+        let use_tls = neighbor.tls.unwrap_or(tls_enabled);
394
+        let fingerprint = neighbor.fingerprint.clone();
395
+        let tofu_enabled = config.network.tls.tofu;
358396
 
359397
         tokio::spawn(async move {
360398
             loop {
@@ -369,8 +407,21 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
369407
                     }
370408
                 }
371409
 
372
-                tracing::debug!("Connecting to {} at {}...", direction, addr);
373
-                match network::connect(addr).await {
410
+                tracing::debug!("Connecting to {} at {} (TLS: {})...", direction, addr, use_tls);
411
+
412
+                // Connect with or without TLS
413
+                let conn_result = if use_tls {
414
+                    network::connect_tls(
415
+                        addr,
416
+                        &neighbor_name,
417
+                        fingerprint.as_deref(),
418
+                        tofu_enabled,
419
+                    ).await
420
+                } else {
421
+                    network::connect(addr).await
422
+                };
423
+
424
+                match conn_result {
374425
                     Ok(mut conn) => {
375426
                         // Send Hello
376427
                         let hello = Message::Hello(HelloPayload {
@@ -1695,8 +1746,21 @@ async fn run_daemon(config_path: &std::path::Path) -> anyhow::Result<()> {
16951746
                                 let peers_clone = peers.clone();
16961747
                                 let machine_name = config.machines.self_name.clone();
16971748
                                 let neighbor_name = n.name.clone();
1749
+
1750
+                                // Determine TLS settings for this neighbor
1751
+                                let use_tls = n.tls.unwrap_or(tls_enabled);
1752
+                                let fingerprint = n.fingerprint.clone();
1753
+                                let tofu = config.network.tls.tofu;
1754
+
16981755
                                 tokio::spawn(async move {
1699
-                                    match network::connect(addr).await {
1756
+                                    // Connect with or without TLS
1757
+                                    let conn_result = if use_tls {
1758
+                                        network::connect_tls(addr, &neighbor_name, fingerprint.as_deref(), tofu).await
1759
+                                    } else {
1760
+                                        network::connect(addr).await
1761
+                                    };
1762
+
1763
+                                    match conn_result {
17001764
                                         Ok(mut conn) => {
17011765
                                             // Send Hello
17021766
                                             let hello = Message::Hello(HelloPayload {
hyprkvm-daemon/src/network/mod.rsmodified
@@ -2,6 +2,8 @@
22
 //!
33
 //! Handles peer-to-peer connections between HyprKVM instances.
44
 
5
+#![allow(unused_imports)]
6
+
57
 #[allow(dead_code)]
68
 pub mod known_hosts;
79
 #[allow(dead_code)]
hyprkvm-daemon/src/network/tls.rsmodified
@@ -234,6 +234,7 @@ pub struct HyprkvmCertVerifier {
234234
 
235235
 impl HyprkvmCertVerifier {
236236
     /// Create verifier with pinned fingerprint
237
+    #[allow(dead_code)]
237238
     pub fn with_fingerprint(fingerprint: Fingerprint) -> Self {
238239
         Self {
239240
             expected_fingerprint: Some(fingerprint),
@@ -242,6 +243,7 @@ impl HyprkvmCertVerifier {
242243
     }
243244
 
244245
     /// Create verifier with TOFU enabled
246
+    #[allow(dead_code)]
245247
     pub fn with_tofu() -> Self {
246248
         Self {
247249
             expected_fingerprint: None,
hyprkvm-daemon/src/network/transport.rsmodified
@@ -8,7 +8,6 @@ use std::io;
88
 use std::net::SocketAddr;
99
 use std::path::Path;
1010
 use std::pin::Pin;
11
-use std::sync::Arc;
1211
 use std::task::{Context, Poll};
1312
 
1413
 use bytes::{Buf, BufMut, BytesMut};
@@ -16,7 +15,7 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
1615
 use tokio::net::{TcpListener, TcpStream};
1716
 use tokio_rustls::client::TlsStream as ClientTlsStream;
1817
 use tokio_rustls::server::TlsStream as ServerTlsStream;
19
-use tokio_rustls::{TlsAcceptor, TlsConnector};
18
+use tokio_rustls::TlsAcceptor;
2019
 
2120
 use hyprkvm_common::protocol::Message;
2221