9.8 KiB
Networking ReactOS in v86
The browser cannot open arbitrary TCP, UDP, or raw Ethernet sockets. v86 works around that limitation by emulating a network card in the guest and forwarding its traffic to a proxy over WebSocket.
For ReactOS, use v86's ne2k device. It emulates an NE2000/RTL8390-compatible
PCI adapter, for which ReactOS has a suitable driver. Do not select the v86
VirtIO network device unless a compatible ReactOS driver has been installed in
the image first.
There are two useful proxy configurations:
| Backend | Guest traffic | Server requirements | Recommended use |
|---|---|---|---|
| WISP | Outbound TCP; synthetic DHCP, DNS, NTP, ping, and related services | Unprivileged Node.js service behind a TLS reverse proxy | Web browsing, HTTP(S), and other outbound TCP clients |
WebSocket Ethernet (wsproxy) |
Raw Ethernet, including TCP and UDP | TAP device, DHCP, DNS, forwarding, NAT, and usually a privileged container | Applications that require arbitrary UDP or fuller network emulation |
Important WISP UDP limitation
The WISP protocol and current wisp-js server support both TCP and UDP stream
types. However, the WISP backend in the v86 version used by this page does not
forward arbitrary guest UDP streams. It forwards TCP and locally emulates the
small set of UDP-based services required for normal configuration, including
DHCP, DNS through DNS-over-HTTPS, and NTP.
Consequently:
- DNS, DHCP, ping, NTP, HTTP, HTTPS, and outbound TCP applications can work through WISP.
- A ReactOS program sending arbitrary UDP datagrams to an Internet server will not work through the v86 WISP backend.
- Use the raw WebSocket Ethernet backend described later if arbitrary TCP and UDP are both required.
Option 1: WISP for outbound TCP
WISP is the simplest and safest starting point. The proxy does not need root, TAP devices, IP forwarding, or direct access to Ethernet frames.
1. Install the WISP server
Use the maintained @mercuryworkshop/wisp-js package. The older
wisp-server-node package is archived and explicitly deprecated for security
and stability reasons.
On a server with a supported Node.js and npm installation:
sudo install -d -o "$USER" -g "$USER" /opt/r2-wisp
cd /opt/r2-wisp
npm init -y
npm install @mercuryworkshop/wisp-js
For an initial test, listen only on localhost:
npx wisp-js-server \
--host 127.0.0.1 \
--port 5001 \
--logging INFO \
--options '{"allow_private_ips":false,"allow_loopback_ips":false,"allow_direct_ip":false,"stream_limit_total":64,"stream_limit_per_host":8,"port_whitelist":[80,443]}'
This example intentionally permits only web ports. Adjust port_whitelist if
the guest needs other outbound TCP services. Avoid an unrestricted public
relay: it can be abused as an anonymous proxy and can consume arbitrary amounts
of bandwidth and file descriptors.
For a long-running installation, create a dedicated unprivileged account and a service managed by the host's init system. Pin and review the npm package version when deploying rather than updating production automatically.
2. Put the WISP server behind TLS
A page served over HTTPS must connect to a secure WebSocket endpoint. In v86,
the secure WISP URL uses the wisps:// scheme. Terminate TLS in nginx, Caddy, or
another reverse proxy and forward WebSocket upgrades to 127.0.0.1:5001.
Example nginx location inside an HTTPS virtual host:
location /wisp/ {
proxy_pass http://127.0.0.1:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1d;
proxy_send_timeout 1d;
}
The public endpoint in this example is:
wisps://reactos.example.org/wisp/
Use wisp:// only for local HTTP development. Browsers will block an insecure
WebSocket connection made by an HTTPS page as mixed content.
3. Configure v86 with NE2000 and WISP
Add a net_device member to the object passed to new V86(...):
net_device: {
type: "ne2k",
relay_url: "wisps://reactos.example.org/wisp/",
router_ip: "192.168.86.1",
vm_ip: "192.168.86.100",
masquerade: true,
dns_method: "doh",
doh_server: "cloudflare-dns.com"
},
The important settings are:
type: "ne2k"exposes the compatible virtual network adapter to ReactOS.relay_urlselects WISP and identifies the proxy endpoint.vm_ipis the address offered to ReactOS by v86's built-in DHCP service.router_ipis the synthetic gateway presented to ReactOS.masquerademakes destinations outside the virtual subnet reachable.dns_method: "doh"lets v86 resolve guest DNS requests using the browser's HTTPS facilities, because general guest UDP is not forwarded by WISP.
If the page and relay share a hostname, nginx can serve the static page and
proxy /wisp/ from the same HTTPS virtual host.
4. Configure and test ReactOS
ReactOS should detect an NE2000/RTL8029-style PCI adapter. If it asks for a driver, let its hardware wizard search the installed ReactOS driver set. Then configure the adapter for automatic addressing (DHCP).
From a ReactOS command prompt:
ipconfig /all
ping 192.168.86.1
ping example.com
Expected results include an address such as 192.168.86.100, a gateway and DNS
server of 192.168.86.1, and successful name resolution. A successful ping
checks v86's synthetic ICMP handling; it does not prove arbitrary ICMP access to
the remote host.
Test actual TCP with an HTTP client or browser. HTTPS connectivity can still fail because the browser and TLS libraries shipped with ReactOS may not support the algorithms or certificate chains used by modern sites. That is separate from the v86 network connection.
When debugging, inspect all three layers:
- ReactOS Device Manager: the NE2000 adapter must exist without an error icon.
- ReactOS
ipconfig /all: DHCP must assign the virtual address and gateway. - Browser developer tools and WISP logs: the WebSocket upgrade must succeed and the requested destination must pass the server restrictions.
Common browser-side failures are an invalid certificate, an HTTPS page using a
wisp:// rather than wisps:// URL, a reverse proxy that does not forward the
WebSocket Upgrade header, or a CDN that imposes a short WebSocket timeout.
Option 2: raw Ethernet for TCP and UDP
Use v86's wsproxy backend when ReactOS applications need arbitrary UDP. This
backend forwards complete Ethernet frames over WebSocket. The relay host must
provide a TAP interface, DHCP, DNS, routing, and NAT.
The reference websockproxy Docker image provides these pieces for testing:
docker run --detach \
--name r2-reactos-relay \
--privileged \
--publish 127.0.0.1:8080:80 \
benjamincburns/jor1k-relay:latest
The container is privileged because it creates a TAP interface and configures IP forwarding, iptables masquerading, and dnsmasq. Binding it to localhost keeps the unencrypted relay port from being exposed directly. Put it behind an HTTPS reverse proxy with WebSocket upgrade support, as with WISP.
For example, proxy /relay/ to http://127.0.0.1:8080/, then configure v86:
net_device: {
type: "ne2k",
relay_url: "wss://reactos.example.org/relay/"
},
Notice the scheme difference:
wisps://selects the WISP transport backend.wss://selects the raw WebSocket Ethernet backend.
With raw Ethernet, addressing comes from the relay's DHCP service rather than
v86's WISP DHCP implementation. Verify it using ipconfig /all, then test TCP,
DNS, and an application that uses UDP.
Raw relay security
A raw Ethernet relay has a substantially larger security surface than WISP:
- A privileged container can affect host networking. Prefer a dedicated VM or isolated server rather than a general-purpose host.
- Block access from the relay network to the host, cloud metadata addresses, loopback, RFC1918/private networks, and administrative subnets unless access is explicitly required.
- Apply outbound firewall rules, connection limits, bandwidth quotas, and log rotation.
- A simple shared TAP relay can place unrelated browser guests on the same layer-2 network. They may be able to scan or attack one another. Use a relay that isolates clients, or run separate relay instances for untrusted users.
- Do not expose the relay's plain
ws://listener to the Internet. Publish only the TLS reverse-proxy endpoint. - The reference container is useful for validation, but its dependencies, privileges, firewall behavior, and maintenance status must be reviewed before production deployment.
The v86 networking documentation also lists alternatives such as wsnic and
RootlessRelay, which may offer better client isolation or avoid TUN/TAP
privileges. Evaluate them against the required TCP, UDP, DHCP, NAT, and security
properties before choosing a production relay.
Choosing a backend
Start with WISP when the goal is to browse sites, download files, or connect to outbound TCP services. It is simpler to deploy and easier to restrict.
Choose raw WebSocket Ethernet only when an application genuinely needs general UDP, broadcast/multicast behavior, guest-to-guest networking, or protocols that the WISP backend cannot translate. The extra behavior comes with significantly more operational and security work.