# OpenBSD-WRT appliance

OpenBSD-WRT is an **OpenBSD 7.9 amd64 virtual-router appliance**. It provides a clear local management workflow while keeping the OpenBSD base system and native configuration files authoritative. It is not OpenWrt and deliberately does not create a UCI-style parallel configuration database.

> **Current source version:** 0.1.23. The image target is OpenBSD 7.9 amd64. Application support for a later base release is not image or hypervisor certification.

## What it provides

- Explicit first-boot selection of separate WAN and LAN interfaces.
- Native OpenBSD routing, PF, DHCP, and Unbound DNS configuration.
- A loopback-only dashboard reached through authenticated SSH port forwarding.
- Read-only status plus controlled workflows for interfaces, PF, DHCP, DNS, routes, bridges, VLANs, diagnostics, system administration, and VPN Gate.
- Native parser validation, timestamped backups, atomic replacement where applicable, and an independent **120-second confirmed-apply** rollback for network and PF changes.
- Signed application updates with retained rollback backups.

## Security and configuration model

Native OpenBSD files remain the source of truth. Examples include /etc/pf.conf, /etc/hostname.IF, /etc/dhcpd.conf, /etc/sysctl.conf, /etc/rc.conf.local, and the Unbound configuration. The dashboard reads and edits those native files through narrowly scoped helpers; it does not maintain a second configuration database.

The dashboard is intentionally bound to 127.0.0.1:8080. It has no direct LAN or WAN listener and no separate dashboard login. Access is through SSH, so the configured appliance administrator and SSH policy remain the access boundary:

~~~sh
# Run on the administrator workstation
ssh -L 8080:127.0.0.1:8080 ADMIN@APPLIANCE_LAN_ADDRESS
# Then browse: http://127.0.0.1:8080/
~~~

Do **not** expose the dashboard listener directly. Keep a local console available whenever changing routing, firewalling, addresses, interface roles, VLANs, or the default gateway.

## Appliance architecture

The base OpenBSD httpd service serves the static dashboard on loopback. Fixed-command, root-owned helper programs collect state and apply requested native changes. Browser input never becomes an arbitrary privileged command.

For the read-only status path, a root-owned collector runs periodically and atomically writes a JSON snapshot below the httpd document root. It reports interface state, routes, leases, selected service status, and PF state. The UI marks stale data and presents individual collector failures rather than treating missing data as healthy.

For configuration paths, the expected safety sequence is:

1. Read the current native configuration and prevent conflicting transactions.
2. Generate a candidate native configuration and validate it with the relevant OpenBSD parser.
3. Create a timestamped backup and show a reviewable diff or preview.
4. Atomically install the candidate and reload only the affected native service or PF ruleset.
5. Start a 120-second independent confirmation deadline maintained outside the browser session.
6. Confirm only after management access and intended network behaviour are verified; otherwise recovery restores the backup automatically.

This confirmed-apply design is critical for a router: closing the browser, losing the SSH tunnel, or a crashed web process must not leave a bad remote-access, PF, or addressing change in place.

## Installation and first boot

### Required VM shape

Use an x86-64 VM with a persistent **32 GiB** disk, at least two Ethernet NICs, a usable local console, and firmware matching the image. Start at **1 vCPU and 2 GiB RAM**; a 1 GiB standalone KVM smoke configuration has been exercised. Disable Secure Boot.

Use separate, initially disconnected WAN and isolated LAN networks. Record NIC MAC addresses before booting. Interface names and enumeration order do not establish trust; the operator maps the displayed MACs to actual virtual switches.

### Install flow

1. Verify the published release checksum before import.
2. Import the QCOW2 on KVM, or convert a **copy** for another hypervisor. Conversion changes the container format; it does not change a BIOS or UEFI boot scheme.
3. Open the local console before first boot. Sealed builds use either the display console (pc0) or COM1 (com0); historical images may be serial-only.
4. At first boot, select distinct WAN and LAN interfaces by MAC, create an administrator, configure WAN DHCP or static addressing, choose a LAN address/netmask and DNS server, then review and type APPLY.
5. First boot writes native configuration, creates host keys, and schedules a reboot. Enable only reviewed NIC links after setup.
6. Connect from the LAN using the SSH tunnel above, inspect the dashboard, reboot once, and verify interface names, MACs, addresses, and routing before trusting live traffic.

The setup process validates PF, Unbound, and optional DHCP configuration before committing. If it fails, it restores saved native files. Completed initial setup is preserved by later boots and application updates.

### Proxmox example

The repository includes a helper for a new, unused VM ID:

