errcheck v1.30.0 reports a bare receive from an error channel as an
unchecked error. Each receive it flagged waits for a process or server
to exit after Lima has stopped or killed it, so the error adds nothing;
discard it with "_ = <-ch".
But staticcheck's S1005 flags "_ = <-ch" as an unnecessary blank
assignment, so each spelling of the discard fails one of the two
linters. Disable S1005. It also flags blank identifiers in range loops,
such as "for _ = range", but gofmt's simplify step and revive's range
rule still catch those.
Signed-off-by: Jan Dubois <jan.dubois@suse.com>
The default user-mode network maps the gateway IP to the host's 127.0.0.1,
so the guest can reach every host service via `host.lima.internal`,
including ones bound only to the loopback. That is useful for dev VMs but
exposes local databases and dev servers to a guest that runs untrusted
code, such as a CI runner.
Add `networkOpts.hostLoopback` to lima.yaml (default true, unchanged),
in a new `networkOpts` group for network options. When false, the
in-process gvisor-tap-vsock gateway maps the gateway IP and
0.0.0.0 to 0.0.0.1 instead of 127.0.0.1. The host has no route to 0.0.0.1,
so connections to `host.lima.internal` are refused at once, rather than
going to the host's LAN, and UDP to 0.0.0.0 cannot reach the host's
loopback either. DNS, outbound access and SSH are unaffected.
Only `vmType: vz` supports it. `qemu`, `krunkit`, `wsl2` and `hcs` reject
`false` rather than ignore it (krunkit's default host resolver sends DNS
through `host.lima.internal`, so it needs separate work), and so does a
`user-v2` network, which is a daemon shared by instances. The refusals
are in each driver's Validate, not in validateConfig, which Configure also
runs on deletion.
A proxy on the host's loopback, whether it comes from the host's network
settings, `env`, or `propagateProxyEnv`, is rewritten to the gateway
address, which `networkOpts.hostLoopback: false` makes unreachable. Lima
warns when that happens, and the docs say so.
A BATS test, run in the vz CI job, checks end to end that a host
loopback service is refused at once with `false` and reachable after
switching to `true`, that DNS keeps working, and that a `user-v2`
network is rejected.
Refs #5574
Assisted-by: Claude Code
Signed-off-by: Rana Md Ali Ahsan <rana@codesamplez.com>
These options are specific to the QEMU driver, so validate them in the
driver's Validate path rather than in limactl's generic limayaml.Validate.
The values are interpolated verbatim into the host QEMU `-virtfs`
argument (securityModel) and the guest 9p mount options
(protocolVersion, cache), so a value containing a comma injects extra
options; reject anything outside the sets documented in
docs/config/mount.md.
Signed-off-by: nashit hayyat <nashit@bugqore.com>
The host agent waits for the VZ driver to report the SSH port ready
before it reads the driver's errors. The driver reported ready only
when its startup goroutine returned, and an error send blocked the
driver's goroutines until the host agent read it, so each waited for
the other. A failed usernet setup, a pidfile error, or the VM stopping
during boot all hung `limactl start` until its 10-minute timeout, and
the usernet error never reached the log.
Release the host agent's wait before sending any error, so it reads
the error and stops the instance. An existing pidfile sent a nil
error; send a real error, and skip the SSH and usernet setup after
any pidfile error.
Signed-off-by: Jan Dubois <jan.dubois@suse.com>
`limactl stop` waits until the instance reports Stopped, and each status
check of a running WSL2 instance looks up its SSH address with `wsl -d`.
WSL stops a distro 15 seconds (by default) after the last `wsl` command
in it exits, and these checks come every half second. So when another
program launches the distro right after the host agent terminates it,
the checks keep it running, and the stop times out after three minutes.
A `wsl` command without `-d` runs in the default distro, which is Lima's
when no other distro is installed. GitHub's Windows runners issue one
from time to time (`wsl -u root -e ls`, from their hosted-compute-agent).
A launch that arrives during a terminate waits, then runs right after the
distro stops. Since WSL 3.0.1 a terminate takes ten seconds instead of
under one, which widens that window; older versions hang the same way
when the launch arrives in their shorter one.
Look up the address only while a host agent runs. An instance without
one keeps the default 127.0.0.1.
CI hits this race only now and then, so the plain-host WSL2 job
forces it on every run. Its smoke test launches the default distro with
`wsl -u root -e ls` while `limactl stop` terminates the instance, only
to show that the stop completes anyway.
Signed-off-by: Jan Dubois <jan.dubois@suse.com>
libkrun has no snapshot API, so copy the boot disk with clonefile only while the instance is stopped and refuse a full copy when copy-on-write is unavailable.
Fixes: #5533
Signed-off-by: Chengyu Zhu <hudsonzhu@tencent.com>
This fixes the case where WSL is installed, but no distributions are
installed at all. In that case, newer versions of WSL no longer prints the
error code `Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND` (or indeed, any error code
at all).
Instead of trying to parse error messages, use the WSL API to check if the
distribution exists. However, the API is very limited and does not allow
us to check the state of the distribution in the case it _does_ exist, so
we still need to parse the text for that.
If WSL is not installed, using the WSL API causes a command prompt window
to appear with a prompt to install WSL. To avoid showing that, try to
detect whether WSL is installed by opening its registry key.
Signed-off-by: Mark Yen <mark.yen@suse.com>
Signed-off-by: Jan Dubois <jan.dubois@suse.com>
getSSHAddress reads the instance address from commands run inside the
guest; only the fallback probe checked that the output was an IP, the
first two returned it verbatim. The pipeline in the first probe exits 0
even when hostname does not support -I, so its usage text could become
inst.SSHAddress and end up on the ssh/scp/rsync command line instead of
falling through to the next probe.
Route all three probes through one helper that accepts only global
unicast addresses (private and ULA ranges included), so unspecified,
loopback, link-local and multicast values fall through the same way
127.0.1.1 already did in the fallback.
Signed-off-by: Sabith Saheb <shabi7204192361@gmail.com>
Attach explicitly allowed host disks through a dedicated root-owned helper,
passing validated file descriptors to unprivileged VZ over an authenticated
Unix socket. Scope sudoers grants to the invoking user and exact device paths,
reject unsafe helper paths and socket symlinks, authenticate both peers, and
validate the received device identity.
Retain descriptors per VM while preserving process-lifetime network wrappers
used by krunkit. Lock whole host disks across instances sharing LIMA_HOME,
including raw/block aliases and partitions. Reject duplicate configuration
entries and external VZ drivers at runtime.
Compose network and block-device sudoers fragments through pkg/sudoers, match
complete generated lines, and warn when regeneration removes existing grants.
Keep integration-test grants in temporary fragments and revoke them before
detaching the test RAM disk. Add security, lifecycle, and real-disk regression
coverage and document installation requirements and remaining grant limits.
Signed-off-by: Nick Sweeting <git@sweeting.me>
When creating a macOS guest, the vz driver loads the IPSW as a
VZMacOSRestoreImage and reads only the hardware model from it, then
removes the IPSW after installation. The image also carries the OS
version and build number, which are otherwise lost at that point.
Log both and persist them next to vz-hwmodel as vz-guest-os-version and
vz-guest-os-build-version, written once when the hardware model is
extracted. store.ReadGuestOSVersion reads them back (empty when unknown,
e.g. for instances created before this change), so that host-side steps
such as disk patching and cidata generation can select behavior by guest
macOS version. For macOS guests, cidata also exposes the values to
provisioning scripts as LIMA_CIDATA_GUEST_OS_VERSION and
LIMA_CIDATA_GUEST_OS_BUILD_VERSION, emitted empty when unknown like the
other optional LIMA_CIDATA_* variables.
The values describe the restore image, i.e. the guest at first boot;
they do not track later OS updates.
Signed-off-by: Blake Garner <blake@netjibbing.com>
Add support for `nestedVirtualization` in the QEMU driver:
- On macOS (aarch64, HVF): requires QEMU 11.1.0 or later and macOS 15 or
later (Apple M3 or later). QEMU 11.1 added nested virtualization support
to HVF for the aarch64 `virt` machine using `hv_vm_config_set_el2_enabled`
(https://wiki.qemu.org/ChangeLog/11.1). Enabled by appending
`virtualization=on` to the `-machine virt` option.
- On Linux (aarch64, KVM): requires QEMU 10.1.0 or later and a host kernel
(6.16 or later) booted with `kvm-arm.mode=nested`. Enabled by appending
`virtualization=on` to the `-machine virt` option.
- On Linux (x86_64, KVM): verifies that nested virtualization is enabled
in the host KVM module (`kvm_intel` or `kvm_amd` parameter `nested`) and
that CPU type `host` is used.
For unsupported architectures or accelerators, a warning is logged and
the field is ignored.
Tested on Apple M4 / macOS 26.5 with QEMU 11.1.0: the guest kernel
initializes KVM in nVHE mode and a nested KVM VM boots EDK2 to the
UEFI shell.
Fix#5419
Signed-off-by: Sharan Raj T <sharanrajtm@gmail.com>
`LimaVzDriver.Stop()` returned an error without stopping the VM when
`CanRequestStop()` reported false, and it never removed the driver PID file.
When launchd sends SIGTERM to the host agent of a LaunchDaemon instance, the
host agent exits with `vz: CanRequestStop is not supported` and leaves `vz.pid`
behind. Within the same boot this is harmless, because `ReadPIDFile` removes
the PID file of a dead process, but after a reboot the recorded PID may have
been reused by an unrelated process. The instance then shows up as `Broken`
with "vz driver is running but host agent is not", and launchd with
`KeepAlive` crash-loops `limactl start`.
Ask the guest to shut down over SSH whenever the request could not be made
through the Virtualization framework, not only for the macOS guests that ignore
`RequestStop()`. The VM is never stopped forcibly with `VirtualMachine.Stop()`:
that does not give the guest a chance to stop cleanly, and may corrupt the disk.
Always remove the PID file when the driver stops, as the qemu and krunkit
drivers already do.
Related to #5087
Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
The vz driver only creates a host output stream, so a guest with
`audio.device: vz` gets a speaker and no microphone: `/dev/snd` has
`pcmC0D0p` but no capture device, and nothing in the guest can record.
Add an opt-in `audio.microphone` boolean, default false. When it is set,
the driver also creates a host input stream and passes both streams to
SetStreams. It is opt-in because an input stream makes macOS ask for
microphone permission on behalf of the process that runs the VM, and
enabling sound alone should not trigger that prompt.
Code-Hex/vz exposes the input stream as
NewVirtioSoundDeviceHostInputStreamConfiguration. It requires macOS 12,
the same as the output stream the driver already creates, so this adds
no new platform requirement.
The key warns as experimental, and setting it without `audio.device`
warns and is ignored, like an `hda` interface request. The QEMU, WSL2,
and krunkit drivers report ignored audio fields via
reflectutil.UnknownNonEmptyFields in validateConfig; FillDefault
materializes every audio field, so zero-value defaults are normalized
back to nil before the check. WSL2's Start()-time audio.interface
warning is removed in favor of that check, and "Audio" joins its
knownYamlProperties so audio settings are reported once. The
--audio-microphone flag and the experimental-features docs entry follow
the audio.interface commit (a9b8e59a).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Vladimir Starkov <iamstarkov@gmail.com>
Previously it was only implemented for VZ.
Convert PPM into the expected output formats.
Assisted-by: LLM
Signed-off-by: Anders F Björklund <anders.f.bjorklund@gmail.com>
macOS VZ guests boot straight into the Setup Assistant ("mini-buddy")
first-login wizard and sit there until someone clicks through it in
the VM's GUI window -- Accessibility, Apple Pay, iCloud, Screen Time,
Siri, Touch ID, Terms of Address, and more, plus a separate Software
Update opt-in/out prompt on the same first login. Neither can
currently be skipped, which blocks unattended macOS guest
provisioning.
This adds an opt-in osOpts.Darwin.suppressFirstLoginSetup boolean.
When set to true, Lima writes a setup-assistant.plist marker file directly
onto the cidata volume (like the existing boot.*/provision.*/
ssh_authorized_keys files there), and fakecloudinit writes the
per-user SetupAssistant plist and the system-wide SoftwareUpdate
preferences as root during account creation, before the account's
first login -- since the preferences exist before first login rather
than being changed afterward, macOS treats them as the guest's actual
initial state instead of resetting them. The built-in plist template
queries sw_vers from inside the guest at provisioning time rather
than hardcoding version strings, so it keeps working as the guest OS
updates; a separate osOpts.Darwin.suppressFirstLoginSetupPlist string
option can supply a custom plist instead, for anyone who wants
different defaults -- its content is written into the same marker
file in place of the built-in template.
The feature's enable/disable signal is carried entirely by that
marker file's presence, not by any key in the generated user-data --
user-data stays a plain, unmodified cloud-config document.
This also switches the guest account-existence check from a
filesystem test to an OpenDirectory (dscl) query, since other
provisioning steps can pre-create a home directory before the account
itself exists, which was making the filesystem check unreliable.
This is a Darwin-guest, VZ-driver-only feature -- QEMU macOS guests
aren't supported at all, so this doesn't affect anything there. It's
opt-in only; without the setting, guest behavior doesn't change. It
targets macOS 15/26 guests; macOS 27+ guests will have native
provisioning support via VZMacGuestProvisioningOptions once that API
is available on both host and guest, so this fills the gap until then.
Tested: go build/go vet/go test clean; gofmt clean. Verified the
osOpts.Darwin.suppressFirstLoginSetup config path end-to-end through
YAML unmarshal into DarwinOpts via a direct test against the real
limayaml.Convert call (osOpts is keyed by the capitalized OS enum
value, DARWIN = "Darwin", matching the existing osOpts.Windows
convention -- no case-normalization exists in the unmarshal/merge
path, so this must be capitalized to take effect, same as Windows).
Also ran live end-to-end boot tests against real macOS 26.6 (25G70)
and macOS 15.6.1 (24G90) VZ guests, each built with only this change
applied (no other local patches). On both, with
osOpts.Darwin.suppressFirstLoginSetup set, the guest's
com.apple.SetupAssistant.plist and system-wide SoftwareUpdate
preferences were confirmed written before first login with the
guest's actual build/version substituted in, the SetupAssistant
process never launched, and the VM booted straight to the standard
login window instead of the first-login wizard.
Fixes#5186
Co-authored-by: Akihiro Suda <suda.kyoto@gmail.com>
Assisted-by: Claude Code (Sonnet 4.7 & Sonnet 5)
Signed-off-by: Blake Garner <blake@netjibbing.com>
`ReadPIDFile` decides whether a PID file belongs to a running process by signaling
the PID. Within a single boot this is fine, because a PID file of a process that
has already exited gets removed. Across a reboot it is not: the recorded PID may
have been reused by an unrelated process, and Lima then reports the instance as
`Broken`, either with "driver is running but host agent is not", or by trying to
connect to a stale `ha.sock`. `limactl start` fails, and a LaunchDaemon with
`KeepAlive` crash-loops it.
Record the boot session ID of the host next to the PID files of an instance, and
ignore a PID file that was written during a different boot. The ID is read from
the `kern.bootsessionuuid` sysctl on macOS and from
`/proc/sys/kernel/random/boot_id` on Linux; on platforms without such an
identifier the behavior is unchanged, as it is for instances that were started by
an older version of Lima and have no marker.
The marker is shared by the PID files of the directory, so writing a PID file
during a new boot removes all of them before the marker is refreshed: refreshing
it must never make a PID file of a previous boot look current. Readers check the
boot before they read the PID, and leave the removal to the next writer, so that
a reader can never remove a PID file that another process is writing at that very
moment. The PID file itself is renamed into place.
When the boot session ID cannot be determined the PID file is not written at all,
because a marker left over from a previous boot would otherwise void a PID file
that is current. Only the platforms without support for a boot session ID fall
back to the previous behavior.
The PID of a stale PID file is never signaled, because it may belong to an
unrelated process.
`StartWithPaths` checked for `ha.pid` with `os.Stat`, which bypassed this logic;
it now uses `ReadPIDFile` as well.
Related to #5087
Related to #5075
Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Some distros publish CPU micro-architecture level variants of their
x86_64 cloud images (e.g. x86-64-v3, x86-64-v2) next to the regular one.
They still run as x86_64 in the guest, so they cannot be told apart by
arch alone.
Add an `archVariant` field on image entries, orthogonal to the existing
distro `variant` (server/minimal), plus a `--arch-variant` flag to select
it. Selection at start time filters by archVariant in parallel with
variant, so the base image stays the default and e.g. `--arch-variant v3`
picks the variant image. The arch field is left untouched (no x86_64v3).
Wire up:
- Ubuntu 26.04: amd64v3 image as archVariant v3.
- AlmaLinux 10: x86_64_v2 image as archVariant v2 (10 ships v3 by default
and v2 for older hardware).
Teach the ubuntu/almalinux update scripts to keep the vendor arch name
(amd64v3, x86_64_v2) for the stream-JSON/CHECKSUM lookup while emitting
arch: x86_64 for lima.
Relates to #5160
Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
The external driver gRPC API had almost no documented contract: call
ordering, method semantics and idempotency were undocumented, so the proto
(which is what external driver authors actually see) did not describe the flow.
Add detailed doc comments to every RPC and message in driver.proto (which also
improve the godoc for the driver.Driver interface), document the driver.Info
and DriverFeatures structs carried in InfoResponse, and add a hand-written
"Driver lifecycle and call ordering" section in drivers.md that points at the
proto and godoc.
Fixes#4896
Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Unexpected re-installation happens when we second-boot Windows VM.
To prevent this, Lima puts a marker file once the OS installation completes.
In a second boot, Lima doesn't mount Windows OS installation ISO, driver ISO and cidata ISO
if the marker file exists.
Signed-off-by: mie313 <mie313.tanaka@gmail.com>