
April is usualy tax season for most people in Norway, and as I got some “money back on the skætt” I wound up purchasing an OpenWrt One to replace my 13-14 year old Asus router. I’ve been meaning to learn a bit more about networking in general and getting an OpenWrt router seemed like a fun project.
Last year I bought a Beryl AX from GL-Inet as I was travelling for a few weeks. It’s a qute smol travel router that runs a fork of OpenWrt. But during a recent conference it was reset and I realized I did not have a backup of any configuration files for the device. Oops!
So getting the OpenWrt One router it seemed like a nice excuse to figure out if there was any teraform providers available. And quite disappointingly there was not any out there actively maintained. So I guess we’ll have to write our own!
OpenWrt has a JSON-RPC API that allows you to set configuration files, and options, through a set of calls. So writing a thin wrapper around this API with a bit of plumbing does not seem to be too difficult.
See: HowTo: Using the JSON-RPC API
This RPC API writes files under /etc/config/
. If you write to the system
configuration file with a json document, it will parse this and write out a
corresponding /etc/config/system
file.
Basic provider setup for my Beryl AX:
terraform {
required_providers {
openwrt = {
source = "foxboron/openwrt"
}
}
}
provider "openwrt" {
user = "root"
password = "admin"
remote = "http://192.168.8.1:8080"
}
The API requires a bit of setup, but essentially it uses the root
(or admin
account?) of the device for authentication. Once that is done we can start
writing some resources.
As this was intended to be a quick hack I’ve squarely only implemented one
resource. The openwrt_system
resource.
resource "openwrt_system" "system" {
hostname = "OpenWrt"
timezone = "UTC"
ttylogin = "0"
log_size = "64"
urandom_seed = "0"
}
This allows you to do things like setting hostname, timezone and some logging options. It’s not a lot, but it is something!
This is neat, but as complete support would require me to implement something like ~20 odd resources, it takes a while to get off the ground. After staring at the API for a little while I did realize you have RPC calls to deal with reading and writing files. This lead me to realize that you could in theory just wrap the file creation part of the UCI API, and allow people to just write them out by hand.
For the UCI documentation and supported configuations: https://openwrt.org/docs/guide-user/base-system/uci
resource "openwrt_configfile" "system" {
name = "system"
content = <<-EOT
config timeserver 'ntp'
option enabled '1'
option enable_server '0'
list server '0.openwrt.pool.ntp.org'
list server '1.openwrt.pool.ntp.org'
list server '2.openwrt.pool.ntp.org'
list server '3.openwrt.pool.ntp.org'
config system
option hostname 'OpenWrt'
option timezone 'UTC'
option ttylogin '0'
option log_size '64'
option urandom_seed '0'
EOT
}
This is a openwrt_configfile
resource that just allows you to wite out UCI
configuration files to the OpenWrt device, and call uci commit
on the
resource. While this doesn’t help you to model everything in HCL, it works as a
nice escape hatch to just do the raw configuration instead of waiting for
support in the provider.
I think this is neat and probably solves quite a bit of problems people have with the existing providers. They model everything and you do not really have an escape hatch when the support is not implemented.
The source code is here: https://github.com/Foxboron/terraform-provider-openwrt
I’ve also published it on the opentofu registry, and the terraform registry.
Please note I’m probably not going to care about feature requests beyond what I’ll need to configure my OpenWrt One. So I’ll probably just accept everything that comes my way in the form of pull-requests.