Skip to content

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

ChoiceRationale
RustMemory 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 created

HTTP 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.lock

Core 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-musl target
# 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-musl

The 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/null

Configuration

In the current release the listen address and image path are hardcoded.

ParameterCurrent value
Listen address127.0.0.1:3000
Proxy endpointimage.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-*.rpm

Integration 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 behaviour

Contributing

To report a problem or suggest a change, open an issue on Vagrantin/xcp-hl.