Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,68 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

---

## [1.0.4] — 2026-05-12

**VM runtime + cloud-init paths per-user.** Fourth patch
release of the 1.x line. `lib/vm_run.cpp` now resolves the
VM DNS share and cloud-init user-data temp directories to
the per-user base when the privops socket is detected.

### What changes

New file-local `vmBaseDir()` helper in `lib/vm_run.cpp` —
same lazy-resolve pattern as `stack.cpp` (1.0.3) and
`network_lease.cpp` (0.9.27).

| Mode | DNS share base | cloud-init temp dir |
|-------------------------------|--------------------------------------|--------------------------------------|
| Legacy (no `crated`) | `/var/run/crate/vm/<vmName>/dns/` | `/var/run/crate/cloud-init/` |
| Rootless (crated + privops) | `/var/run/crate/<uid>/vm/<vmName>/dns/` | `/var/run/crate/<uid>/cloud-init/`|

Two functions updated:

- `configureVmDns()` — DNS share dir tree (`vm/<vmName>/dns/`)
resolves under per-user root
- The cloud-init user-data writer (anonymous helper near line
340) — writes `user-data-9p-<pid>.yaml` under per-user root

### Why this matters

Before this release, two operators on a rootless host with
the VM track enabled would clobber each other's resolv.conf
9p-share AND collide on cloud-init user-data files (despite
the PID suffix). Single-tenant deployments saw no impact.

VM track is `#ifdef HAVE_LIBVIRT`-gated, so non-libvirt
builds are completely unaffected.

### 1.x backlog

Path-leak track complete with this release. Remaining 1.x
items (different shapes):

- `lib/run_net.cpp:446` direct `ifconfig -vnet` (should use
`SetIfaceUp` privops verb)
- **PfctlOps privops-wiring** — `lib/run.cpp` call sites
(1.1.0)
- Query-side privops verbs (inspect/doctor/migrate)
- Test coverage on impure modules (run.cpp 1810 / run_pure.cpp
24 lines)

### Tests

No new tests — single helper + 2 sites. Same `dnsBaseDir`
pattern from 1.0.3. Suite stays at 1303.

### Files

- `lib/vm_run.cpp` — `vmBaseDir()` helper; 2 sites routed
through it; new includes
- `cli/args.cpp` — version `crate 1.0.4`
- `CHANGELOG.md` — this entry

---

## [1.0.3] — 2026-05-12

**Stack DNS dirs per-user.** Third patch release of the 1.x
Expand Down
4 changes: 2 additions & 2 deletions cli/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ Args parseArguments(int argc, char** argv, unsigned &processed) {
args.noColor = true;
break;
} else if (strEq(argv[a], "--version")) {
std::cout << "crate 1.0.3" << std::endl;
std::cout << "crate 1.0.4" << std::endl;
exit(0);
} else if (auto argShort = isShort(argv[a])) {
switch (argShort) {
Expand All @@ -764,7 +764,7 @@ Args parseArguments(int argc, char** argv, unsigned &processed) {
args.logProgress = true;
break;
case 'V':
std::cout << "crate 1.0.3" << std::endl;
std::cout << "crate 1.0.4" << std::endl;
exit(0);
default:
err("unsupported short option '%s'", argv[a]);
Expand Down
45 changes: 37 additions & 8 deletions lib/vm_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "vm_run.h"
#include "ifconfig_ops.h"
#include "privops_client.h"
#include "runtime_paths_pure.h"
#include "util.h"
#include "err.h"
#include <rang.hpp>
Expand All @@ -25,6 +27,28 @@

namespace VmRun {

namespace {

// 1.0.4: resolve the base directory for VM runtime state (DNS share,
// cloud-init user-data temp files). Legacy single-tenant uses
// /var/run/crate; rootless mode lazy-resolves to
// /var/run/crate/<uid> when crated's privops socket is detected.
// Same pattern as stack.cpp (1.0.3) and network_lease.cpp (0.9.27).
const std::string &vmBaseDir() {
static std::string cached;
static bool computed = false;
if (!computed) {
if (!PrivOpsClient::detectSocketPath().empty())
cached = RuntimePathsPure::perUserRoot((uint32_t)::getuid());
else
cached = "/var/run/crate";
computed = true;
}
return cached;
}

} // anon

// ---------------------------------------------------------------------------
// libvirt connection (singleton)
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -225,12 +249,15 @@ void connectToSharedBridge(const std::string &vmName,

void configureVmDns(const std::string &vmName, const std::string &dnsIp) {
// Create a shared directory with resolv.conf for the VM
std::string dnsShareDir = "/var/run/crate/vm/" + vmName + "/dns";
const std::string &base = vmBaseDir();
std::string vmDir = base + "/vm";
std::string vmInstance = vmDir + "/" + vmName;
std::string dnsShareDir = vmInstance + "/dns";

// Create the directory tree
::mkdir("/var/run/crate", 0755);
::mkdir("/var/run/crate/vm", 0755);
::mkdir(("/var/run/crate/vm/" + vmName).c_str(), 0755);
::mkdir(base.c_str(), 0755);
::mkdir(vmDir.c_str(), 0755);
::mkdir(vmInstance.c_str(), 0755);
::mkdir(dnsShareDir.c_str(), 0755);

// Write resolv.conf with the stack DNS IP
Expand Down Expand Up @@ -340,12 +367,14 @@ std::string generateCloudInitFor9p(
<< " 9p " << opts << " 0 0\n";
}

// Write to a temp file under /var/run/crate
::mkdir("/var/run/crate", 0755);
::mkdir("/var/run/crate/cloud-init", 0755);
// Write to a temp file under the per-user (or legacy) base dir
const std::string &base = vmBaseDir();
std::string cloudInitDir = base + "/cloud-init";
::mkdir(base.c_str(), 0755);
::mkdir(cloudInitDir.c_str(), 0755);

// Use a unique name based on PID to avoid collisions
std::string path = "/var/run/crate/cloud-init/user-data-9p-"
std::string path = cloudInitDir + "/user-data-9p-"
+ std::to_string(::getpid()) + ".yaml";

std::ofstream ofs(path);
Expand Down
Loading