Skip to content

EC2 AMI support - #311

Draft
martinpitt wants to merge 8 commits into
mainfrom
ami
Draft

martinpitt wants to merge 8 commits into
mainfrom
ami

Conversation

@martinpitt

@martinpitt martinpitt commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

I tested this end-to-end with both the demo and the secureboot demo images, with getting the root login shell on the serial console. (Neither of these has sshd). I tested this with our downstream OS ✔️

@pothos

pothos commented Oct 15, 2026

Copy link
Copy Markdown
Member

As far as I know AWS supports VMDK images, too, instead of plain images. They are internally already sparse and thus smaller and simpler to handle on disk and when uploading. A qemu-img convert call can create VMDKs (there are variants depending on the features supported, though), could be added to image_format/convert.py.

Some years ago I noticed some disk corruption with VMDKs and reported it to AWS but I don't know what came out of this and if this is still reproducible.

@pothos

pothos commented Oct 15, 2026

Copy link
Copy Markdown
Member

Here the docs: https://docs.aws.amazon.com/vm-import/latest/userguide/prerequisites.html#vmimport-image-formats
They also list more than the stream-optimized VMDK: OVA and VHD/VHDX could be alternatives as well.

@martinpitt

Copy link
Copy Markdown
Member Author

@pothos thanks for the pointer! It's more work locally, as it has to essentially copy the existing raw image again, and the upload shouldn't make much difference. I'll test if the possibly reduced upload time makes up for the "additional local disk space + IO" overhead.

@pothos

pothos commented Oct 15, 2026

Copy link
Copy Markdown
Member

For those uploading their own AMIs from a published release it's nicer to not have to extract the large image to a local disk but directly download the vmdk from the release server and upload it as AMI because the extraction of the zstd disk is not sparse by default. It can be made sparse with a hack through piping it into cp but that's not common knowledge and no idea if that even works on Windows/MacOS.

@martinpitt

martinpitt commented Oct 15, 2026 •

Copy link
Copy Markdown
Member Author

I tried this on our EC2 dev account. Notes:

  • ✔️ VMDK conversion of the demo image takes 6.5 s for the 381 MiB image, and upload takes 86s, vs. the 112s it takes for the parallel raw block uploads.
  • 😠 restriction: Unlike a direct image import, these non-native formats require setting up an S3 bucket for uploading the vmdk there. That's extra setup for every consumer.
  • 🚫 blocker: this requires the vmimport role which our "PowerUser" dev account role doesn't have, so I can't test it.
  • 🤷‍♂️ : it doesn't benefit Azure and other clouds either, it's still cloud specific
  • 🤷‍♂️ Annoying: the VMDK isn't reproducible: qemu writes a random CID and the output file name into the descriptor. (Hackable, but since apparently nobody complained until now, accept it)

So I won't follow that route right now. Image upload is by and large an internal implementation detail, so we can tweak this later.

What I can do easily is to add a [vmdk] output format, next to [raw.zstd] and [qcow2]. If consumers want that, they can easily build it and then do the AMI upload with their own tooling.

@martinpitt
martinpitt marked this pull request as ready for review October 15, 2026 14:18
@martinpitt

Copy link
Copy Markdown
Member Author

@pothos [vmdk] output added

@martinpitt
martinpitt removed the request for review from daandemeyer October 15, 2026 16:15
@martinpitt
martinpitt marked this pull request as draft October 15, 2026 16:15
@martinpitt
martinpitt marked this pull request as ready for review October 15, 2026 16:16
@martinpitt

Copy link
Copy Markdown
Member Author

SSH is still broken. There's an /usr/share/empty.sshd dir with 0775 permissions which sshd refuses. Probably from my local 002 umask. Let me look into this next.

@martinpitt

Copy link
Copy Markdown
Member Author

SSH fixed in #314

