diff --git a/pageserver/src/tenant/remote_timeline_client.rs b/pageserver/src/tenant/remote_timeline_client.rs index ef975d1f5d..77c529ad97 100644 --- a/pageserver/src/tenant/remote_timeline_client.rs +++ b/pageserver/src/tenant/remote_timeline_client.rs @@ -732,9 +732,43 @@ impl RemoteTimelineClient { reason: "no need for a downloads gauge", }, ); + let key_pair = if let Some(ref key_id) = layer_metadata.encryption_key { + let wrapped_key = { + let mut queue = self.upload_queue.lock().unwrap(); + let upload_queue = queue.initialized_mut().unwrap(); + let encryption_key_pair = + upload_queue.dirty.keys.iter().find(|key| &key.id == key_id); + if let Some(encryption_key_pair) = encryption_key_pair { + // TODO: also check if we have uploaded the key yet; we should never use a key that is not persisted + encryption_key_pair.clone() + } else { + return Err(DownloadError::Other(anyhow::anyhow!( + "Encryption key pair not found in index_part.json" + ))); + } + }; + let Some(kms) = self.kms_impl.as_ref() else { + return Err(DownloadError::Other(anyhow::anyhow!( + "KMS not configured when downloading encrypted layer file" + ))); + }; + let plain_key = kms + .decrypt(&wrapped_key.key) + .context("failed to decrypt encryption key") + .map_err(DownloadError::Other)?; + Some(EncryptionKeyPair::new( + wrapped_key.id, + plain_key, + wrapped_key.key, + )) + } else { + None + }; + download::download_layer_file( self.conf, &self.storage_impl, + key_pair.as_ref(), self.tenant_shard_id, self.timeline_id, layer_file_name, diff --git a/pageserver/src/tenant/remote_timeline_client/download.rs b/pageserver/src/tenant/remote_timeline_client/download.rs index 70f77ef9e8..e967c8d957 100644 --- a/pageserver/src/tenant/remote_timeline_client/download.rs +++ b/pageserver/src/tenant/remote_timeline_client/download.rs @@ -23,7 +23,7 @@ use utils::crashsafe::path_with_suffix_extension; use utils::id::{TenantId, TimelineId}; use utils::{backoff, pausable_failpoint}; -use super::index::{IndexPart, LayerFileMetadata}; +use super::index::{EncryptionKeyPair, IndexPart, LayerFileMetadata}; use super::manifest::TenantManifest; use super::{ FAILED_DOWNLOAD_WARN_THRESHOLD, FAILED_REMOTE_OP_RETRIES, INITDB_PATH, parse_remote_index_path, @@ -51,6 +51,7 @@ use crate::virtual_file::{MaybeFatalIo, VirtualFile, on_fatal_io_error}; pub async fn download_layer_file<'a>( conf: &'static PageServerConf, storage: &'a GenericRemoteStorage, + key_pair: Option<&'a EncryptionKeyPair>, tenant_shard_id: TenantShardId, timeline_id: TimelineId, layer_file_name: &'a LayerName, @@ -86,7 +87,16 @@ pub async fn download_layer_file<'a>( let bytes_amount = download_retry( || async { - download_object(storage, &remote_path, &temp_file_path, gate, cancel, ctx).await + download_object( + storage, + key_pair, + &remote_path, + &temp_file_path, + gate, + cancel, + ctx, + ) + .await }, &format!("download {remote_path:?}"), cancel, @@ -145,6 +155,7 @@ pub async fn download_layer_file<'a>( /// The unlinking has _not_ been made durable. async fn download_object( storage: &GenericRemoteStorage, + encryption_key_pair: Option<&EncryptionKeyPair>, src_path: &RemotePath, dst_path: &Utf8PathBuf, #[cfg_attr(target_os = "macos", allow(unused_variables))] gate: &utils::sync::gate::Gate, @@ -160,9 +171,12 @@ async fn download_object( .with_context(|| format!("create a destination file for layer '{dst_path}'")) .map_err(DownloadError::Other)?; - let download = storage - .download(src_path, &DownloadOpts::default(), cancel) - .await?; + let mut opts = DownloadOpts::default(); + if let Some(encryption_key_pair) = encryption_key_pair { + opts.encryption_key = Some(encryption_key_pair.plain_key.to_vec()); + } + + let download = storage.download(src_path, &opts, cancel).await?; pausable_failpoint!("before-downloading-layer-stream-pausable"); diff --git a/pageserver/src/tenant/secondary/downloader.rs b/pageserver/src/tenant/secondary/downloader.rs index 60cf7ac79e..90fbdcccc8 100644 --- a/pageserver/src/tenant/secondary/downloader.rs +++ b/pageserver/src/tenant/secondary/downloader.rs @@ -1310,6 +1310,7 @@ impl<'a> TenantDownloader<'a> { let downloaded_bytes = download_layer_file( self.conf, self.remote_storage, + None, // TODO: add encryption key pair *tenant_shard_id, *timeline_id, &layer.name,