Managing OpenWrt remotely with cURL

OpenWrt is a free open source Linux distribution for routers. One of the main benefits of OpenWrt is the level of access you have over standard router firmware. This post is going to look at using the luci-mod-rpc module to manage OpenWrt over HTTP.

Installing luci-mod-rpc

The luci-mod-rpc module needs to be install. To do this you can run the following commands over SSH:

opkg install luci-mod-rpc
/etc/init.d/uhttpd restart

Alternatively you can install the package using the web interface by going to System -> Software and restart uhttpd from the System -> Startup page.

Requesting an API token

API calls require a session token. To request a new token make a request to the auth API endpoint:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/auth --data '
{
  "id": 1,
  "method": "login",
  "params": ["root", "secret_password"]
}'

This will return JSON with a token similar to the following assuming the authentication details are correct:

{"id":1,"result":"a191c1c171c210def536e5279ed0c67f","error":null}

Note: the authentication details should normally be the same as the details for the LuCI web interface.

Calling the API

At the time of writing there are currently four API end points besides the auth API:

  • /cgi-bin/luci/rpc/uci
  • /cgi-bin/luci/rpc/fs
  • /cgi-bin/luci/rpc/sys
  • /cgi-bin/luci/rpc/ipkg

The OpenWrt wiki briefly documents the available endpoints, however it's worth referring to the LuCI docs on GitHub for more information on the API calls being exposed.

Universal Configuration Interface

Universal Configuration Interface or UCI for short is an easy way to configure OpenWrt. If you're not already familiar with UCI the OpenWrt docs are a good place to go for further info.

The get method can be used to check configuration values:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/uci?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "get",
  "params": ["luci", "sauth", "sessiontime"]
}'

This will return output similar to the following:

{"id":"1","result":"3600","error":null}

You can also set configuration, for example:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/uci?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "set",
  "params": ["luci", "sauth", "sessiontime", 7200]
}'

File system access

The fs endpoint calls the nixio.fs library. This can be used to interact with files on the file system. For example the metadata associated with a file can be checked using the stat method:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/fs?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "stat",
  "params": ["/etc/hosts"]
}'

It's also possible to read and write files. For example you can use readfile to get the contents of /etc/hosts:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/fs?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "stat",
  "params": ["/etc/hosts"]
}'

The returned JSON should hopefully look similar to the following:

{"id":"1","result":"MTI3LjAuMC4xIGxvY2FsaG9zdAo=","error":null}

The result can then be decoded:

$ echo MTI3LjAuMC4xIGxvY2FsaG9zdAo= | base64 --decode
127.0.0.1 localhost

Note: unless you have the luasocket module installed you may get an error similar to the following:

{
  "id":"1",
  "result":null,
  "error":{
    "message":"Invalid params.",
    "data":"/usr/lib/lua/luci/controller/rpc.lua:110: attempt to call global 'error' (a nil value)",
    "code":-32602
  }
}

Making system calls

The sys endpoint exposes methods from several libraries including luci.sys, luci.sys.process, and luci.sys.net. By default the endpoint will assume calls are being made to the luci.sys library. For example the exec method could be called with a curl command similar to the following:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/sys?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "exec",
  "params": ["echo 'hello world'"]
}'

A prefix can be added to the method name to call methods from other sys libraries. For example if you wanted call the arptable method from luci.sys.net you could use a command similar to the following:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/sys?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "net.arptable",
  "params": []
}'

Managing packages

The ipkg end point can be used to managed installed software. For example you could check if luci-mod-rpc is installed with a command similar to the following:

$ curl -X POST http://192.168.1.1/cgi-bin/luci/rpc/ipkg?auth=a191c1c171c210def536e5279ed0c67f --data '
{
  "id": "1",
  "method": "installed",
  "params": ["luci-mod-rpc"]
}'

The command above should return the following JSON:

{"id":"1","result":true,"error":null}

Note: Unfortunately several methods such as list_all use a call back function instead of returning data. This makes them fairly tricky to call with the RPC API.