1 Extension Reference
Louis Raymond edited this page 2026-08-19 13:48:46 -04:00

The opnsense: extension

A single extra top-level key inside user-data, alongside the standard #cloud-config fields. Real cloud-init ignores unknown top-level keys, so a user-data document carrying this key is still a fully valid #cloud-config — nothing about the standard seed changes.

You only need this when the default behavior doesn't fit:

  • Default role assignment: the first NIC in network-config becomes WAN, the second LAN, any further NICs become opt1, opt2, ...
  • Default addressing: whatever each NIC's network-config subnet says (DHCP or static v4/v6).
  • Default users: ssh_authorized_keys / users: entries named root are applied to the root account; nothing else changes.

If that's all you need — e.g. examples/wan-lan-dhcp/ — skip this extension entirely.

Schema

#cloud-config
# ... standard fields (hostname, ssh_authorized_keys, users, ...) ...

opnsense:
  roles:
    # MAC (any case) -> role, overriding the default net0/net1/netN
    # positional assignment for that interface.
    "bc:24:11:00:10:01": wan
    "bc:24:11:00:10:02": lan

  vlans:
    # Each entry creates a VLAN sub-interface. A NIC referenced only as a
    # VLAN parent (no `roles` entry of its own) does not get a standalone
    # top-level role slot.
    - parent_mac: "bc:24:11:00:10:03"
      tag: 10
      role: opt1
      descr: "Guest Wi-Fi VLAN"
    - parent_mac: "bc:24:11:00:10:03"
      tag: 20
      role: opt2
      descr: "IoT VLAN"

  api_keys:
    # Generates an OPNsense API key/secret pair for the root user on first
    # boot. The secret is written once to /root/.opnseed-api-keys.txt
    # (mode 600) on the guest -- config.xml only ever gets the hash.
    - description: "ansible-provisioning"

  ipv6_track:
    # MAC -> role to track for IPv6 (e.g. LAN tracking WAN's DHCPv6-PD
    # delegation). Standard cloud-init network-config has no "track6"
    # subnet type, so this can only come from here.
    "bc:24:11:00:10:02": wan

See opnseed/src/opnseed/extension.py for the exact parsing rules and examples/wan-lan-static-vlans/ for a complete worked example (also exercised by the test suite, opnseed/tests/test_extension.py and test_merge.py::test_merge_static_vlans_example).

Delivering it

Proxmox's native Cloud-Init fields (GUI, qm set, or this repo's OpenTofu module's ip_config/user_account/dns) can't express an extension block directly — there's no field for it. Instead, author a user-data file with the standard fields plus this block, upload it as a Proxmox snippet, and point the VM's Cloud-Init config at it:

qm set <vmid> --cicustom "user=local:snippets/my-user-data.yml"

Or, in OpenTofu, set user_data_file_id on the opnsense-vm module (see terraform/examples/branch-with-vlans/) — this supersedes the module's own ip_config/user_account-derived user-data entirely; network-config (the NIC list and its own addressing) still comes from the module's network_interfaces, since the extension only ever lives in user-data.