Gargoyle API Access (rpcd and uhttpd-mod-ubus)
Posted: Tue Sep 12, 2023 8:21 am
Install required software
You will need to install rpcd and uhttpd-mod-ubus
Check /etc/config/uhttpd has been modified and option ubus_prefix has been set automatically, otherwise
ubus Calls
Available ubus calls are found by "ubus list"
Methods against each one are found by "-v"
Example "ubus call"
Access control lists (ACLs)
Are defined in /usr/share/rpcd/acl.d/
By default one exists, unauthenticated.json, which allows unauthenticated users to access the "session access" and "session login" methods
Let's login
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your password IN PLAINTEXT. Consider using HTTPS.
In the response we see several important things
1. SESSION TOKEN. You will need to pass this to any further API requests that are not accessible without first being authenticated
2. EXPIRES. You will need to refresh your token after this period
3. ACLs. These are what you currently have access to. By default, not much.
Let's define an ACL
For accessing the "network.interface.wan status" command
Get a new Session Token and check that the system now recognises our new ACLs
First Useful API Call
Let's call the "network.interface.wan status" API that we now have access to
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your session token IN PLAINTEXT. Consider using HTTPS.
One more example
Let's define an ACL for accessing the "system board" command
Get a new Session Token and check that the system now recognises our new ACLs
Let's call the "system board" API that we now have access to
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your session token IN PLAINTEXT. Consider using HTTPS.
Granular controls
By default, root can access everything.
You can define users in /etc/config/rpcd and give them less access
This user won't be able to call the "network.interface.wan status" API
You will need to install rpcd and uhttpd-mod-ubus
Code: Select all
gpkg update
gpkg install rpcd
gpkg install uhttpd-mod-ubus
Code: Select all
option ubus_prefix '/ubus'
Available ubus calls are found by "ubus list"
Code: Select all
root@Gargoyle:~# ubus list
dhcp
dnsmasq
dnsmasq.dns
hotplug.block
hotplug.dhcp
hotplug.iface
hotplug.neigh
hotplug.net
hotplug.ntp
hotplug.openvpn
hotplug.tftp
hotplug.tty
hotplug.usb
hotplug.usbmisc
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.interface.wan
network.interface.wan6
network.interface.wg0
network.wireless
rc
service
session
system
uci
usteer
Code: Select all
root@Gargoyle:~# ubus -v list network.interface.wan
'network.interface.wan' @78a6a62b
"up":{}
"down":{}
"renew":{}
"status":{}
"prepare":{}
"dump":{}
"add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
"remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
"notify_proto":{}
"remove":{}
"set_data":{}
Code: Select all
root@Gargoyle:~# ubus call network.interface.wan status
{
"up": true,
"pending": false,
"available": true,
"autostart": true,
"dynamic": false,
"uptime": 3470914,
"l3_device": "eth0",
"proto": "dhcp",
"device": "eth0",
"updated": [
"data"
],
"metric": 0,
"dns_metric": 0,
"delegation": true,
"ipv4-address": [
{
"address": "xxx.xxx.xxx.xxx",
"mask": 16
}
],
"ipv6-address": [
],
"ipv6-prefix": [
],
"ipv6-prefix-assignment": [
],
"route": [
{
"target": "0.0.0.0",
"mask": 0,
"nexthop": "xxx.xxx.xxx.xxx",
"source": "xxx.xxx.xxx.xxx/32"
}
],
"dns-server": [
"1.1.1.1",
"1.0.0.1"
],
"dns-search": [
],
"neighbors": [
],
"inactive": {
"ipv4-address": [
],
"ipv6-address": [
],
"route": [
],
"dns-server": [
"xxx.xxx.xxx.xxx",
"xxx.xxx.xxx.xxy"
],
"dns-search": [
"ns.xxx.xxx.xxx"
],
"neighbors": [
]
},
"data": {
"dhcpserver": "xxx.xxx.xxx.xxx",
"hostname": "Gargoyle",
"leaseacquired": 3444277,
"leasetime": 86400
}
}
Are defined in /usr/share/rpcd/acl.d/
By default one exists, unauthenticated.json, which allows unauthenticated users to access the "session access" and "session login" methods
Let's login
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your password IN PLAINTEXT. Consider using HTTPS.
Code: Select all
curl --request POST \
--url http://192.168.1.1/ubus \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"00000000000000000000000000000000",
"session",
"login",
{
"username": "root",
"password": "YOUR ROOT PASSWORD"
}
]
}'
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"ubus_rpc_session": "SESSION TOKEN",
"timeout": 300,
"expires": 300,
"acls": {
"access-group": {
"unauthenticated": [
"read"
]
},
"ubus": {
"session": [
"access",
"login"
]
}
},
"data": {
"username": "root"
}
}
]
}
1. SESSION TOKEN. You will need to pass this to any further API requests that are not accessible without first being authenticated
2. EXPIRES. You will need to refresh your token after this period
3. ACLs. These are what you currently have access to. By default, not much.
Let's define an ACL
For accessing the "network.interface.wan status" command
Code: Select all
cp /usr/share/rpcd/acl.d/unauthenticated.json /usr/share/rpcd/acl.d/waninterface.json
vim /usr/share/rpcd/acl.d/waninterface.json
# Copy contents below
{
"waninterface": {
"description": "Access controls for WAN Interface",
"read": {
"ubus": {
"network.interface.wan": ["status"]
}
}
}
}
# Copy contents above
Code: Select all
curl --request POST \
--url http://192.168.1.1/ubus \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"00000000000000000000000000000000",
"session",
"login",
{
"username": "root",
"password": "YOUR ROOT PASSWORD"
}
]
}'
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"ubus_rpc_session": "SESSION TOKEN",
"timeout": 300,
"expires": 300,
"acls": {
"access-group": {
"unauthenticated": [
"read"
],
"waninterface": [
"read"
]
},
"ubus": {
"network.interface.wan": [
"status"
],
"session": [
"access",
"login"
]
}
},
"data": {
"username": "root"
}
}
]
}
Let's call the "network.interface.wan status" API that we now have access to
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your session token IN PLAINTEXT. Consider using HTTPS.
Code: Select all
curl --request POST \
--url http://192.168.1.1/ubus \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"YOUR SESSION TOKEN",
"network.interface.wan",
"status",
{}
]
}'
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"up": true,
"pending": false,
"available": true,
"autostart": true,
"dynamic": false,
"uptime": 3471506,
"l3_device": "eth0",
"proto": "dhcp",
"device": "eth0",
"updated": [
"data"
],
"metric": 0,
"dns_metric": 0,
"delegation": true,
"ipv4-address": [
{
"address": "xxx.xxx.xxx.xxx",
"mask": 16
}
],
"ipv6-address": [],
"ipv6-prefix": [],
"ipv6-prefix-assignment": [],
"route": [
{
"target": "0.0.0.0",
"mask": 0,
"nexthop": "xxx.xxx.xxx.xxx",
"source": "xxx.xxx.xxx.xxx/32"
}
],
"dns-server": [
"1.1.1.1",
"1.0.0.1"
],
"dns-search": [],
"neighbors": [],
"inactive": {
"ipv4-address": [],
"ipv6-address": [],
"route": [],
"dns-server": [
"xxx.xxx.xxx.xxx",
"xxx.xxx.xxx.xxx"
],
"dns-search": [
"ns.xxx.xxx.xxx"
],
"neighbors": []
},
"data": {
"dhcpserver": "xxx.xxx.xxx.xxx",
"hostname": "Gargoyle",
"leaseacquired": 3444277,
"leasetime": 86400
}
}
]
}
Let's define an ACL for accessing the "system board" command
Code: Select all
cp /usr/share/rpcd/acl.d/unauthenticated.json /usr/share/rpcd/acl.d/systemboard.json
vim /usr/share/rpcd/acl.d/systemboard.json
# Copy contents below
{
"systemboard": {
"description": "Access controls for system board command",
"read": {
"ubus": {
"system": ["board"]
}
}
}
}
# Copy contents above
Code: Select all
curl --request POST \
--url http://192.168.1.1/ubus \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"00000000000000000000000000000000",
"session",
"login",
{
"username": "root",
"password": "YOUR ROOT PASSWORD"
}
]
}'
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"ubus_rpc_session": "SESSION TOKEN",
"timeout": 300,
"expires": 300,
"acls": {
"access-group": {
"systemboard": [
"read"
],
"unauthenticated": [
"read"
],
"waninterface": [
"read"
]
},
"ubus": {
"network.interface.wan": [
"status"
],
"session": [
"access",
"login"
],
"system": [
"board"
]
}
},
"data": {
"username": "root"
}
}
]
}
WARNING: Examples are sent to HTTP, not HTTPS. You will be sending your session token IN PLAINTEXT. Consider using HTTPS.
Code: Select all
curl --request POST \
--url http://192.168.1.1/ubus \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"YOUR SESSION TOKEN",
"system",
"board",
{}
]
}'
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
0,
{
"kernel": "5.10.176",
"hostname": "Gargoyle",
"system": "Intel(R) Celeron(R) J4125 CPU @ 2.00GHz",
"model": "Default string Default string",
"board_name": "default-string-default-string",
"rootfs_type": "ext4",
"release": {
"distribution": "OpenWrt",
"version": "22.03-SNAPSHOT",
"revision": "r19160+967-7ea2f3d6e2",
"target": "x86/64",
"description": "OpenWrt 22.03-SNAPSHOT r19160+967-7ea2f3d6e2"
}
}
]
}
By default, root can access everything.
You can define users in /etc/config/rpcd and give them less access
This user won't be able to call the "network.interface.wan status" API
Code: Select all
config login 'testuser'
option username 'testuser'
option password 'xxxxxxxx'
list read 'systemboard'