From e43d5c83bc8f7fcbb6ee0e5bc4b7009ec3492b7f Mon Sep 17 00:00:00 2001 From: Alex Chi Z Date: Tue, 15 Apr 2025 13:08:15 -0400 Subject: [PATCH] add encryption key id Signed-off-by: Alex Chi Z --- pageserver/benches/upload_queue.rs | 2 +- .../tenant/remote_timeline_client/index.rs | 106 +++++++++++------- pageserver/src/tenant/upload_queue.rs | 4 +- 3 files changed, 70 insertions(+), 42 deletions(-) diff --git a/pageserver/benches/upload_queue.rs b/pageserver/benches/upload_queue.rs index d7c8e0d421..41ef9a28fd 100644 --- a/pageserver/benches/upload_queue.rs +++ b/pageserver/benches/upload_queue.rs @@ -45,7 +45,7 @@ fn bench_upload_queue_next_ready(c: &mut Criterion) { shard: ShardIndex::new(ShardNumber(1), ShardCount(2)), generation: Generation::Valid(1), file_size: 0, - encrypted_with_key_version: None, + encryption_key: None, }; // Construct the (initial and uploaded) index with layer0. diff --git a/pageserver/src/tenant/remote_timeline_client/index.rs b/pageserver/src/tenant/remote_timeline_client/index.rs index 7247906d7a..a2f9ebbfdf 100644 --- a/pageserver/src/tenant/remote_timeline_client/index.rs +++ b/pageserver/src/tenant/remote_timeline_client/index.rs @@ -122,12 +122,22 @@ pub struct IndexPart { pub(crate) keys: Option>, } +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct KeyVersion(u32); + +/// An identifier for an encryption key. The scope of the key is the timeline (TBD). +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct EncryptionKeyId { + version: KeyVersion, + generation: Generation, +} + #[serde_as] #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct EncryptionKey { #[serde_as(as = "Base64")] pub key: Vec, - pub version: u32, + pub id: EncryptionKeyId, pub created_at: NaiveDateTime, } @@ -157,7 +167,7 @@ impl IndexPart { /// - 12: +l2_lsn /// - 13: +gc_compaction /// - 14: +marked_invisible_at - /// - 15: +keys and encrypted_with_key_version in layer_metadata + /// - 15: +keys and encryption_key in layer_metadata const LATEST_VERSION: usize = 15; // Versions we may see when reading from a bucket. @@ -240,7 +250,7 @@ impl IndexPart { /// /// Fields have to be `Option`s because remote [`IndexPart`]'s can be from different version, which /// might have less or more metadata depending if upgrading or rolling back an upgrade. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct LayerFileMetadata { pub file_size: u64, @@ -253,7 +263,7 @@ pub struct LayerFileMetadata { pub shard: ShardIndex, #[serde(skip_serializing_if = "Option::is_none", default)] - pub encrypted_with_key_version: Option, + pub encryption_key: Option, } impl LayerFileMetadata { @@ -262,7 +272,7 @@ impl LayerFileMetadata { file_size, generation, shard, - encrypted_with_key_version: None, + encryption_key: None, } } /// Helper to get both generation and file size in a tuple @@ -476,7 +486,7 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for @@ -484,7 +494,7 @@ mod tests { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -528,7 +538,7 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for @@ -536,7 +546,7 @@ mod tests { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -581,7 +591,7 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for @@ -589,7 +599,7 @@ mod tests { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -686,7 +696,7 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for @@ -694,7 +704,7 @@ mod tests { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -738,13 +748,13 @@ mod tests { file_size: 23289856, generation: Generation::new(1), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000014EF499-00000000015A7619".parse().unwrap(), LayerFileMetadata { file_size: 1015808, generation: Generation::new(1), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: Lsn::from_str("0/15A7618").unwrap(), @@ -795,7 +805,7 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { // serde_json should always parse this but this might be a double with jq for @@ -803,7 +813,7 @@ mod tests { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -857,13 +867,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -922,13 +932,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -989,13 +999,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1068,13 +1078,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1152,13 +1162,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1240,13 +1250,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1331,13 +1341,13 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: None, + encryption_key: None, }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1381,8 +1391,8 @@ mod tests { let example = r#"{ "version": 15, "layer_metadata":{ - "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9": { "file_size": 25600000, "encrypted_with_key_version": 1 }, - "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51": { "file_size": 9007199254741001, "encrypted_with_key_version": 2 } + "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9": { "file_size": 25600000, "encryption_key": 1 }, + "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51": { "file_size": 9007199254741001, "encryption_key": 2 } }, "disk_consistent_lsn":"0/16960E8", "metadata": { @@ -1416,12 +1426,18 @@ mod tests { "keys": [ { "key": "dGVzdF9rZXk=", - "version": 1, + "id": { + "version": 1, + "generation": 5 + }, "created_at": "2024-07-19T09:00:00.123" }, { "key": "dGVzdF9rZXlfMg==", - "version": 2, + "id": { + "version": 2, + "generation": 6 + }, "created_at": "2024-07-19T10:00:00.123" } ] @@ -1434,13 +1450,19 @@ mod tests { file_size: 25600000, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: Some(1), + encryption_key: Some(EncryptionKeyId { + version: KeyVersion(1), + generation: Generation::Valid(5), + }), }), ("000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51".parse().unwrap(), LayerFileMetadata { file_size: 9007199254741001, generation: Generation::none(), shard: ShardIndex::unsharded(), - encrypted_with_key_version: Some(2), + encryption_key: Some(EncryptionKeyId { + version: KeyVersion(2), + generation: Generation::Valid(6), + }), }) ]), disk_consistent_lsn: "0/16960E8".parse::().unwrap(), @@ -1475,12 +1497,18 @@ mod tests { keys: Some(vec![ EncryptionKey { key: "test_key".as_bytes().to_vec(), - version: 1, + id: EncryptionKeyId { + version: KeyVersion(1), + generation: Generation::Valid(5), + }, created_at: parse_naive_datetime("2024-07-19T09:00:00.123000000"), }, EncryptionKey { key: "test_key_2".as_bytes().to_vec(), - version: 2, + id: EncryptionKeyId { + version: KeyVersion(2), + generation: Generation::Valid(6), + }, created_at: parse_naive_datetime("2024-07-19T10:00:00.123000000"), } ]), diff --git a/pageserver/src/tenant/upload_queue.rs b/pageserver/src/tenant/upload_queue.rs index a02a5a0231..5a38b04750 100644 --- a/pageserver/src/tenant/upload_queue.rs +++ b/pageserver/src/tenant/upload_queue.rs @@ -641,7 +641,7 @@ mod tests { generation: timeline.generation, shard: timeline.get_shard_index(), file_size: size as u64, - encrypted_with_key_version: None, + encryption_key: None, }; make_layer_with_metadata(timeline, name, metadata) } @@ -1379,7 +1379,7 @@ mod tests { shard, generation: Generation::Valid(generation), file_size: 0, - encrypted_with_key_version: None, + encryption_key: None, }; make_layer_with_metadata(&tli, name, metadata) };