Skip to main content

A Practical Guide to Passing Secrets to VMs

The central question is: how do you manage secrets like SSH keys, API keys, and passwords for disposable VMs? 🤷‍♂️

Let's establish some ground rules for this scenario. Suppose I want to pass an API key to the VM chimera, which is run by the chimera-runner user on the host. My security requirements are:

  • On the host, only root and chimera-runner should have access to the secrets.

  • In VM chimera, only root and relevant service users should have access to the secrets.

  • No one from other VMs, including their root users, should have access to VM chimera's secrets.

  • The guest VMs themselves are not trusted.

The bootc documentation on this topic is very informative.

On a high level, there are a few ways to achieve this.


1. OEM Strings / Firmware

QEMU can pass data to a VM via SMBIOS OEM strings (-smbios) or firmware configuration (-fw_cfg). Notably, both methods are supported by systemd-creds using special keys.

This approach is practical for small pieces of data, like an individual password or an encryption key. It's not a new idea and was discussed years ago.

However, there are a few caveats:

  • Size Limits: The QEMU manpage states that the total size of all SMBIOS tables is limited to 65535 bytes. While not explicitly defined, fw_cfg is also intended for small amounts of data.

  • Key Length: The maximum length of a fw_cfg key name is 55 characters. If you use it with systemd-creds, a special prefix is required, making the available space for your key name even shorter.

  • Security Risk: If you pass data as a string (e.g. -fw_cfg string=secrets), the secret becomes part of the QEMU process's command line, which is visible to all users on the host!

  • Bugs: You can provide a file to SMBIOS using -smbios path=filename, which avoids exposing the secret on the command line. However, this feature is affected by a bug that is still present in Debian Bookworm.

  • Accessibility: Data passed via firmware appears in the guest's /sys/firmware directory, which cannot be mounted as a typical block device.

For these reasons, I generally use -fw_cfg file=filepath for passing small secrets and leverage systemd-creds within the guest whenever possible.

I might switch to -smbios path=filename later, when the bug is fixed.

2. Network Share

This is probably the most common way of sharing files between a host and its guests. The host sets up a file-sharing service, and the guest connects to it to access the files.

  • Pros: Changes on the host are reflected in the guest immediately, although this might not be important for static secrets.

  • Cons: The server on the host needs to authenticate clients (the VMs) to ensure one VM cannot access another's secrets. The guest VM also needs to set proper file permissions internally.

Common options include:

  • QEMU's built-in SMB server: Easy to set up, but the guest share is accessible to everyone in the guest. A non-root user can access the content using userspace tools.

  • SMB Server: For each VM, you must create a dedicated user/password pair. This password must then be passed securely to the VM (using another method).

  • NFS Server: NFS authenticates clients by IP address and trusts the client's root user. This is risky because a compromised VM could spoof its IP address. An extra authentication layer, like WireGuard, might be necessary.

  • SSHFS: Each VM needs a dedicated SSH key pair stored securely. The host can use a standard SSH server configuration.

While these options are viable, I find them less than ideal. They require significant effort to set up correctly, and the servers add extra maintenance overhead. Furthermore, SSHFS appears to be no longer actively maintained.

3. Filesystem Passthrough

This method is similar to a network share but is optimized for VM environments. Instead of the network stack, it uses a more direct channel to expose a host filesystem to the guest, which is generally faster.

Common options are:

  • 9pfs: Easy to set up, but my guest OS (centos-bootc:stream9) lacks the necessary kernel support, and I prefer not to compile custom kernels.

  • virtiofsd: This is a popular and high-performance method, but it requires shared memory between the host and guest, plus an extra daemon running on the host.

Unfortunately, neither of these options worked for my specific setup.

4. Credential Fetcher

With this approach, the guest fetches credentials as needed, typically during boot. This can be implemented easily in the guest, for example, by using scp to copy secrets into a ramfs mount.

However, this requires setting up a server (e.g. SSH) on the host, which I wanted to avoid due to the added complexity and maintenance.

5. Embed in Container Image

It's possible to generate and embed secrets directly into the bootc container image during the build process.

An interesting variation is to encrypt the secrets, embed the encrypted data in the image, and pass the decryption key to the VM using another method (like an OEM string). You could use systemd-creds for this and even bind decryption to a virtual TPM simulated by QEMU. However, as noted in this discussion, this might not be the intended use case for systemd-creds.