Comment thread tools/BUCK
command_alias(
name = "aws",
args = ["aws"],
# The box has no pager, neither aws' `less` default nor the host's $PAGER; aws breaks without it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just put a pager in the box? less isn't exactly the end of the world

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do. But we still need to hardcode it then. A host's $PAGER may already be set, to nvim or delta or bat or whatever, and the box can't grow all of them. I feel like paging rather belongs to the host, not the box.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it again, IMHO hardcoding less from the box is worse than just letting the host env do it.

Publishing images to EC2 needs the `aws` CLI, both to log in and to
drive the EC2 and EBS APIs. Ship it in its own box, as it bundles an
entire boto stack and is *huge*, and it's not always needed. Relaxed
entry point so that it uses the host's AWS login. tine then never
has to handle credentials itself.

Signed-off-by: Martin Pitt <[email protected]>
A cloud image takes its SSH key and other credentials from the cloud's
instance metadata service (IMDS), which systemd imports in the initrd. Name
the packages that needs per distro, so that an initrd can ask for them
symbolically like for the other sets.

Signed-off-by: Martin Pitt <[email protected]>
A cloud instance gets its SSH key and other credentials from the instance
metadata service, and systemd imports them in the initrd if it carries the
IMDS tools and a network. Add an `imds` option to the initrd, and to a
disk's conventional one: that adds the previous commit's package set and
starts networkd for the IMDS network that systemd sets up.

All of this would be dead weight for a real-iron deployment or local VM,
so don't enable this option by default. The initrds are going to differ
anyway per cloud target due to the cloud specific modules that we are
going to add later.

Signed-off-by: Martin Pitt <[email protected]>
Deploying a disk somewhere other than a local VM, such as registering it as
an AMI, needs to know its architecture, its published name, the key firmware
must trust, and whether its initrd takes credentials from IMDS. Only the
composition knows these, so expose them in a provider rather than having
each consumer repeat them as attributes.

Signed-off-by: Martin Pitt <[email protected]>
An AMI is not file format but an EC2 object referring to a disk snapshot
and its boot parameters. Its disk is specific to EC2 too: the initrd must
take credentials from IMDS and needs EC2's network driver to reach it.

Add an `image.ami()` declaration that declares such a disk, and gathers the
information that EC2 `RegisterImage` needs: the raw disk, UEFI + IMDSv2 and
optionally a TPM.

Add an AMI for our boot-demo example.

Signed-off-by: Martin Pitt <[email protected]>
Turn the previous commit's bundle into an actual EC2 AMI image: Upload
the disk as an EBS snapshot and register it with the bundle's
parameters, through the AWS CLI with the user's login.

Only blocks holding data are uploaded, as disks commonly still have lots
of empty space on partitions. That needs an unencrypted snapshot, so
encrypted ones are not supported for now. If/when we do, we need to
avoid big holes in images first.

Signed-off-by: Martin Pitt <[email protected]>
An instance of a signed AMI booted in Secure Boot setup mode, so it ran
without enforcement until something enrolled keys, and whatever booted
first could enroll its own. Register the AMI with a UEFI variable store
that enrolls the disk's signing certificate, and add a Secure Boot AMI
with a TPM to the secureboot example.

Signed-off-by: Martin Pitt <[email protected]>
Useful for VMWare or cloud importers such as AWS VM Import (you need an
S3 bucket and the `import` role privilege).

Signed-off-by: Martin Pitt <[email protected]>
@martinpitt

Copy link
Copy Markdown
Member Author

Rebased after #312, there were some extensive conflicts after that.

Comment thread distribution/arch.bzl
"buildroot": ["base-devel"],
# Arch ships IMDS support and networkd inside systemd. systemd is already there, but a set
# cannot be empty.
"imds": ["systemd"],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just add imds to the initrd by default? It can't be that big. As long as we don't enable it by default it's fine.

Comment thread image/compose.bzl
secure_boot_key: str | None = None,
sign_expected_pcr_key: str | None = None,
initrd: str | Select | None = None,
imds: bool = False,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming we can just add imds to the initrd by default. I would really prefer not to have a boolean here to enable/disable imds. Should be a runtime decision, not a build time one.

Comment thread image/compose.bzl
"basename": provider_field(str),
"disk": provider_field(Artifact),
# Whether its initrd imports credentials from a cloud's instance metadata service.
"imds": provider_field(bool),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dislike this, should be a runtime decision, not a build time one assuming imds isn't too big to include by default.

Comment thread image/compose.bzl
# Whether its initrd imports credentials from a cloud's instance metadata service.
"imds": provider_field(bool),
# The certificate of the key its boot chain is signed with, which firmware must trust.
"secure_boot_certificate": provider_field(Artifact | None),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have a separate boot provider already that it would make sense to put this in?

I don't like having a combo of disk and bootable in one provider. It seems rather we should two distinct providers, one to cover the disk image aspect, one to cover the bootable aspect.

Comment thread image/ami.bzl
image_id = image_id or name,
initrd = initrd,
imds = initrd == None,
initrd_modules = (initrd_modules if initrd_modules != None else _INITRD_MODULES["patterns"]) + _EC2_INITRD_MODULES,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, let's see how big these are, maybe include these modules by default?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants