Coming from OPNsense#
tools/opnsense-import.py turns an OPNsense config.xml into VyOS set commands plus a
report of what it did. Nothing is applied by the importer, and the commands come out in the
order a person would type them. From there you either build a seed that installs the
replacement machine unattended, or apply the commands to a router you installed by hand.
Afterwards the same tool compares the running router against the configuration it was
supposed to get.
Six steps, in order.
1. Export the configuration#
Take a config.xml off the OPNsense box. The importer only reads it; it never talks to the
router.
2. Import it#
tools/opnsense-import.py config.xml --map lan=eth0,wan=eth1 --out ./outThat writes two files into ./out: config.commands, the set lines, and report.md,
what came across and what did not.
Pass --map. FreeBSD calls a port igc0 and Linux calls it eth0, and nothing in the
file says which cable is in which socket. Give the mapping by role (lan=eth0) or by
device (igc0=eth0). Without it, ports are taken in the order the router lists them.
| Flag | What it does |
|---|---|
--map lan=eth0,wan=eth1 | interface mapping, by role or by device |
--hostname <name> | override the host name from the file |
--out <dir> | write config.commands and report.md there; without it, the commands go to standard output |
--answers <file> | also write a Wheelhouse installer answers file |
--no-firewall, --no-dhcp, --no-dns, --no-qos | skip that part of the translation |
--check-against <file> | compare a live router's commands with the ones this import expects |
What comes across#
The host name, domain, resolvers and time zone; interface addresses, static and DHCP;
static routes, resolving OPNsense gateway names to their next hop; outbound NAT and every
port forward, with host and port aliases resolved; a default-drop firewall that admits
exactly what those forwards need; the DHCP server with its pool, options, PXE handoff and
every reservation — including the resolvers and the router pinned to an individual one;
DNS forwarding with its static host entries and its wildcard records; the WAN egress
shaper; and the UPnP settings, which are agent settings here rather than router
configuration and so are written beside the commands as upnp.json.
3. Read the report before you boot anything#
report.md names what could not come across rather than dropping it silently:
FreeBSD-only plugins, NAT reflection, the old router's own firewall rules, and anything
the router had disabled.
Two entries matter to most OPNsense operators:
- UPnP is an agent setting here, not router configuration. Since 0.8.0 the agent
speaks UPnP IGD and NAT-PMP itself, and every mapping it grants is a commit to this
router's own configuration. So the importer writes the old router's settings to
upnp.jsonbeside the commands, with the WAN interface, the LAN interface and the client subnets filled in, and you apply it after the install under Services → UPnP or withPUT /api/upnp. Read the UPnP page in the product documentation first: it is off by default, and turning it on decides who on your LAN may open a port. - 1:1 NAT is made on the NAT page. The importer does not carry 1:1 mappings across. Add each one under Protect → NAT → Add 1:1 NAT after the install: Wheelhouse builds it as a pair of NAT rules, numbered ahead of the port forwards the import brought, and applies it from the Commit Bar like any other change.
4. Build a seed#
The seed is what makes the replacement machine install itself with your configuration already in it.
tools/wheelhouse-seed.py --opnsense config.xml --map lan=eth0,wan=eth1 \
--admin-password-file admin.pw --console-password-file console.pw \
--out seed.isoThat runs the importer for you, puts config.commands on the seed, copies the report on
as import-report.md, and writes a cloud-config that installs the machine and restores the
configuration. It keeps the router's own host name and time zone from the imported
configuration unless you override them.
It also decides who writes the firewall, and says so in the seed in as many words: if the imported configuration brings a default-drop ruleset, the installer's own firewall is turned off, because two writers numbering the same rules differently produces a configuration the router refuses. If nothing came across, the installer writes its default-deny ruleset instead.
--include-source puts the config.xml on the seed as opnsense-config.xml, so the
machine can check itself against the router it replaced with nothing else to fetch.
5. Install#
Write the seed to a stick or attach it as a second disk, boot the Wheelhouse image with it present, and the machine installs itself. Installing without a keyboard has the seed model in full: where the installer looks, the once-per-boot rule, and the two guards that stop a seed erasing a disk twice.
The configuration is applied after the passwords are set, so a restored firewall can be as closed as you like without locking the installer out of the agent.
6. Check the new router against the old one#
On a running machine the importer ships as wheelhouse-opnsense-import, and
--check-against compares a live router with the configuration it should have:
show configuration commands > /tmp/live
wheelhouse-opnsense-import config.xml --map lan=eth0,wan=eth1 --check-against /tmp/liveIt prints every command the import expects and the router is missing, and exits non-zero if there are any — which makes it a reasonable thing to run from a cron job after a migration until you trust it. Quoting is ignored, because a router quotes the values it prints, as are the settings VyOS writes for itself on every interface.
show configuration commands is a shell function of the operational CLI, so over SSH it
needs the wrapper:
/opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration commands > /tmp/liveOn a machine installed from a seed built with --include-source, the whole check is two
commands with nothing to fetch:
sudo mount /dev/sr1 /mnt
/opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration commands > /tmp/live
sudo wheelhouse-opnsense-import /mnt/opnsense-config.xml --map lan=eth1,wan=eth0 \
--hostname "$(hostname)" --check-against /tmp/liveWhat to expect to be different#
The vocabulary differs. Nothing on any page applies itself: an editor adds its change to one list of pending changes, where it reads as a sentence, and one Commit Bar applies the lot. What OPNsense calls a plugin is an app here, and installing one declares a container in the router's configuration tree — a diff you review, a commit you can roll back, and state that survives an image upgrade.
Two differences change how you work:
- IPv6 is configured and observed on its own page. Network → IPv6 edits addressing, router advertisements and prefix delegation, and shows v6 addresses, neighbours, routes and the ip6 firewall counters; Static routes edits v6 routes. The neighbour tables still read the IPv4 table, and the uplink model has no v6 concept. The installer does write a v6 ruleset.
- Packet capture is in the UI. Diagnostics → Capture runs a bounded
tcpdumpon the router and downloads the pcap.POST /api/captureanswers 501 only where it cannot run: an agent that is not on the router, or a router with notcpdump.
See also#
- Run the importer — the importer in detail, flag by flag.
- Read the report — every line of
report.md. - Build a seed — step 4, at length.
- Verify a migration — step 6, at length.
- Installing without a keyboard — the seed model in full.
- Every installer question — what a seed answers.
- Installing in a VM — rehearse the migration before the cutover.
- Apps are configuration — what replaces a plugin.
- 1:1 NAT — the read-only table, and why.
- The first hour — what to do once the replacement is up.