While this works in theory, it doesn't offer significant benefits for my workflow and feels tedious to implement.

6. Disk Images

Any file can be used as a raw disk image for the VM, the guest just directly read the device for the data, without mounting a filesystem. Note that there may be issues if the file size is not aligned with the block size.

A more practical approach is to create a proper disk image containing the secrets and attach it to the VM. Any filesystem supported by the guest OS will work, but the ideal choice would have these characteristics:

  • The disk image can be created without root privileges on the host.

  • The filesystem is optimized for read-only data.

I found three practical options:

  • EROFS: A modern, read-only filesystem that supports volume labels.

  • SquashFS: mkfs.squashfs is very flexible; its "pseudo file" feature lets you create files directly from command output.

  • ISO 9660 (CD/DVD images): Universally supported but less flexible.

I plan to use EROFS mainly because it supports volume labels. I cannot guarantee that the disk order will be consistent across all VMs. Therefore, the guest needs a reliable way to identify the secrets disk, and mounting by label is the easiest solution.

Conclusion

After evaluating the options, I settled on the following combination:

  • OEM strings for small, individual secrets (like a decryption key).

  • Read-only EROFS disk images for larger sets of secret files.

  • QEMU's built-in SMB server for sharing encrypted data blobs, where the decryption key is passed separately via an OEM string.

Keep in mind that this solution is tailored to my specific use case, which has the following constraints:

  • The guest VMs are not fully trusted.

  • I want to minimize setting up and maintaining complex services on the host.

  • Automating the setup via scripting before a VM starts is acceptable and even preferable.

  • The solution must scale easily to many different VMs.

Comments

Popular posts from this blog

Determine Perspective Lines With Off-page Vanishing Point

In perspective drawing, a vanishing point represents a group of parallel lines, in other words, a direction. For any point on the paper, if we want a line towards the same direction (in the 3d space), we simply draw a line through it and the vanishing point. But sometimes the vanishing point is too far away, such that it is outside the paper/canvas. In this example, we have a point P and two perspective lines L1 and L2. The vanishing point VP is naturally the intersection of L1 and L2. The task is to draw a line through P and VP, without having VP on the paper. I am aware of a few traditional solutions: 1. Use extra pieces of paper such that we can extend L1 and L2 until we see VP. 2. Draw everything in a smaller scale, such that we can see both P and VP on the paper. Draw the line and scale everything back. 3. Draw a perspective grid using the Brewer Method. #1 and #2 might be quite practical. #3 may not guarantee a solution, unless we can measure distances/p...

Qubes OS: First Impressions

A few days ago, while browsing security topics online, Qubes OS surfaced—whether via YouTube recommendations or search results, I can't recall precisely. Intrigued by its unique approach to security through compartmentalization, I delved into the documentation and watched some demos. My interest was piqued enough that I felt compelled to install it and give it a try firsthand. My overall first impression of Qubes OS is highly positive. Had I discovered it earlier, I might have reconsidered starting my hardware password manager project. Conceptually, Qubes OS is not much different from running a bunch of virtual machines simultaneously. However, its brilliance lies in the seamless desktop integration and the well-designed template system, making it far more user-friendly than a manual VM setup. I was particularly impressed by the concept of disposable VMs for temporary tasks and the clear separation of critical functions like networking (sys-net) and USB handling (sys-usb) into the...

Exploring Immutable Distros and Declarative Management

My current server setup, based on Debian Stable and Docker, has served me reliably for years. It's stable, familiar, and gets the job done. However, an intriguing article I revisited recently about Fedora CoreOS, rpm-ostree, and OSTree native containers sparked my curiosity and sent me down a rabbit hole exploring alternative approaches to system management. Could there be a better way? Core Goals & Requirements Before diving into new technologies, I wanted to define what "better" means for my use case: The base operating system must update automatically and reliably. Hosted services (applications) should be updatable either automatically or manually, depending on the service. Configuration and data files need to be easy to modify, and crucially, automatically tracked and backed up. Current Setup: Debian Stable + Docker My current infrastructure consists of several servers, all running Debian Stable. System Updates are andled automatically via unattended-upgrades. Se...