~~~sh
# Run as root on the intended Proxmox node
bash proxmox-import.sh --vmid UNUSED_ID --image IMAGE.qcow2 \
  --storage STORAGE --wan-bridge WAN_BRIDGE --lan-bridge ISOLATED_LAN_BRIDGE
~~~

It creates a UEFI/q35 VM with VirtIO SCSI, two VirtIO NICs, a serial console, and both NIC links disconnected. This is an example deployment path, not a fixed production topology.

## Networking, DHCP, and DNS

The appliance is intended to be a two-NIC router. WAN and LAN roles are explicitly selected at first boot, and native hostname files plus PF wan/lan macros continue to describe the configuration afterward.

### Interface and LAN settings

The dashboard inventories native interface state and supports bounded LAN/WAN settings changes. Addressing, DHCP, DNS, and PF helpers refuse conflicting transactions. A WAN role change should always be made with console recovery available, because a wrong gateway or WAN address can end the SSH session.

### DHCP

DHCP configuration stays in /etc/dhcpd.conf. The dashboard supports reservation administration, dynamic-pool boundaries, and lease durations while leaving the LAN subnet derived from native configuration. Candidate configuration is validated with dhcpd -n -c, backed up, installed atomically, and the service is restarted only after validation. An unconfirmed change rolls back automatically.

### DNS forwarding

Unbound remains the DNS authority. The dashboard limits forwarding edits to the native default-zone upstream configuration. A confirmed change should be validated from a LAN client, not only on the router itself. The helper preserves native Unbound configuration and rejects stale revisions or concurrent transactions.

### VLANs, bridges, and routing

VLANs and bridges are native OpenBSD networking constructs, not abstract dashboard-only objects. Configure them only after the physical interface role and recovery path are understood. A one-NIC router-on-a-stick bootstrap is not offered. IPv6 and multi-LAN policy require their own PF, routing, and client validation; a UI option alone does not certify real dual-stack forwarding.

## PF firewall and NAT

PF is the appliance firewall. Existing OpenBSD rules, NAT, anchors, and installer restrictions are preserved rather than silently replaced. The management UI exposes native PF visibility, counters, anchors, tables, and bounded managed-rule/table operations.

Before a PF change is loaded, the candidate must pass:

~~~sh
# Run as the normal administrator through doas
doas pfctl -nf /etc/pf.conf
~~~

After a successful confirmed apply, inspect active rules and state:

~~~sh
doas pfctl -sr
doas pfctl -sn
doas pfctl -si
doas pfctl -ss
~~~

PF changes reload the ruleset without intentionally flushing all states. The UI can display logged blocked traffic separately from state entries. Logging capacity is bounded, so absence from the displayed event list is not proof that no packets were dropped.

**Operational rule:** never blindly flush a live ruleset. Preserve established SSH management, validate the candidate, apply only after validation, and confirm before the 120-second deadline. Use the local console if a remote PF or interface transaction becomes unreachable.

## Dashboard sections

| Area | Native authority and purpose |
|---|---|
| Overview | Interface, route, lease, selected-service, and PF snapshot; stale/error reporting. |
| Interfaces | Native hostname.if files, gateway, and dependent DHCP/DNS/PF consistency. |
| DHCP | dhcpd.conf reservations, pools, and lease timing. |
| DNS | Unbound default-zone forwarding; native validation and rollback. |
| PF | Active policy visibility, counters, tables, managed rules, and limited state actions. |
| Routing / IPv6 | Native routes and bounded routing policy; validate real client paths. |
| Bridges / VLANs | Native interface topology; use explicit LAN roles and console recovery. |
| VPN Gate | Controlled OpenVPN policy selection, strict profile validation, and managed PF/routing anchors. |
| System / Updates | Host state and signed application update workflow. |

## VPN Gate

VPN Gate is optional and bounded; it does not make public VPN providers implicitly trusted. Profiles are validated strictly and public-server identity deficiencies are intentionally rejected. No user Wi-Fi policy, household client selection, or route changes merely by installing the feature.

When a selected VPN policy is enabled, the appliance uses managed native configuration, routing, and PF anchors. Policy changes follow the same validation, backup, and confirmed-apply pattern as other network changes. A disconnected or failed tunnel should preserve the applicable kill-switch behaviour for selected traffic rather than silently leaking it over WAN.

The controlled-server test demonstrated native OpenVPN authentication, selected-client forwarding, DNS redirection, PF segmentation, reconnect handling, IPv6 state handling, and deadline recovery. It does **not** certify arbitrary public VPN Gate profiles or every provider’s identity material.

## Application updates and rollback

