xoa-proxy
Rust HTTP/HTTPS proxy that streams an XOA image to XAPI.
Repository: Vagrantin/xoa-proxy · Language: Rust · License: AGPL-3.0
Purpose
When XO Lite’s patched “Deploy XOA” button is clicked, XAPI needs to import
a .xva VM archive. XAPI calls VM.import with a URL, it expects the server
at that URL to serve the file over HTTP only and the file must be an XVA format.
With this limited support, was not statisfying for me, and originally even a blocker as I was relaying on Ronivay’s image which is a gz format that can be accessible over https only.
For that purpose, xoa-proxy is the interface in front of XAPI to support HTTP,HTTPS (including self-signed certificates) protocol and supporting as well on top of XVA, the gz format.
With this proxy it gives the flexibility to import images from different sources, even locally, and increase the security by introducing the HTTPS support.
Design
Technology choices
| Choice | Rationale |
|---|---|
| Rust | Memory safety, good for a long-lived server process in Dom0 |
Request flow
XO Lite (browser)
│ HTTP GET /image.xva
▼
xoa-proxy
│
│ HTTP/HTTPS interface for XAPI
│ Decompress Gzip format on the fly
│
▼
XAPI VM.import
│ Writes VDIs to local SR
▼
XOA VM createdHTTP vs HTTPS
xoa-proxy supports both HTTP and HTTPS (including self-signed certificates) when fetching the upstream XOA image. During download, gzip-compressed images are decompressed on the fly so that XAPI always receives a raw, uncompressed XVA stream.
The handoff to XAPI via VM.import is deliberately served over HTTP/1.0, because XAPI does not support chunked transfer encoding (an HTTP/1.1 feature), so using HTTP/1.1 framing would corrupt the import. The proxy handles this constraint internally; callers do not need to configure anything.
Code structure
xoa-proxy/
├── src/
│ └── main.rs ← HTTP server, request handler, streaming logic
├── tests/ ← Integration tests
├── .cargo/ ← Cargo config (cross-compile settings)
├── Cargo.toml ← Dependencies: hyper, tokio, tokio-util, ...
└── Cargo.lockCore streaming pattern
The heart of the proxy is converting a tokio::fs::File into a hyper response
body without loading the whole file into memory:
use tokio_util::io::ReaderStream;
use hyper::Body;
let file = tokio::fs::File::open("image.xva").await?;
let stream = ReaderStream::new(file);
let body = Body::wrap_stream(stream);
let response = Response::builder()
.header("Content-Type", "application/octet-stream")
.header("Content-Encoding", "gzip")
.body(body)?;Building
Prerequisites
- Rust toolchain (stable), installed via rustup
- For cross-compilation to Dom0:
x86_64-unknown-linux-musltarget
# Native build (for testing)
cargo build
# Cross-compile for Dom0 (static musl binary)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-muslThe resulting binary at target/x86_64-unknown-linux-musl/release/xoa-proxy
is a fully static executable with no shared library dependencies, suitable
for embedding in the XCP-ng DOM0 environment.
Running locally for development or tests
# Place a test XVA at the expected path
cp /path/to/test.xva image.xva
# Start the proxy
./target/release/xoa-proxy
# Test streaming
curl -v http://127.0.0.1:3000/image.xva -o /dev/nullConfiguration
In the current release the listen address and image path are hardcoded.
| Parameter | Current value |
|---|---|
| Listen address | 127.0.0.1:3000 |
| Proxy endpoint | image.xva |
GPG signing
The xoa-proxy RPM is signed with the RPM signing subkey of the
XCP-hl keypair. The same subkey is shared with xolite-ce: there is one
subkey for both RPMs.
The project publishes its shared public key
at the production site root. Verify the master fingerprint before importing it.
The commands below require an RPM implementation that supports signing subkeys.
XCP-ng 8.3 Dom0 uses older RPM 4.11; installed hosts instead validate signed
repository metadata with repo_gpgcheck=1. Keep the repository settings in
Updates unchanged.
To verify the RPM locally:
curl -fLO https://vagrantin.github.io/xcp-hl/xcp-ng-ce-public.asc
gpg --show-keys --with-fingerprint xcp-ng-ce-public.asc
# 2F59 1DB9 D2C1 28C4 C3D9 63F4 6DA0 0DCA 5BBA 215A
rpm --import xcp-ng-ce-public.asc
rpm --checksig xoa-proxy-*.rpmIntegration with XO Lite CE
The XO Lite patch in xolite-ce sets the deploy URL to
http://127.0.0.1:3000/image.xva. This is the address xoa-proxy listens
on when started on a XCP-hl host.
Testing
# Run unit and integration tests
cargo test
# Integration tests are in tests/
# They spin up the proxy and verify streaming behaviourContributing
To report a problem or suggest a change, open an issue on Vagrantin/xcp-hl.