Skip to content

Commit

Permalink
DNS api (#161)
Browse files Browse the repository at this point in the history
ns-api: add ns.dns
  • Loading branch information
gsanchietti authored Sep 18, 2023
1 parent f4c3925 commit d0bf66e
Show file tree
Hide file tree
Showing 4 changed files with 321 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/ns-api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ define Package/ns-api/install
$(INSTALL_DATA) ./files/ns.subscription.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_BIN) ./files/ns.dhcp $(1)/usr/libexec/rpcd/
$(INSTALL_DATA) ./files/ns.dhcp.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_BIN) ./files/ns.dns $(1)/usr/libexec/rpcd/
$(INSTALL_DATA) ./files/ns.dns.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_DIR) $(1)/lib/upgrade/keep.d
$(INSTALL_CONF) files/msmtp.keep $(1)/lib/upgrade/keep.d/msmtp
$(LN) /usr/bin/msmtp $(1)/usr/sbin/sendmail
Expand Down
121 changes: 121 additions & 0 deletions packages/ns-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,127 @@ Error response example:
{"error": "lease not found"}
```

## ns.dns

Manage global DNS configuration and DNS records.

### list-records

List DNS records:
```
api-cli ns.dns list-records
```

Response example:
```json
{
"records": [
{
"record": "cfg0af37d",
"ip": "1.2.3.4",
"name": "host1.nethesis.it",
"description": ""
"wildcard": true
}
]
}
```

The `record` field is the id of the DNS record.

### get-record

Retrieve the given record by id:
```
api-cli ns.dns get-record --data '{"record": "cfg0af37d"}'
```

Response example:
```json
{
"name": "host1.nethesis.it",
"ip": "1.2.3.4",
"description": "",
"wildcard": false
}
```

### add-record

Add a DNS record:
```
api-cli ns.dns add-record --data '{"name": "www.example.org", "ip": "192.168.100.2", "description": "My record", "wildcard": true}'
```

Successful response example:
```json
{"record": "my_record"}
```

### edit-record

Edit a DNS record:
```
api-cli ns.dns edit-record --data '{"record": "cfg0af37d", "name": "www.example.org", "ip": "192.168.100.2", "description": "My record", "wildcard": false}'
```

Successful response example:
```json
{"record": "my_record"}
```

Error response example:
```json
{"error": "record not found"}
```

### delete-record

Delete a DNS record:
```
api-cli ns.dns delete-record --data '{"record": "cfg0af37d"}'
```

Successful response example:
```json
{"record": "my_record"}
```

Error response example:
```json
{"error": "record not found"}
```

### get-config

Get DNS general configuration:
```
api-cli ns.dns get-config
```

Response example:
```json
{
"domain": "lan",
"logqueries": true,
"server": [
"8.8.7.7"
]
}
```

### set-config

Set DNS general configuration:
```
api-cli ns.dns set-config --data '{"domain": "lan", "logqueries": true, "server": ["8.8.8.8"], ["1.1.1"]]}''
```

Response example:
```json
{"server": "cfg01411c"}
```

# Creating a new API

Conventions:
Expand Down
185 changes: 185 additions & 0 deletions packages/ns-api/files/ns.dns
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/usr/bin/python3

#
# Copyright (C) 2023 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

# Manage DNS

import sys
import json
import subprocess
from euci import EUci
from nethsec import utils

def list_records():
ret = {"records": []}
u = EUci()
for r in utils.get_all_by_type(u, 'dhcp', 'domain'):
rdata = u.get_all('dhcp', r)
name = rdata.get('name','')
ip = rdata.get('ip', '')
ret['records'].append({"record": r, "ip": ip, "name": name, "description": rdata.get("ns_description", ""), 'wildcard': is_wildcard(u, ip, name)})
return ret

def add_record(args):
u = EUci()
rname = utils.get_random_id()
u.set('dhcp', rname, 'domain')
u.set('dhcp', rname, 'ip', args["ip"])
u.set('dhcp', rname, 'name', args["name"])
u.set('dhcp', rname, 'ns_description', args["description"])
if args["wildcard"]:
add_wildcard(u, args['ip'], args['name'])
u.save('dhcp')
return {"record": rname}

def is_wildcard(u, ip, name):
try:
cur = u.get('dhcp', get_server(u), 'address', list=True)
except:
return False
for addr in cur:
if f'/{name}/{ip}' == addr:
return True
return False

def remove_wildcard(u, ip, name):
server = get_server(u)
new = []
try:
cur = u.get('dhcp', server, 'address', list=True)
except:
return
for addr in cur:
if f'/{name}/{ip}' == addr:
continue
new.append(addr)
u.set('dhcp', server, 'address', new)
u.save('dhcp')

def add_wildcard(u, ip, name):
remove_wildcard(u, ip, name)
server = get_server(u)
try:
addr = u.get('dhcp', server, 'address', list=True)
except:
addr = []
addr.append(f'/{name}/{ip}')
u.set('dhcp', server, 'address', addr)
u.save('dhcp')

def edit_record(args):
u = EUci()
try:
u.get('dhcp', args['record'])
except:
return {"error": "record not found"}
rname = args["record"]
u.set('dhcp', rname, 'domain')
u.set('dhcp', rname, 'ip', args["ip"])
u.set('dhcp', rname, 'name', args["name"])
u.set('dhcp', rname, 'ns_description', args["description"])
if args["wildcard"]:
add_wildcard(u, args['ip'], args['name'])
else:
remove_wildcard(u, args['ip'], args['name'])
u.save('dhcp')
return {"record": rname}

def get_record(args):
u = EUci()
try:
record = u.get_all('dhcp', args['record'])
except:
return {"error": "record not found"}
return {"name": record.get('name', ''), "ip": record.get("ip", ''), "description": record.get("ns_description",""), 'wildcard': is_wildcard(u, record.get("ip", ''), record.get('name', ''))}

def delete_record(args):
u = EUci()
try:
u.get('dhcp', args['record'])
u.delete('dhcp', args['record'])
u.save('dhcp')
except:
return {"error": "record not found"}
return {"record": args['record']}

def get_server(u):
for section in u.get("dhcp"):
if u.get("dhcp", section) == "dnsmasq":
return section
return None

def get_config():
u = EUci()
ret = {}
section = get_server(u)
try:
config = u.get_all("dhcp", section)
ret["domain"] = config.get("domain", "lan")
ret["logqueries"] = config.get("logqueries", "0") == "1"
ret["server"] = config.get("server", [])
except:
return {"error": "config read error"}
return ret

def bool2str(bval):
if bval:
return "1"
else:
return "0"

def set_config(args):
u = EUci()
ret = {}
section = get_server(u)
try:
config = u.get_all("dhcp", section)
for opt in args:
val = args[opt]
if type(val) == type(True):
val = bool2str(val)
u.set("dhcp", section, opt, val)
# calculate local option to avoid errors
if 'domain' in args and not 'local' in args:
u.set("dhcp", section, 'local', f'/{args["domain"]}/')
u.save("dhcp")
except:
return {"error": "config write error"}

return {"server": section}

cmd = sys.argv[1]

if cmd == 'list':
print(json.dumps({
"list-records": {},
"get-config": {},
"set-config": {"domain": "lan", "logqueries": True, "server": ["1.1.1.1"]},
"add-record": {"hostname": "www.example.org", "ipaddr": "192.168.100.2", "description": "My record", "wildcard": True},
"edit-record": {"record": "ns_myrecord", "hostname": "www.example.org", "ipaddr": "192.168.100.2", "description": "My record", "wildcard": True},
"get-record": {"record": "ns_myrecord"},
"delete-record": {"record": "ns_myrecord"}
}))
elif cmd == 'call':
action = sys.argv[2]
ret = {}
if action == "list-records":
ret = list_records()
elif action == "get-config":
ret = get_config()
else:
args = json.loads(sys.stdin.read())
if action == "set-config":
ret = set_config(args)
elif action == "get-record":
ret = get_record(args)
elif action == "delete-record":
ret = delete_record(args)
elif action == "edit-record":
ret = edit_record(args)
elif action == "add-record":
ret = add_record(args)
print(json.dumps(ret))
13 changes: 13 additions & 0 deletions packages/ns-api/files/ns.dns.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"dns-manager": {
"description": "Manage DNS",
"write": {},
"read": {
"ubus": {
"ns.dns": [
"*"
]
}
}
}
}

0 comments on commit d0bf66e

Please sign in to comment.