diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 97332d587..b7b4e24f8 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -1414,8 +1414,10 @@ impl Vmm { let mut ctx = OngoingMigrationContext::new(); // Set up the socket connection - let mut socket = - migration_transport::send_migration_socket(&send_data_migration.destination_url)?; + let mut socket = migration_transport::send_migration_socket( + &send_data_migration.destination_url, + send_data_migration.tls_dir.as_deref(), + )?; // Start the migration migration_transport::send_request_expect_ok( @@ -1505,6 +1507,7 @@ impl Vmm { let mut mem_send = migration_transport::SendAdditionalConnections::new( &send_data_migration.destination_url, send_data_migration.connections, + send_data_migration.tls_dir.as_deref(), &vm.guest_memory(), )?; @@ -2549,13 +2552,21 @@ impl RequestHandler for Vmm { &mut self, receive_data_migration: VmReceiveMigrationData, ) -> result::Result<(), MigratableError> { + receive_data_migration + .validate() + .context("Invalid receive migration configuration") + .map_err(MigratableError::MigrateReceive)?; + info!( - "Receiving migration: receiver_url = {}", - receive_data_migration.receiver_url + "Receiving migration: receiver_url={},tls={}", + receive_data_migration.receiver_url, + receive_data_migration.tls_dir.is_some() ); - let mut listener = - migration_transport::receive_migration_listener(&receive_data_migration.receiver_url)?; + let mut listener = migration_transport::receive_migration_listener( + &receive_data_migration.receiver_url, + receive_data_migration.tls_dir.as_deref(), + )?; event!("vm", "migration-receive-ready"); @@ -2614,9 +2625,10 @@ impl RequestHandler for Vmm { .map_err(MigratableError::MigrateSend)?; info!( - "Sending migration: destination_url={},local={},downtime={}ms,timeout={}s,timeout_strategy={:?}", + "Sending migration: destination_url={},local={},tls={},downtime={}ms,timeout={}s,timeout_strategy={:?}", send_data_migration.destination_url, send_data_migration.local, + send_data_migration.tls_dir.is_some(), send_data_migration.downtime().as_millis(), send_data_migration.timeout().as_secs(), send_data_migration.timeout_strategy diff --git a/vmm/src/migration_transport.rs b/vmm/src/migration_transport.rs index 7c7007ea6..caa27809b 100644 --- a/vmm/src/migration_transport.rs +++ b/vmm/src/migration_transport.rs @@ -9,7 +9,7 @@ use std::num::{NonZeroU32, ParseIntError}; use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::io::AsRawFd; use std::os::unix::net::{UnixListener, UnixStream}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::result::Result; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Receiver, Sender, SyncSender, TrySendError, channel, sync_channel}; @@ -511,6 +511,7 @@ impl SendAdditionalConnections { pub(crate) fn new( destination: &str, connections: NonZeroU32, + tls_dir: Option<&Path>, guest_memory: &GuestMemoryAtomic, ) -> Result { let mut threads = Vec::new(); @@ -537,7 +538,7 @@ impl SendAdditionalConnections { // the memory chunks to the workers, but does not send memory anymore. Thus in // this case we create one additional thread for each connection. for n in 0..configured_connections { - let mut socket = send_migration_socket(destination)?; + let mut socket = send_migration_socket(destination, tls_dir)?; let guest_memory = guest_memory.clone(); let message_rx = message_rx.clone(); let worker_error = worker_error.clone(); @@ -852,6 +853,7 @@ pub fn tcp_address_to_server_name(address: &str) -> Result<&str, TcpAddressParse /// Connect to a migration endpoint and return the established stream. pub(crate) fn send_migration_socket( destination_url: &str, + tls_dir: Option<&Path>, ) -> Result { if let Some(address) = destination_url.strip_prefix("tcp:") { info!("Connecting to TCP socket at {address}"); @@ -860,7 +862,19 @@ pub(crate) fn send_migration_socket( MigratableError::MigrateSend(anyhow!("Error connecting to TCP socket: {e}")) })?; - Ok(SocketStream::Tcp(socket)) + if let Some(tls_dir) = tls_dir { + // The address should have been validated by the API using this exact function. + // Any error here has to be treated as a programming error. + let server_name = tcp_address_to_server_name(address) + .expect("TCP address should have been validated by the API"); + TlsStream::new_client(socket, tls_dir, server_name) + .map(Box::new) + .map(SocketStream::Tls) + .context("Error creating TLS migration stream") + .map_err(MigratableError::MigrateSend) + } else { + Ok(SocketStream::Tcp(socket)) + } } else { let path = socket_url_to_path(destination_url).map_err(MigratableError::MigrateSend)?; info!("Connecting to UNIX socket at {path:?}"); @@ -876,12 +890,21 @@ pub(crate) fn send_migration_socket( /// Bind a migration listener for the receiver side. pub(crate) fn receive_migration_listener( receiver_url: &str, + tls_dir: Option<&Path>, ) -> Result { if let Some(address) = receiver_url.strip_prefix("tcp:") { - TcpListener::bind(address) - .map(ReceiveListener::Tcp) + let listener = TcpListener::bind(address) .context("Error binding to TCP socket") - .map_err(MigratableError::MigrateReceive) + .map_err(MigratableError::MigrateReceive)?; + + if let Some(tls_dir) = tls_dir { + let config = TlsServerConfig::new(tls_dir) + .context("Error creating TLS server config") + .map_err(MigratableError::MigrateReceive)?; + Ok(ReceiveListener::Tls(listener, config)) + } else { + Ok(ReceiveListener::Tcp(listener)) + } } else { let path = socket_url_to_path(receiver_url).map_err(MigratableError::MigrateReceive)?; UnixListener::bind(&path)