OpenWrt Configurator - A CLI to manage and provision configuration to multiple OpenWrt devices

Hello,

I'd like to share a project I've been working on for the last few days called OpenWrt Configurator. It's a simple CLI that makes it easy to define and provision configuration onto multiple OpenWrt devices from a single UCI-like JSON file.

OpenWrt Configurator is a CLI tool and corresponding JSON config file which lets you specify the entire state of your network including UCI configuration, packages and firmware versions in a single UCI-like JSON config file which can be provisioned to your OpenWrt devices using OpenWrt Configurator.

$ openwrt-configurator provision ./network-config.json

The JSON config file can be conditionally composed with .if and/or .overrides keys, and implements light abstractions over device ethernet ports and Wi-Fi radios to seamlessly support configuration for multiple devices, different device models/types, as well as different device roles (Router, switch, dump-ap etc) from a single JSON config file.

Some of the main features include:

  • Store all network config for all devices in a single UCI-like JSON config file (UCI config, packages, firmware versions and more).
  • Conditionally compose your JSON file to support multiple OpenWrt devices, different device models/types, and different roles (Routers, switches and dump-ap's etc).
  • Light abstractions over ethernet ports and WiFi radios to keep multi-device configuration simple.
  • Strict config syntax validation and logical error checking for configuration to prevent invalid configuration.
  • Convert your JSON file into UCI commands for each of your OpenWrt devices.
  • Provision your JSON file to your OpenWrt devices.
  • JSON file migrations to keep your JSON file up-to-date with any UCI configuration changes/updates.
  • Build and flash sysupgrade images to your OpenWrt devices based on your JSON config file.

I built this tool as a simpler and easier-to-use replacement for a large UCI script I used to use to provision my devices, which became very cumbersome and unwieldy to maintain and manage.

If you'd like to give it a try, or want to see more details, please see the OpenWrt Configurator GitHub Repo or reply below.

If you have any thoughts or suggestions etc, please do share :slight_smile:

9 Likes

One place where the utility of this project could be extended is to older flash/ram limited devices. Essentially substitute your Configurator for luci. Even better is to use the Configurator on a workstation and provision over ssh. I had thoughts of a remote LUCI interface writing the configuration, or in your case writing the *json file.

Good work.

1 Like

One place where the utility of this project could be extended is to older flash/ram limited devices. Essentially substitute your Configurator for luci. Even better is to use the Configurator on a workstation and provision over ssh. I had thoughts of a remote LUCI interface writing the configuration, or in your case writing the *json file.

This is precisely one part of the value proposition of OpenWrt Configurator.

I've updated the README.md in the repo to hopefully better reflect all this:

OpenWrt Configurator is a CLI tool and corresponding JSON config file which lets you specify the entire state of your network including UCI configuration, packages and firmware versions in a single UCI-like JSON config file which can be provisioned to your OpenWrt devices using OpenWrt Configurator.

$ openwrt-configurator provision ./network-config.json

The JSON config file can be conditionally composed with .if and/or .overrides keys, and implements light abstractions over device ethernet ports and Wi-Fi radios to seamlessly support configuration for multiple devices, different device models/types, as well as different device roles (Router, switch, dump-ap etc) from a single JSON config file.

  "interface": [
    {
      ".if": "device.tag.role == 'router'", // Apply the pppoe interface to only the router.
      ".name": "wan",
      "device": "eth0",
      "proto": "pppoe",
      "username": "me@pppoe.com",
      "password": "123"
    },
    {
      ".name": "lan",
      "device": "br-lan.1",
      ".overrides": [
        {
          ".if": "device.tag.role == 'router'", // Apply a static ip to only the router.
          "override": {
            "proto": "static",
            "ipaddr": "10.0.0.1",
            "netmask": "255.255.0.0"
          }
        },
        {
          ".if": "device.tag.role != 'router'", // Apply dhcp to all non-router devices.
          "override": {
            "proto": "dhcp"
          }
        }
      ]
    },
  ]
}