Base-system maintenance and application updates are distinct:

- OpenBSD base patches use supported native tooling such as syspatch, where applicable to the installed release.
- Firmware uses fw_update when appropriate.
- The dashboard application uses its signed application updater and keeps a retained rollback backup.

Before an update, check appliance identity, OpenBSD release/architecture, current app version, free disk space, failed services, and any pending native transaction. Do not perform an application update while a PF or network confirmation is pending.

The updater verifies signed release metadata using existing trust, stages the package, records an application backup, and restores that backup on a failed health check. Native configuration activation is a separate, confirmed transaction. Preserve exact backup locations printed by the updater.

## Recovery

Keep console access, the release artifact, release checksum, and recorded interface/MAC mapping with the VM.

- **Failed first boot:** use the console. Setup retains native-file backups and restores them on validation failure. For a disposable test VM, discard it and re-import a pristine image.
- **Unconfirmed dashboard network/PF change:** wait for the independent 120-second timeout; it restores backed-up native files even if the browser is gone.
- **Confirmed but bad native configuration:** use console access, validate the intended backup with the relevant native parser, preserve the current file, restore the known-good native file, then reload only the relevant daemon or PF.
- **Application update failure:** use the exact application rollback backup/command created by the signed updater. Do not confuse it with a full operating-system or router-configuration rollback.
- **Lost remote access:** do not repeatedly guess changes over SSH. Use the hypervisor or physical console and inspect interfaces, routes, PF status, and active files first.

For a router rollback, treat the target, active configuration, and access path as critical data. A broader VM snapshot can be appropriate for a lab appliance, but is not a substitute for understanding current native files and persistent state.

## Compatibility status

The shipping target is OpenBSD 7.9 amd64. A supported deployment needs a persistent disk, two distinct Ethernet NICs, and a local console. The disk image is 32 GiB and should not be shrunk during format conversion. Secure Boot should be disabled.

| Platform | Status |
|---|---|
| Proxmox/KVM | Historical deployment tested; corrected source smoke-tested on standalone KVM. |
| Standalone QEMU/KVM | Local acceptance path exercised with UEFI, VirtIO storage, and mixed Intel/VirtIO NIC profiles. |
| VMware, VirtualBox, Xen HVM, bhyve | Documented driver and firmware paths; no end-to-end acceptance claim. |
| Hyper-V Gen 1 | Requires a separate BIOS/MBR build. |
| Hyper-V Gen 2 | Blocked by the OpenBSD 7.9 Hyper-V limitation; no workaround is claimed. |

Keep NIC model, MAC address, and attachment order stable after installation. Changing controller type, firmware, or NIC enumeration can require native boot/recovery work; image conversion is not a configuration migration.

## Operational checklist

Before any production change:

1. Inspect current native configuration and active state; do not rely on old notes alone.
2. Keep console or out-of-band recovery available.
3. Take a timestamped backup when the operation does not already retain one.
4. Validate native candidates with the relevant parser.
5. Apply the smallest change and reload instead of broadly restarting services where supported.
6. Test the affected path from the appropriate client or network side.
7. Confirm before the deadline, or deliberately allow automatic rollback.
8. Record changed files, backup location, verification result, and recovery command.

## Documentation map

The source repository contains detailed operational material:

- README.md — project overview and safety posture.
- appliance/BUILD.md — image provenance and reproducible build steps.
- appliance/DEPLOY.md — VM import, first boot, console selection, and first login.
- appliance/RECOVERY.md and appliance/UPGRADES.md — recovery and signed-update operation.
- appliance/COMPATIBILITY.md and appliance/PORTABILITY-RESULTS.md — hypervisor suitability and acceptance evidence.
- PF-VISIBILITY.md, INTERFACES.md, DHCP-POOL.md, DHCP-RESERVATIONS.md, DNS-FORWARDING.md, VLANS.md, BRIDGES.md, and ROUTING-IPV6.md — focused native networking workflows.
- docs/VPNGATE-EXECUTION-STATUS.md and docs/VPNGATE-OPERATIONS.md — VPN Gate scope, evidence, limitations, and recovery.

## Non-goals and cautions

- This project does not authorize changes to a household or production network merely by documenting them.
- A release image does not turn untested hypervisor combinations into supported deployments.
- A web UI does not override native validation, backups, console recovery, or the OpenBSD manual pages for the installed release.
- Never place passwords, private keys, signing keys, API tokens, VPN credentials, tunnel tokens, or personal network inventories in this wiki.
- Treat historical notes as evidence, not current machine state. Inspect the installed release, architecture, active configuration, free space, and service state before making a live change.
