vmm: encrypt migration data with TLS if configured

Wire in the code paths that activate the TLS encrypting if the necessary
API arguments are provided.

On-behalf-of: SAP sebastian.eydam@sap.com
Signed-off-by: Sebastian Eydam <sebastian.eydam@cyberus-technology.de>
This commit is contained in:
Sebastian Eydam
2026-04-15 10:33:58 +02:00
committed by Rob Bradford
parent 320403a11e
commit cf28552085
2 changed files with 48 additions and 13 deletions

View File

@@ -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

View File

@@ -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<GuestMemoryMmap>,
) -> Result<Self, MigratableError> {
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<SocketStream, MigratableError> {
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<ReceiveListener, MigratableError> {
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)