@shep could you please let me know if the updated README.md better reflects what OpenWrt Configurator does now?

On first glance, I thought it was a utility for admins who have to configure many devices.

Looks good but so many possibilities.

What are your plans to avoid an inaccessible device due to mistakes in configuration? Would it be possible for those who have a working configuration to create a json file or pull it from a backup? Write to /tmp first and if no errors then commit the configuration?

1 Like

What are your plans to avoid an inaccessible device due to mistakes in configuration? Would it be possible for those who have a working configuration to create a json file or pull it from a backup? Write to /tmp first and if no errors then commit the configuration?

Couple of things:

  1. The tool itself does some configuration validation and checks, including syntax & value checks to make sure config keys/values are specified correctly.
  2. The tool also does some logical referential checks, to for example ensure you don't create an interface on a device which does not exist, or create a wifi network on an interface which does not exist etc.
  3. The tool also currently does a full UCI revert on any UCI error encountered while provisioning.

What I've also planned is, as you suggested, performing some sort of connect-ability test after provisioning to ensure the device is still connectible, else reverting, in much the same way LUCI does at the moment.

Have you had a look at the netjsonconfig project? Very similar concepts, and it would be handy to have schemas compatible across projects.

1 Like

An alternative solution could be - albeit it's a bit more complex - running an agent service on the configuration target devices, which would take care of actually applying the configuration. Then the whole SSH charade could be dropped in favour of an (encrypted+authenticated) RPC call, resulting in the following flow:

  1. Configuration host receives new JSON configuration
  2. Host generates per-client UCI configuration
  3. Host prioritises configuration order (e.g. you can deploy APs/switches at the same time, but shouldn't do main gateway simultaneously, as it will result in loss of local DHCP, etc.)
  4. Host connects to configuration client via RPC call (/heartbeat endpoint)
  5. Client replies to the heartbeat with an auth ok response
  6. Host sends configuration to client (/config endpoint)
  7. Client creates backup of current config, applies new one, and opens a 60 second window during which it tries to ping/RPC heartbeat/SSH the configuration host (so the service needs to be bidirectional)
  8. Based on the result, two paths are possible
    8a. If client is successful in reaching host during the 60 second window, new configuration is kept
    8b. If client is unsuccessful, new configuration is discarded and old one is restored

I could also see adding a few quality of life improvements to improve the project overall:

  1. Automatic provisioning. Upon first start, interfaces are iterated, connections checked, and DHCP client started (where appropriate). If DHCP responds with a private IP, designate device as gateway, otherwise, as switch/bridge/AP. Advertise provisioning service on local network. During provisioning, SSH keys are exchanged between configuration client (the device) and configuration host (akin to the Unifi controller, can be your laptop, your RasPi, or it can run directly on your gateway). Provisioning also assigns a random root password that is sent to the host, then all references are dropped to it. Once initial provisioning is done, host sends new config to client. Obviously this section is a very rough first draft of what needs to happen for provisioning purposes.
  2. A luci app for managing devices, config types, and whatnot, to visualise the configuration. Alternatively, a whole new web interface combined with the configuration host (or controller), which could run in e.g. a Docker container (a la Unifi OS)
  3. "Reverse" configuration - i.e. reading UCI config and creating the JSON from that (with additional user input to decide e.g. role). Useful for migration purposes (e.g. you have an existing, working UCI config that you want to manage via the configurator)
2 Likes

Yes, I have had a look at it.

I decided against following NetJson for OpenWrt Configurator because I assumed it would be less familiar to OpenWrt users. So I instead decided to make the configuration syntax more uci-like to be more familiar and easier to use for OpenWrt users.

Nevertheless, if there is demand for it, I would consider supporting both NetJson and uci-like configuration syntax. Users would then be able to chose which format they prefer.

1 Like