Skip to content

Commit 49f02ce

Browse files
committed
feat(eif): add EIF image format support for Nitro Enclaves
Introduce a new `image-format = "eif"` variant type that produces an AWS Nitro Enclaves Image Format (kernel + cmdline + ramdisk + metadata) alongside a GPT disk image and bare kernel. - Add `tools/eif-builder` crate implementing the EIF v4 container format - Add `twoliter/embedded/rpm2eif` build script and wire it into `build.Dockerfile`, branching on `IMAGE_FORMAT=eif` to skip rpm2img's partitioning/secure-boot flags (rpm2eif owns the layout) - Extend `buildsys::manifest::ImageFormat` with `Eif` and plumb it through `DockerBuild`; reject `repack-variant` for EIF with a clear error - Teach `pubsys fetch_variant` to fetch the `.eif`, `-disk.img`, and `-kernel` artifacts published by EIF variants - Bundle `eif-builder` as a twoliter embedded tool - Add `hello-eif` test variant and manifest-parsing tests
1 parent 0188ec0 commit 49f02ce

21 files changed

Lines changed: 940 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,20 @@ members = [
2323
"tools/unplug",
2424
"tools/update-metadata",
2525
"tools/advisory-checker",
26+
"tools/eif-builder",
2627

2728
"twoliter",
28-
"twoliter/src/tool-crates/*",
29+
"twoliter/src/tool-crates/advisory-checker",
30+
"twoliter/src/tool-crates/buildsys",
31+
"twoliter/src/tool-crates/embedded-bundle",
32+
"twoliter/src/tool-crates/pcrsys",
33+
"twoliter/src/tool-crates/pipesys",
34+
"twoliter/src/tool-crates/pubsys",
35+
"twoliter/src/tool-crates/pubsys-setup",
36+
"twoliter/src/tool-crates/testsys",
37+
"twoliter/src/tool-crates/tuftool",
38+
"twoliter/src/tool-crates/unplug",
39+
"twoliter/src/tool-crates/eif-builder",
2940

3041
"tests/integration-tests",
3142
]
@@ -62,6 +73,7 @@ bottlerocket-types = { version = "0.0.16", git = "https://github.com/bottlerocke
6273
bottlerocket-variant = { version = "0.1", path = "tools/bottlerocket-variant" }
6374
buildsys = { version = "0.1", path = "tools/buildsys", lib = true, artifact = [ "bin:buildsys" ] }
6475
buildsys-config = { version = "0.1", path = "tools/buildsys-config" }
76+
eif-builder = { version = "0.1", path = "tools/eif-builder", artifact = [ "bin:eif-builder" ] }
6577
error-utils = { version = "0.1", path = "tools/error-utils" }
6678
include-env-compressed = { version = "0.1", path = "tools/include-env-compressed" }
6779
include-env-compressed-macro = { version = "0.1", path = "tools/include-env-compressed/include-env-compressed-macro" }
@@ -84,6 +96,7 @@ testsys-model = { version = "0.0.16", git = "https://github.com/bottlerocket-os/
8496
twoliter = { version = "0.20.0", path = "twoliter", artifact = [ "bin:twoliter" ] }
8597
twoliter-tool-advisory-checker = { version = "0.1", path = "twoliter/src/tool-crates/advisory-checker" }
8698
twoliter-tool-buildsys = { version = "0.1", path = "twoliter/src/tool-crates/buildsys" }
99+
twoliter-tool-eif-builder = { version = "0.1", path = "twoliter/src/tool-crates/eif-builder" }
87100
twoliter-tool-embedded-bundle = { version = "0.1", path = "twoliter/src/tool-crates/embedded-bundle" }
88101
twoliter-tool-pcrsys = { version = "0.1", path = "twoliter/src/tool-crates/pcrsys" }
89102
twoliter-tool-pipesys = { version = "0.1", path = "twoliter/src/tool-crates/pipesys" }

tests/projects/local-kit/Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/projects/local-kit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ members = [
1313
"packages/pkg-f",
1414
"packages/pkg-g",
1515
"variants/hello-ootb",
16+
"variants/hello-eif",
1617
]
1718

1819
[profile.dev]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "hello-eif"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
build = "../build.rs"
7+
# Don't rebuild crate just because of changes to README.
8+
exclude = ["README.md"]
9+
10+
[package.metadata.build-variant]
11+
included-packages = ["pkg-e"]
12+
image-format = "eif"
13+
kernel-parameters = []
14+
15+
[lib]
16+
path = "../variants.rs"
17+
18+
[build-dependencies]
19+
extra-3-kit = { path = "../../kits/extra-3-kit" }

tools/buildsys/src/builder.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,16 @@ impl VariantBuildArgs {
265265
args.build_arg("VARIANT_RUNTIME", &self.variant_runtime);
266266
args.build_arg("VERSION_ID", &self.version_image);
267267

268+
// TWOLITER_VERSION is injected by the twoliter binary into its
269+
// subprocess environment. Passing it through as a Docker build-arg
270+
// lets rpm2eif embed it into EIF metadata (BuildMetadata.BuildToolVersion).
271+
// Absence is not an error: buildsys may be invoked directly during
272+
// development, in which case the field is left empty.
273+
args.build_arg(
274+
"TWOLITER_VERSION",
275+
std::env::var("TWOLITER_VERSION").unwrap_or_default(),
276+
);
277+
268278
for image_feature in self.image_features.iter() {
269279
args.build_arg(format!("{image_feature}"), "1");
270280
}
@@ -492,6 +502,7 @@ impl DockerBuild {
492502
data_image_size_gib: data_image_size_gib.to_string(),
493503
image_features: manifest.info().image_features().unwrap_or_default(),
494504
image_format: match manifest.info().image_format() {
505+
Some(ImageFormat::Eif) => "eif",
495506
Some(ImageFormat::Raw) | None => "raw",
496507
Some(ImageFormat::Qcow2) => "qcow2",
497508
Some(ImageFormat::Vmdk) => "vmdk",
@@ -533,6 +544,13 @@ impl DockerBuild {
533544

534545
/// Create a new `DockerBuild` that can repackage a variant image.
535546
pub(crate) fn repack_variant(args: RepackVariantArgs, manifest: &Manifest) -> Result<Self> {
547+
// Repack is implemented by `img2img`, which has no EIF support. Fail
548+
// fast here so users get a clear error instead of a cryptic failure
549+
// deep inside the Dockerfile build.
550+
if matches!(manifest.info().image_format(), Some(ImageFormat::Eif)) {
551+
return error::EifRepackUnsupportedSnafu.fail();
552+
}
553+
536554
let image_layout = manifest.info().image_layout().cloned().unwrap_or_default();
537555
let ImageLayout {
538556
os_image_size_gib,
@@ -577,6 +595,7 @@ impl DockerBuild {
577595
data_image_size_gib: data_image_size_gib.to_string(),
578596
image_features: manifest.info().image_features().unwrap_or_default(),
579597
image_format: match manifest.info().image_format() {
598+
Some(ImageFormat::Eif) => "eif",
580599
Some(ImageFormat::Raw) | None => "raw",
581600
Some(ImageFormat::Qcow2) => "qcow2",
582601
Some(ImageFormat::Vmdk) => "vmdk",

tools/buildsys/src/builder/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ pub(crate) enum Error {
107107
source: semver::Error,
108108
version_str: String,
109109
},
110+
111+
#[snafu(display("Repacking is not supported for image-format = \"eif\""))]
112+
EifRepackUnsupported,
110113
}
111114

112115
pub(super) type Result<T> = std::result::Result<T, Error>;

tools/buildsys/src/manifest.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ included-packages = ["release"]
115115
```
116116
117117
`image-format` is the desired format for the built images.
118-
This can be `raw` (the default), `vmdk`, or `qcow2`.
118+
This can be `raw` (the default), `vmdk`, `qcow2`, or `eif`
119+
(AWS Nitro Enclaves Image Format).
119120
```ignore
120121
[package.metadata.build-variant]
121122
image-format = "vmdk"
@@ -721,6 +722,7 @@ pub struct BuildVariant {
721722
#[derive(Deserialize, Debug, Clone)]
722723
#[serde(rename_all = "lowercase")]
723724
pub enum ImageFormat {
725+
Eif,
724726
Qcow2,
725727
Raw,
726728
Vmdk,
@@ -1033,4 +1035,30 @@ mod test {
10331035
];
10341036
assert_eq!(kit_list, expected);
10351037
}
1038+
1039+
/// Verify that a variant declaring `image-format = "eif"` parses and
1040+
/// surfaces `ImageFormat::Eif` from `image_format()`.
1041+
#[test]
1042+
fn test_image_format_eif_variant_hello_eif() {
1043+
let manifest_path = cargo_manifest("hello-eif");
1044+
let temp_dir = TempDir::new().unwrap();
1045+
let cargo_metadata_path = cargo_metadata_path(&temp_dir);
1046+
let manifest = Manifest::new(manifest_path, cargo_metadata_path).unwrap();
1047+
assert!(matches!(
1048+
manifest.info().image_format(),
1049+
Some(ImageFormat::Eif)
1050+
));
1051+
}
1052+
1053+
/// Sanity check: the existing non-EIF variant has no `image-format` set and
1054+
/// therefore returns `None`. This guards the default-`raw` path used by
1055+
/// `builder.rs`.
1056+
#[test]
1057+
fn test_image_format_default_variant_hello_ootb() {
1058+
let manifest_path = cargo_manifest("hello-ootb");
1059+
let temp_dir = TempDir::new().unwrap();
1060+
let cargo_metadata_path = cargo_metadata_path(&temp_dir);
1061+
let manifest = Manifest::new(manifest_path, cargo_metadata_path).unwrap();
1062+
assert!(manifest.info().image_format().is_none());
1063+
}
10361064
}

tools/eif-builder/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "eif-builder"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "Apache-2.0 OR MIT"
6+
publish = false
7+
8+
[dependencies]
9+
crc32fast.workspace = true
10+
clap = { workspace = true, features = ["derive"] }
11+
serde_json.workspace = true
12+
13+
[dev-dependencies]
14+
tempfile.workspace = true
15+
16+
[lints]
17+
workspace = true

tools/eif-builder/patches/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)