feat: Add guest execution agent and vsock support

- Introduced a standalone guest execution agent to facilitate command execution within the guest VM over Firecracker's vsock.
- Enhanced FirecrackerClient to support vsock configuration, validating guest CID and UDS path.
- Updated configuration to include vsock settings by default.
- Refactored FirecrackerProcessManager to manage runtime sockets more effectively.
- Implemented guest agent installation and provisioning in the image store.
- Added API endpoints for executing commands in the VM and installing the guest agent.
- Improved error handling for guest execution failures and agent availability.
This commit is contained in:
2026-09-25 17:39:06 +05:30
parent dca7f78dae
commit b0bf99a6ee
25 changed files with 1893 additions and 30 deletions
+13 -5
View File
@@ -14,17 +14,17 @@ from pathlib import Path
from .config import Settings
from .errors import UvmError
from .images import GUEST_NETWORK_TEMPLATE, GuestAssets
from .images import GUEST_NETWORK_TEMPLATE, GuestAssets, guest_agent_files
from .integrity import load_manifest, require_checksum, sha256_file, verify_file, write_manifest
from .state import StateStore
from .system import CommandRunner, check_kvm, ensure_data_directories, require_root
_UBUNTU_RELEASE = "jammy"
_ROOTFS_RECIPE = "uvm-ubuntu-jammy-v3\n"
_ROOTFS_RECIPE = "uvm-ubuntu-jammy-v4\n"
_GUEST_PACKAGES = (
"ca-certificates", "openssh-server", "sudo", "systemd-sysv", "udev",
"dbus", "libpam-systemd", "iproute2", "iputils-ping", "curl",
"dbus", "libpam-systemd", "iproute2", "iputils-ping", "curl", "python3",
)
# Keep the image mountable by the older Firecracker quickstart guest kernels.
_EXT4_FEATURES = (
@@ -397,7 +397,14 @@ class Installer:
machine_id.symlink_to("../../../etc/machine-id")
for key in (root / "etc/ssh").glob("ssh_host_*_key*"):
key.unlink()
self._runner.run(("systemctl", "--root", root, "enable", "ssh.service", "systemd-networkd.service"), capture=True)
for guest_path, contents in guest_agent_files().items():
path = root / guest_path.lstrip("/")
path.parent.mkdir(parents=True, exist_ok=True)
path.parent.chmod(0o755)
path.write_bytes(contents)
path.chmod(0o644)
self._runner.run(("systemctl", "--root", root, "enable", "ssh.service", "systemd-networkd.service",
"uvm-agent.service"), capture=True)
self._runner.run(("systemctl", "--root", root, "set-default", "multi-user.target"), capture=True)
@staticmethod
@@ -406,6 +413,7 @@ class Installer:
"etc/passwd", "etc/shadow", "etc/group", "etc/sudoers", "etc/ssh/sshd_config",
"usr/bin/sudo", "usr/bin/apt-get", "usr/sbin/sshd", "usr/lib/systemd/systemd",
"var/lib/dpkg/status", "var/lib/dpkg/info/apt.list", "var/lib/dpkg/info/sudo.list",
"usr/bin/python3", "usr/lib/uvm/agent.py", "etc/systemd/system/uvm-agent.service",
):
if not (root / relative).is_file():
raise UvmError(f"built Ubuntu rootfs is missing /{relative}")
@@ -414,7 +422,7 @@ class Installer:
fields = dict(line.split(": ", 1) for line in paragraph.splitlines() if ": " in line)
if fields.get("Status") == "install ok installed":
installed.add(fields.get("Package"))
if not {"apt", "sudo", "openssh-server", "systemd-sysv"}.issubset(installed):
if not {"apt", "sudo", "openssh-server", "systemd-sysv", "python3"}.issubset(installed):
raise UvmError("built Ubuntu rootfs has incomplete apt/dpkg package metadata")
if not (root / "usr/bin/sudo").stat().st_mode & 0o4000:
raise UvmError("built Ubuntu rootfs sudo is missing its setuid permission")