Skip to content

Commit 2b85629

Browse files
committed
cdh: store ephemeral LUKS headers in tmpfs
When creating ephemeral storages, place the LUKS headers in guest memory rather than onto the storage device itself. For this, create LUKS header files in the CDH storage directory, and call into CryptInit::init_with_data_device instead of into CryptInit::init. The path where existing, encrypted storages are utilized remains untouched. Signed-off-by: Manuel Huber <manuelh@nvidia.com>
1 parent 22434de commit 2b85629

5 files changed

Lines changed: 310 additions & 65 deletions

File tree

confidential-data-hub/hub/src/auth/kbs.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ use kms::{plugins::kbs::KbcClient, Annotations, Getter};
1212
use log::debug;
1313
use tokio::fs;
1414

15-
use crate::{hub::Hub, Error, Result};
16-
17-
/// This directory is used to store all the kbs resources get by CDH's init
18-
/// function, s.t. `[[Credential]]` sections in the config.toml file.
19-
pub const KBS_RESOURCE_STORAGE_DIR: &str = "/run/confidential-containers/cdh";
15+
use crate::{config::KBS_RESOURCE_STORAGE_DIR, hub::Hub, Error, Result};
2016

2117
impl Hub {
2218
pub(crate) async fn init_kbs_resources(&self) -> Result<()> {

confidential-data-hub/hub/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ cfg_if::cfg_if! {
2020
}
2121
}
2222

23+
/// This directory is used to store all the kbs resources get by CDH's init
24+
/// function, s.t. `[[Credential]]` sections in the config.toml file.
25+
pub const KBS_RESOURCE_STORAGE_DIR: &str = "/run/confidential-containers/cdh";
26+
2327
const CDH_DEFAULT_IMAGE_AUTHENTICATED_REGISTRY_CREDENTIALS: &str =
2428
"CDH_DEFAULT_IMAGE_AUTHENTICATED_REGISTRY_CREDENTIALS";
2529

confidential-data-hub/hub/src/storage/drivers/luks2.rs

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
1313
use std::path::Path;
1414

15-
use anyhow::Context;
15+
use anyhow::{bail, Context};
1616
use libcryptsetup_rs::consts::flags::{CryptActivate, CryptDeactivate, CryptVolumeKey};
1717
use libcryptsetup_rs::consts::vals::EncryptionFormat;
1818
use libcryptsetup_rs::{CryptInit, CryptParamsLuks2, CryptParamsLuks2Ref};
@@ -43,11 +43,11 @@ impl Luks2Formatter {
4343

4444
pub fn encrypt_device(
4545
&self,
46-
device_path: &str,
46+
device_path: &Path,
47+
header_path: Option<&Path>,
4748
passphrase: Zeroizing<Vec<u8>>,
4849
) -> anyhow::Result<()> {
49-
let path = Path::new(device_path);
50-
let mut device = CryptInit::init(path)?;
50+
let mut device = init_device(device_path, header_path)?;
5151
let mut volume_key_length = LUKS2_VOLUME_KEY_SIZE_BIT_WITHOUT_INTEGRITY / 8;
5252
let mut params = CryptParamsLuks2 {
5353
pbkdf: None,
@@ -81,12 +81,12 @@ impl Luks2Formatter {
8181

8282
pub fn open_device(
8383
&self,
84-
device_path: &str,
84+
device_path: &Path,
85+
header_path: Option<&Path>,
8586
name: &str,
8687
passphrase: Zeroizing<Vec<u8>>,
8788
) -> anyhow::Result<()> {
88-
let path = Path::new(device_path);
89-
let mut device = CryptInit::init(path)?;
89+
let mut device = init_device(device_path, header_path)?;
9090

9191
let mut params = CryptParamsLuks2 {
9292
pbkdf: None,
@@ -130,18 +130,45 @@ impl Luks2Formatter {
130130
}
131131
}
132132

133+
fn init_device(
134+
device_path: &Path,
135+
header_path: Option<&Path>,
136+
) -> anyhow::Result<libcryptsetup_rs::CryptDevice> {
137+
let device_paths = match header_path {
138+
Some(header_path) => {
139+
if !header_path.exists() {
140+
bail!("LUKS header file not found: {}", header_path.display());
141+
}
142+
libcryptsetup_rs::Either::Right((header_path, device_path))
143+
}
144+
None => libcryptsetup_rs::Either::Left(device_path),
145+
};
146+
147+
Ok(CryptInit::init_with_data_device(device_paths)?)
148+
}
149+
133150
#[cfg(test)]
134151
mod tests {
135152
use std::io::Write;
153+
use std::path::PathBuf;
136154

137155
use serial_test::serial;
138156
use zeroize::Zeroizing;
139157

140158
use crate::storage::drivers::luks2::Luks2Formatter;
159+
use crate::storage::volume_type::blockdevice::prepare_luks_header_file;
141160

142161
const TEST_PASSPHRASE: &[u8] = b"test";
143162
const NAME: &str = "test";
144163

164+
/// Removes the LUKS header file on drop so tests don't leave files behind on panic.
165+
struct RemoveHeaderOnDrop(PathBuf);
166+
impl Drop for RemoveHeaderOnDrop {
167+
fn drop(&mut self) {
168+
let _ = std::fs::remove_file(&self.0);
169+
}
170+
}
171+
145172
#[test]
146173
#[serial]
147174
fn encrypt_open_device_no_integrity() {
@@ -151,15 +178,17 @@ mod tests {
151178
.as_file_mut()
152179
.write_all(&vec![0; 20 * 1024 * 1024])
153180
.unwrap();
154-
let path = bin_file.path().to_str().unwrap();
181+
let path = bin_file.path();
155182

156183
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
157184
let luks2_formatter = Luks2Formatter { integrity: false };
158185
luks2_formatter
159-
.encrypt_device(path, passphrase.clone())
186+
.encrypt_device(path, None, passphrase.clone())
160187
.unwrap();
161188

162-
luks2_formatter.open_device(path, NAME, passphrase).unwrap();
189+
luks2_formatter
190+
.open_device(path, None, NAME, passphrase)
191+
.unwrap();
163192

164193
luks2_formatter.close_device(NAME).unwrap();
165194
}
@@ -173,19 +202,113 @@ mod tests {
173202
.as_file_mut()
174203
.write_all(&vec![0; 20 * 1024 * 1024])
175204
.unwrap();
176-
let path = bin_file.path().to_str().unwrap();
205+
let path = bin_file.path();
177206

178207
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
179208
let luks2_formatter = Luks2Formatter { integrity: true };
180209
luks2_formatter
181-
.encrypt_device(path, passphrase.clone())
210+
.encrypt_device(path, None, passphrase.clone())
182211
.unwrap();
183212

184-
luks2_formatter.open_device(path, NAME, passphrase).unwrap();
213+
luks2_formatter
214+
.open_device(path, None, NAME, passphrase)
215+
.unwrap();
185216

186217
luks2_formatter.close_device(NAME).unwrap();
187218
}
188219

220+
#[test]
221+
#[serial]
222+
fn encrypt_open_device_no_integrity_with_header() {
223+
let mut bin_file = tempfile::NamedTempFile::new().unwrap();
224+
bin_file
225+
.as_file_mut()
226+
.write_all(&vec![0; 20 * 1024 * 1024])
227+
.unwrap();
228+
let path = bin_file.path();
229+
let header_path = prepare_luks_header_file(None, path).unwrap();
230+
let _guard = RemoveHeaderOnDrop(header_path.clone());
231+
232+
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
233+
let luks2_formatter = Luks2Formatter { integrity: false };
234+
luks2_formatter
235+
.encrypt_device(path, Some(&header_path), passphrase.clone())
236+
.unwrap();
237+
238+
luks2_formatter
239+
.open_device(path, Some(&header_path), NAME, passphrase)
240+
.unwrap();
241+
242+
luks2_formatter.close_device(NAME).unwrap();
243+
}
244+
245+
#[test]
246+
#[serial]
247+
fn encrypt_open_device_integrity_with_header() {
248+
let mut bin_file = tempfile::NamedTempFile::new().unwrap();
249+
bin_file
250+
.as_file_mut()
251+
.write_all(&vec![0; 20 * 1024 * 1024])
252+
.unwrap();
253+
let path = bin_file.path();
254+
let header_path = prepare_luks_header_file(None, path).unwrap();
255+
let _guard = RemoveHeaderOnDrop(header_path.clone());
256+
257+
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
258+
let luks2_formatter = Luks2Formatter { integrity: true };
259+
luks2_formatter
260+
.encrypt_device(path, Some(&header_path), passphrase.clone())
261+
.unwrap();
262+
263+
luks2_formatter
264+
.open_device(path, Some(&header_path), NAME, passphrase)
265+
.unwrap();
266+
267+
luks2_formatter.close_device(NAME).unwrap();
268+
}
269+
270+
#[test]
271+
#[serial]
272+
fn encrypt_with_existing_header_file() {
273+
let mut bin_file = tempfile::NamedTempFile::new().unwrap();
274+
bin_file
275+
.as_file_mut()
276+
.write_all(&vec![0; 20 * 1024 * 1024])
277+
.unwrap();
278+
let path = bin_file.path();
279+
let header_path = prepare_luks_header_file(None, path).unwrap();
280+
let _guard = RemoveHeaderOnDrop(header_path.clone());
281+
282+
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
283+
let luks2_formatter = Luks2Formatter { integrity: false };
284+
let result = luks2_formatter.encrypt_device(path, Some(&header_path), passphrase);
285+
assert!(result.is_ok());
286+
}
287+
288+
#[test]
289+
#[serial]
290+
fn open_device_missing_header_file_fails() {
291+
let mut bin_file = tempfile::NamedTempFile::new().unwrap();
292+
bin_file
293+
.as_file_mut()
294+
.write_all(&vec![0; 20 * 1024 * 1024])
295+
.unwrap();
296+
let path = bin_file.path();
297+
let header_path = prepare_luks_header_file(None, path).unwrap();
298+
let _guard = RemoveHeaderOnDrop(header_path.clone());
299+
300+
let passphrase = Zeroizing::new(TEST_PASSPHRASE.to_vec());
301+
let luks2_formatter = Luks2Formatter { integrity: false };
302+
luks2_formatter
303+
.encrypt_device(path, Some(&header_path), passphrase.clone())
304+
.unwrap();
305+
306+
std::fs::remove_file(&header_path).unwrap();
307+
308+
let result = luks2_formatter.open_device(path, Some(&header_path), NAME, passphrase);
309+
assert!(result.is_err());
310+
}
311+
189312
/// This test can be used to clean useless devices under /dev/mapper/
190313
#[ignore]
191314
#[test]

confidential-data-hub/hub/src/storage/volume_type/blockdevice/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub enum BlockDeviceError {
2929
#[error("The scheme of the key uri should be `kbs`, `file` or `sealed`")]
3030
IllegalKeyScheme,
3131

32+
#[error("Providing a key is not supported when formatting empty LUKS2 devices with detached headers")]
33+
KeyNotAllowedForEphemeralLuksHeader,
34+
3235
#[error("Failed to get key: {source}")]
3336
GetKeyFailed {
3437
#[source]

0 commit comments

Comments
 (0)