Merge pull request #2010 from EricLuehrsen/unbound_20180721

luci-app-unbound: add LuCI for forward stub and auth zone clauses
This commit is contained in:
Jo-Philipp Wich 2018-07-30 07:01:46 +02:00 committed by GitHub
commit 9db490ab1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 640 additions and 333 deletions

View file

@ -1,151 +1,200 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2017 Eric Luehrsen <ericluehrsen@hotmail.com>
-- Copyright 2017 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.unbound", package.seeall)
function index()
local ucl = luci.model.uci.cursor()
local valexp = ucl:get_first("unbound", "unbound", "extended_luci")
local valman = ucl:get_first("unbound", "unbound", "manual_conf")
local fs = require "nixio.fs"
local ucl = luci.model.uci.cursor()
local valman = ucl:get_first("unbound", "unbound", "manual_conf")
if not nixio.fs.access("/etc/config/unbound") then
return
end
if not fs.access("/etc/config/unbound") then
return
end
if valexp == "1" then
-- Expanded View
entry({"admin", "services", "unbound"}, firstchild(), _("Recursive DNS")).dependent = false
entry({"admin", "services", "unbound"},
firstchild(), _("Recursive DNS")).dependent = false
-- UCI Tab(s)
entry({"admin", "services", "unbound", "configure"}, cbi("unbound/configure"), _("Settings"), 10)
entry({"admin", "services", "unbound", "configure"},
cbi("unbound/configure"), _("Unbound"), 10)
if (valman == "0") then
entry({"admin", "services", "unbound", "zones"},
cbi("unbound/zones"), _("Zones"), 15)
end
-- Status Tab(s)
entry({"admin", "services", "unbound", "status"}, firstchild(), _("Status"), 20)
entry({"admin", "services", "unbound", "status", "syslog"}, call("QuerySysLog"), _("Log"), 50).leaf = true
entry({"admin", "services", "unbound", "status"},
firstchild(), _("Status"), 20)
entry({"admin", "services", "unbound", "status", "syslog"},
call("QuerySysLog"), _("Log"), 50).leaf = true
if nixio.fs.access("/usr/sbin/unbound-control") then
-- Require unbound-control to execute
entry({"admin", "services", "unbound", "status", "statistics"}, call("QueryStatistics"), _("Statistics"), 10).leaf = true
entry({"admin", "services", "unbound", "status", "localdata"}, call("QueryLocalData"), _("Local Data"), 20).leaf = true
entry({"admin", "services", "unbound", "status", "localzone"}, call("QueryLocalZone"), _("Local Zones"), 30).leaf = true
if fs.access("/usr/sbin/unbound-control") then
-- Require unbound-control to execute
entry({"admin", "services", "unbound", "status", "statistics"},
call("QueryStatistics"), _("Statistics"), 10).leaf = true
entry({"admin", "services", "unbound", "status", "localdata"},
call("QueryLocalData"), _("Local Data"), 20).leaf = true
entry({"admin", "services", "unbound", "status", "localzone"},
call("QueryLocalZone"), _("Local Zones"), 30).leaf = true
else
entry({"admin", "services", "unbound", "status", "statistics"}, call("ShowEmpty"), _("Statistics"), 10).leaf = true
entry({"admin", "services", "unbound", "status", "statistics"},
call("ShowEmpty"), _("Statistics"), 10).leaf = true
end
-- Raw File Tab(s)
entry({"admin", "services", "unbound", "files"}, firstchild(), _("Files"), 30)
entry({"admin", "services", "unbound", "files"},
firstchild(), _("Files"), 30)
if valman ~= "1" then
entry({"admin", "services", "unbound", "files", "base"}, call("ShowUnboundConf"), _("UCI: Unbound"), 10).leaf = true
if (valman == "0") then
entry({"admin", "services", "unbound", "files", "uci"},
form("unbound/uciedit"), _("Edit: UCI"), 5).leaf = true
entry({"admin", "services", "unbound", "files", "base"},
call("ShowUnboundConf"), _("Show: Unbound"), 10).leaf = true
else
entry({"admin", "services", "unbound", "files", "base"}, form("unbound/manual"), _("Edit: Unbound"), 10).leaf = true
entry({"admin", "services", "unbound", "files", "base"},
form("unbound/manual"), _("Edit: Unbound"), 10).leaf = true
end
entry({"admin", "services", "unbound", "files", "server"}, form("unbound/server"), _("Edit: Server"), 20).leaf = true
entry({"admin", "services", "unbound", "files", "extended"}, form("unbound/extended"), _("Edit: Extended"), 30).leaf = true
entry({"admin", "services", "unbound", "files", "server"},
form("unbound/server"), _("Edit: Server"), 20).leaf = true
entry({"admin", "services", "unbound", "files", "extended"},
form("unbound/extended"), _("Edit: Extended"), 30).leaf = true
if nixio.fs.access("/var/lib/unbound/unbound_dhcp.conf") then
entry({"admin", "services", "unbound", "files", "dhcp"}, call("ShowDHCPConf"), _("Include: DHCP"), 40).leaf = true
if fs.access("/var/lib/unbound/dhcp.conf") then
entry({"admin", "services", "unbound", "files", "dhcp"},
call("ShowDHCPConf"), _("Show: DHCP"), 40).leaf = true
end
if nixio.fs.access("/var/lib/unbound/adb_list.overall") then
entry({"admin", "services", "unbound", "files", "adblock"}, call("ShowAdblock"), _("Include: Adblock"), 50).leaf = true
if fs.access("/var/lib/unbound/adb_list.overall") then
entry({"admin", "services", "unbound", "files", "adblock"},
call("ShowAdblock"), _("Show: Adblock"), 50).leaf = true
end
else
-- Simple View to UCI only
entry({"admin", "services", "unbound"}, cbi("unbound/configure"), _("Recursive DNS")).dependent = false
end
end
function ShowEmpty()
local lclhead = "Unbound Control"
local lcldesc = luci.i18n.translate("This could display more statistics with the unbound-control package.")
luci.template.render("unbound/show-empty", {heading = lclhead, description = lcldesc})
local lclhead = "Unbound Control"
local lcldesc = luci.i18n.translate(
"This could display more statistics with the unbound-control package.")
luci.template.render("unbound/show-empty",
{heading = lclhead, description = lcldesc})
end
function QuerySysLog()
local lclhead = "System Log"
local lcldata = luci.util.exec("logread | grep -i unbound")
local lcldesc = luci.i18n.translate("This shows syslog filtered for events involving Unbound.")
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local lcldata = luci.util.exec("logread | grep -i unbound")
local lcldesc = luci.i18n.translate(
"This shows syslog filtered for events involving Unbound.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function QueryStatistics()
local lclhead = "Unbound Control Stats"
local lcldata = luci.util.exec("unbound-control -c /var/lib/unbound/unbound.conf stats_noreset")
local lcldesc = luci.i18n.translate("This shows some performance statistics tracked by Unbound.")
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local lcldata = luci.util.exec(
"unbound-control -c /var/lib/unbound/unbound.conf stats_noreset")
local lcldesc = luci.i18n.translate(
"This shows Unbound self reported performance statistics.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function QueryLocalData()
local lclhead = "Unbound Control Local Data"
local lcldata = luci.util.exec("unbound-control -c /var/lib/unbound/unbound.conf list_local_data")
local lcldesc = luci.i18n.translate("This shows local host records that shortcut recursion.")
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local lcldata = luci.util.exec(
"unbound-control -c /var/lib/unbound/unbound.conf list_local_data")
local lcldesc = luci.i18n.translate(
"This shows Unbound 'local-data:' entries from default, .conf, or control.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function QueryLocalZone()
local lclhead = "Unbound Control Local Zones"
local lcldata = luci.util.exec("unbound-control -c /var/lib/unbound/unbound.conf list_local_zones")
local lcldesc = luci.i18n.translate("This shows local zone definitions that affect recursion routing or processing. ")
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local lcldata = luci.util.exec(
"unbound-control -c /var/lib/unbound/unbound.conf list_local_zones")
local lcldesc = luci.i18n.translate(
"This shows Unbound 'local-zone:' entries from default, .conf, or control.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function ShowUnboundConf()
local unboundfile = "/var/lib/unbound/unbound.conf"
local lclhead = "Unbound Conf"
local lcldata = nixio.fs.readfile(unboundfile)
local lcldesc = luci.i18n.translate("This shows configuration generated by UCI:")
lcldesc = lcldesc .. " (" .. unboundfile .. ")"
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local unboundfile = "/var/lib/unbound/unbound.conf"
local lcldata = nixio.fs.readfile(unboundfile)
local lcldesc = luci.i18n.translate(
"This shows '" .. unboundfile .. "' generated from UCI configuration.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function ShowDHCPConf()
local dhcpfile = "/var/lib/unbound/unbound_dhcp.conf"
local lclhead = "DHCP Conf"
local lcldata = nixio.fs.readfile(dhcpfile)
local lcldesc = luci.i18n.translate("This shows LAN hosts added by DHCP hook scripts:")
lcldesc = lcldesc .. " (" .. dhcpfile .. ")"
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
local dhcpfile = "/var/lib/unbound/dhcp.conf"
local lcldata = nixio.fs.readfile(dhcpfile)
local lcldesc = luci.i18n.translate(
"This shows '" .. dhcpfile .. "' list of hosts from DHCP hook scripts.")
luci.template.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
function ShowAdblock()
local adblockfile = "/var/lib/unbound/adb_list.overall"
local lclhead = "Adblock Conf"
local lcldata, lcldesc
local fs = require "nixio.fs"
local tp = require "luci.template"
local tr = require "luci.i18n"
local adblockfile = "/var/lib/unbound/adb_list.overall"
local lcldata, lcldesc
if nixio.fs.stat(adblockfile).size > 262144 then
lcldesc = luci.i18n.translate("Adblock domain list is too large for LuCI:")
lcldesc = lcldesc .. " (" .. adblockfile .. ")"
luci.template.render("unbound/show-empty", {heading = lclhead, description = lcldesc})
if fs.stat(adblockfile).size > 262144 then
lcldesc = tr.translate(
"Adblock domain list '" .. adblockfile .. "' is too large for LuCI.")
else
lcldata = nixio.fs.readfile(adblockfile)
lcldesc = luci.i18n.translate("This shows blocked domains provided by Adblock scripts:")
lcldesc = lcldesc .. " (" .. adblockfile .. ")"
luci.template.render("unbound/show-textbox", {heading = lclhead, description = lcldesc, content = lcldata})
end
tp.render("unbound/show-empty",
{heading = "", description = lcldesc})
else
lcldata = fs.readfile(adblockfile)
lcldesc = tr.translate(
"This shows '" .. adblockfile .. "' list of adblock domains." )
tp.render("unbound/show-textbox",
{heading = "", description = lcldesc, content = lcldata})
end
end

View file

@ -1,5 +1,5 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2016 Eric Luehrsen <ericluehrsen@hotmail.com>
-- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
-- Copyright 2016 Dan Luedtke <mail@danrl.com>
-- Licensed to the public under the Apache License 2.0.
@ -9,307 +9,312 @@ local rlh, rpv, vld, nvd, eds, prt, tlm
local ctl, dlk, dom, dty, lfq, wfq, exa
local dp6, d64, pfx, qry, qrs
local pro, tgr, rsc, rsn, ag2, stt
local rpn, din, dfw, ath
local rpn, din, ath
local ut = require "luci.util"
local sy = require "luci.sys"
local ht = require "luci.http"
local ds = require "luci.dispatcher"
local ucl = luci.model.uci.cursor()
local valman = ucl:get_first("unbound", "unbound", "manual_conf")
m1 = Map("unbound")
s1 = m1:section(TypedSection, "unbound", translate("DNS Resolver"),
translatef("Unbound <a href=\"%s\" target=\"_blank\">(NLnet Labs)</a>"
.. " is a validating, recursive, and caching DNS resolver.",
"https://www.unbound.net/"))
s1 = m1:section(TypedSection, "unbound")
s1.addremove = false
s1.anonymous = true
--LuCI, Unbound, or Not
s1:tab("basic", translate("Basic"),
translatef("<h3>Unbound Basic Settings</h3>\n"
.. "<a href=\"%s\" target=\"_blank\">Unbound (link)</a>"
.. " is a validating, recursive, and caching DNS resolver. "
.. "UCI documentation can be found on "
.. "<a href=\"%s\" target=\"_blank\">github (link)</a>.",
"https://www.unbound.net/",
"https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md"))
s1:tab("basic", translate("Basic"))
if valman ~= "1" then
-- Not in manual configuration mode; show UCI
s1:tab("advanced", translate("Advanced"),
translatef("<h3>Unbound Advanced Settings</h3>\n"
.. "Domain manipulation, lookup protection, and workarounds for "
.. "<a href=\"%s\" target=\"_blank\">Unbound </a>"
.. " DNS resolver.", "https://www.unbound.net/"))
s1:tab("DHCP", translate("DHCP"),
translatef("<h3>Unbound DHCP Settings</h3>\n"
.. "Link your DHCP server to "
.. "<a href=\"%s\" target=\"_blank\">Unbound </a>"
.. " DNS resolver.", "https://www.unbound.net/ "))
s1:tab("resource", translate("Resource"),
translatef("<h3>Unbound Resource Settings</h3>\n"
.. "Memory and protocol setttings for "
.. "<a href=\"%s\" target=\"_blank\">Unbound </a>"
.. " DNS resolver.", "https://www.unbound.net/"))
if (valman == "0") then
-- Not in manual configuration mode; show UCI
s1:tab("advanced", translate("Advanced"))
s1:tab("DHCP", translate("DHCP"))
s1:tab("resource", translate("Resource"))
end
s1:tab("trigger", translate("Trigger"),
translatef("<h3>Unbound Event Trigger Settings</h3>\n"
.. "Start, reload, and save RFC5011 DNSKEY records for "
.. "<a href=\"%s\" target=\"_blank\">Unbound </a>"
.. " DNS resolver.", "https://www.unbound.net/"))
--Basic Tab, unconditional pieces
ena = s1:taboption("basic", Flag, "enabled", translate("Enable Unbound:"),
translate("Enable the initialization scripts for Unbound"))
translate("Enable the initialization scripts for Unbound"))
ena.rmempty = false
mcf = s1:taboption("basic", Flag, "manual_conf", translate("Manual Conf:"),
translate("Skip UCI and use /etc/unbound/unbound.conf"))
translate("Skip UCI and use /etc/unbound/unbound.conf"))
mcf.rmempty = false
lci = s1:taboption("basic", Flag, "extended_luci", translate("Extended Tabs:"),
translate("See detailed tabs for statistics, debug, and manual configuration"))
lci.rmempty = false
if (valman == "0") then
-- Not in manual configuration mode; show UCI
--Basic Tab
lsv = s1:taboption("basic", Flag, "localservice",
translate("Local Service:"),
translate("Accept queries only from local subnets"))
lsv.rmempty = false
if valman ~= "1" then
-- Not in manual configuration mode; show UCI
--Basic Tab
lsv = s1:taboption("basic", Flag, "localservice", translate("Local Service:"),
translate("Accept queries only from local subnets"))
lsv.rmempty = false
vld = s1:taboption("basic", Flag, "validator",
translate("Enable DNSSEC:"),
translate("Enable the DNSSEC validator module"))
vld.rmempty = false
vld = s1:taboption("basic", Flag, "validator", translate("Enable DNSSEC:"),
translate("Enable the DNSSEC validator module"))
vld.rmempty = false
nvd = s1:taboption("basic", Flag, "validator_ntp",
translate("DNSSEC NTP Fix:"),
translate("Break the loop where DNSSEC needs NTP and NTP needs DNS"))
nvd.rmempty = false
nvd:depends({ validator = true })
nvd = s1:taboption("basic", Flag, "validator_ntp", translate("DNSSEC NTP Fix:"),
translate("Break the loop where DNSSEC needs NTP and NTP needs DNS"))
nvd.rmempty = false
nvd:depends({ validator = true })
prt = s1:taboption("basic", Value, "listen_port",
translate("Listening Port:"),
translate("Choose Unbounds listening port"))
prt.datatype = "port"
prt.rmempty = false
d64 = s1:taboption("basic", Flag, "dns64", translate("Enable DNS64:"),
translate("Enable the DNS64 module"))
d64.rmempty = false
--Avanced Tab
rlh = s1:taboption("advanced", Flag, "rebind_localhost",
translate("Filter Localhost Rebind:"),
translate("Protect against upstream response of 127.0.0.0/8"))
rlh.rmempty = false
pfx = s1:taboption("basic", Value, "dns64_prefix", translate("DNS64 Prefix:"),
translate("Prefix for generated DNS64 addresses"))
pfx.datatype = "ip6addr"
pfx.placeholder = "64:ff9b::/96"
pfx.optional = true
pfx:depends({ dns64 = true })
rpv = s1:taboption("advanced", ListValue, "rebind_protection",
translate("Filter Private Rebind:"),
translate("Protect against upstream responses within local subnets"))
rpv:value("0", translate("No Filter"))
rpv:value("1", translate("Filter RFC1918/4193"))
rpv:value("2", translate("Filter Entire Subnet"))
rpv.rmempty = false
prt = s1:taboption("basic", Value, "listen_port", translate("Listening Port:"),
translate("Choose Unbounds listening port"))
prt.datatype = "port"
prt.rmempty = false
d64 = s1:taboption("advanced", Flag, "dns64", translate("Enable DNS64:"),
translate("Enable the DNS64 module"))
d64.rmempty = false
--Avanced Tab
din = s1:taboption("advanced", DynamicList, "domain_insecure",
translate("Domain Insecure:"),
translate("List domains to bypass checks of DNSSEC"))
din:depends({ validator = true })
pfx = s1:taboption("advanced", Value, "dns64_prefix",
translate("DNS64 Prefix:"),
translate("Prefix for generated DNS64 addresses"))
pfx.datatype = "ip6addr"
pfx.placeholder = "64:ff9b::/96"
pfx.optional = true
pfx:depends({ dns64 = true })
dfw = s1:taboption("advanced", DynamicList, "domain_forward",
translate("Domain Forward:"),
translate("List domains to simply forward to stub resolvers in /tmp/resolve.auto"))
din = s1:taboption("advanced", DynamicList, "domain_insecure",
translate("Domain Insecure:"),
translate("List domains to bypass checks of DNSSEC"))
din:depends({ validator = true })
rlh = s1:taboption("advanced", Flag, "rebind_localhost", translate("Filter Localhost Rebind:"),
translate("Protect against upstream response of 127.0.0.0/8"))
rlh.rmempty = false
ag2 = s1:taboption("advanced", Value, "root_age",
translate("Root DSKEY Age:"),
translate("Limit days between RFC5011 copies to reduce flash writes"))
ag2.datatype = "and(uinteger,min(1),max(99))"
ag2:value("3", "3")
ag2:value("9", "9 ("..translate("default")..")")
ag2:value("12", "12")
ag2:value("24", "24")
ag2:value("99", "99 ("..translate("never")..")")
rpv = s1:taboption("advanced", ListValue, "rebind_protection", translate("Filter Private Rebind:"),
translate("Protect against upstream responses within local subnets"))
rpv:value("0", translate("No Filter"))
rpv:value("1", translate("Filter RFC1918/4193"))
rpv:value("2", translate("Filter Entire Subnet"))
rpv.rmempty = false
tgr = s1:taboption("advanced", Value, "trigger_interface",
translate("Trigger Networks:"),
translate("Networks that may trigger Unbound to reload (avoid wan6)"))
tgr.template = "cbi/network_netlist"
tgr.widget = "checkbox"
tgr.rmempty = true
tgr.cast = "string"
tgr.nocreate = true
rpn = s1:taboption("advanced", Value, "rebind_interface", translate("Rebind Network Filter:"),
translate("Network subnets to filter from upstream responses"))
rpn.template = "cbi/network_netlist"
rpn.widget = "checkbox"
rpn.rmempty = true
rpn.cast = "string"
rpn.nocreate = true
rpn:depends({ rebind_protection = 2 })
rpn:depends({ rebind_protection = 3 })
--DHCP Tab
dlk = s1:taboption("DHCP", ListValue, "dhcp_link",
translate("DHCP Link:"),
translate("Link to supported programs to load DHCP into DNS"))
dlk:value("none", translate("No Link"))
dlk:value("dnsmasq", "dnsmasq")
dlk:value("odhcpd", "odhcpd")
dlk.rmempty = false
--DHCP Tab
dlk = s1:taboption("DHCP", ListValue, "dhcp_link", translate("DHCP Link:"),
translate("Link to supported programs to load DHCP into DNS"))
dlk:value("none", translate("No Link"))
dlk:value("dnsmasq", "dnsmasq")
dlk:value("odhcpd", "odhcpd")
dlk.rmempty = false
dp6 = s1:taboption("DHCP", Flag, "dhcp4_slaac6",
translate("DHCPv4 to SLAAC:"),
translate("Use DHCPv4 MAC to discover IP6 hosts SLAAC (EUI64)"))
dp6.rmempty = false
dp6:depends({ dhcp_link = "odhcpd" })
dp6 = s1:taboption("DHCP", Flag, "dhcp4_slaac6", translate("DHCPv4 to SLAAC:"),
translate("Use DHCPv4 MAC to discover IP6 hosts SLAAC (EUI64)"))
dp6.rmempty = false
dp6:depends({ dhcp_link = "odhcpd" })
dom = s1:taboption("DHCP", Value, "domain",
translate("Local Domain:"),
translate("Domain suffix for this router and DHCP clients"))
dom.placeholder = "lan"
dom:depends({ dhcp_link = "none" })
dom:depends({ dhcp_link = "odhcpd" })
dom = s1:taboption("DHCP", Value, "domain", translate("Local Domain:"),
translate("Domain suffix for this router and DHCP clients"))
dom.placeholder = "lan"
dom:depends({ dhcp_link = "none" })
dom:depends({ dhcp_link = "odhcpd" })
dty = s1:taboption("DHCP", ListValue, "domain_type",
translate("Local Domain Type:"),
translate("How to treat queries of this local domain"))
dty:value("deny", translate("Denied (nxdomain)"))
dty:value("refuse", translate("Refused"))
dty:value("static", translate("Static (local only)"))
dty:value("transparent", translate("Transparent (local/global)"))
dty:depends({ dhcp_link = "none" })
dty:depends({ dhcp_link = "odhcpd" })
dty = s1:taboption("DHCP", ListValue, "domain_type", translate("Local Domain Type:"),
translate("How to treat queries of this local domain"))
dty:value("deny", translate("Ignored"))
dty:value("refuse", translate("Refused"))
dty:value("static", translate("Only Local"))
dty:value("transparent", translate("Also Forwarded"))
dty:depends({ dhcp_link = "none" })
dty:depends({ dhcp_link = "odhcpd" })
lfq = s1:taboption("DHCP", ListValue, "add_local_fqdn",
translate("LAN DNS:"),
translate("How to enter the LAN or local network router in DNS"))
lfq:value("0", translate("No Entry"))
lfq:value("1", translate("Hostname, Primary Address"))
lfq:value("2", translate("Hostname, All Addresses"))
lfq:value("3", translate("Host FQDN, All Addresses"))
lfq:value("4", translate("Interface FQDN, All Addresses"))
lfq:depends({ dhcp_link = "none" })
lfq:depends({ dhcp_link = "odhcpd" })
lfq = s1:taboption("DHCP", ListValue, "add_local_fqdn", translate("LAN DNS:"),
translate("How to enter the LAN or local network router in DNS"))
lfq:value("0", translate("No Entry"))
lfq:value("1", translate("Hostname, Primary Address"))
lfq:value("2", translate("Hostname, All Addresses"))
lfq:value("3", translate("Host FQDN, All Addresses"))
lfq:value("4", translate("Interface FQDN, All Addresses"))
lfq:depends({ dhcp_link = "none" })
lfq:depends({ dhcp_link = "odhcpd" })
wfq = s1:taboption("DHCP", ListValue, "add_wan_fqdn",
translate("WAN DNS:"),
translate("Override the WAN side router entry in DNS"))
wfq:value("0", translate("Use Upstream"))
wfq:value("1", translate("Hostname, Primary Address"))
wfq:value("2", translate("Hostname, All Addresses"))
wfq:value("3", translate("Host FQDN, All Addresses"))
wfq:value("4", translate("Interface FQDN, All Addresses"))
wfq:depends({ dhcp_link = "none" })
wfq:depends({ dhcp_link = "odhcpd" })
wfq = s1:taboption("DHCP", ListValue, "add_wan_fqdn", translate("WAN DNS:"),
translate("Override the WAN side router entry in DNS"))
wfq:value("0", translate("Use Upstream"))
wfq:value("1", translate("Hostname, Primary Address"))
wfq:value("2", translate("Hostname, All Addresses"))
wfq:value("3", translate("Host FQDN, All Addresses"))
wfq:value("4", translate("Interface FQDN, All Addresses"))
wfq:depends({ dhcp_link = "none" })
wfq:depends({ dhcp_link = "odhcpd" })
exa = s1:taboption("DHCP", ListValue, "add_extra_dns",
translate("Extra DNS:"),
translate("Use extra DNS entries found in /etc/config/dhcp"))
exa:value("0", translate("Ignore"))
exa:value("1", translate("Host Records"))
exa:value("2", translate("Host/MX/SRV RR"))
exa:value("3", translate("Host/MX/SRV/CNAME RR"))
exa:depends({ dhcp_link = "none" })
exa:depends({ dhcp_link = "odhcpd" })
exa = s1:taboption("DHCP", ListValue, "add_extra_dns", translate("Extra DNS:"),
translate("Use extra DNS entries found in /etc/config/dhcp"))
exa:value("0", translate("Ignore"))
exa:value("1", translate("Include Network/Hostnames"))
exa:value("2", translate("Advanced MX/SRV RR"))
exa:value("3", translate("Advanced CNAME RR"))
exa:depends({ dhcp_link = "none" })
exa:depends({ dhcp_link = "odhcpd" })
--TODO: dnsmasq needs to not reference resolve-file and get off port 53.
--TODO: dnsmasq needs to not reference resolve-file and get off port 53.
--Resource Tuning Tab
ctl = s1:taboption("resource", ListValue, "unbound_control",
translate("Unbound Control App:"),
translate("Enable access for unbound-control"))
ctl.rmempty = false
ctl:value("0", translate("No Remote Control"))
ctl:value("1", translate("Local Host, No Encryption"))
ctl:value("2", translate("Local Host, Encrypted"))
ctl:value("3", translate("Local Subnet, Encrypted"))
ctl:value("4", translate("Local Subnet, Static Encryption"))
--Resource Tuning Tab
ctl = s1:taboption("resource", ListValue, "unbound_control", translate("Unbound Control App:"),
translate("Enable access for unbound-control"))
ctl.rmempty = false
ctl:value("0", translate("No Remote Control"))
ctl:value("1", translate("Local Host, No Encryption"))
ctl:value("2", translate("Local Host, Encrypted"))
ctl:value("3", translate("Local Subnet, Encrypted"))
ctl:value("4", translate("Local Subnet, Static Encryption"))
pro = s1:taboption("resource", ListValue, "protocol",
translate("Recursion Protocol:"),
translate("Chose the protocol recursion queries leave on"))
pro:value("default", translate("Default"))
pro:value("ip4_only", translate("IP4 Only"))
pro:value("ip6_only", translate("IP6 Only"))
pro:value("ip6_prefer", translate("IP6 Preferred"))
pro:value("mixed", translate("IP4 and IP6"))
pro.rmempty = false
pro = s1:taboption("resource", ListValue, "protocol", translate("Recursion Protocol:"),
translate("Chose the protocol recursion queries leave on"))
pro:value("default", translate("Default"))
pro:value("ip4_only", translate("IP4 Only"))
pro:value("ip6_only", translate("IP6 Only"))
pro:value("ip6_prefer", translate("IP6 Preferred"))
pro:value("mixed", translate("IP4 and IP6"))
pro.rmempty = false
rsc = s1:taboption("resource", ListValue, "resource",
translate("Memory Resource:"),
translate("Use menu System/Processes to observe any memory growth"))
rsc:value("default", translate("Default"))
rsc:value("tiny", translate("Tiny"))
rsc:value("small", translate("Small"))
rsc:value("medium", translate("Medium"))
rsc:value("large", translate("Large"))
rsc.rmempty = false
rsc = s1:taboption("resource", ListValue, "resource", translate("Memory Resource:"),
translate("Use menu System/Processes to observe any memory growth"))
rsc:value("default", translate("Default"))
rsc:value("tiny", translate("Tiny"))
rsc:value("small", translate("Small"))
rsc:value("medium", translate("Medium"))
rsc:value("large", translate("Large"))
rsc.rmempty = false
rsn = s1:taboption("resource", ListValue, "recursion",
translate("Recursion Strength:"),
translate("Recursion activity affects memory growth and CPU load"))
rsn:value("default", translate("Default"))
rsn:value("passive", translate("Passive"))
rsn:value("aggressive", translate("Aggressive"))
rsn.rmempty = false
rsn = s1:taboption("resource", ListValue, "recursion", translate("Recursion Strength:"),
translate("Recursion activity affects memory growth and CPU load"))
rsn:value("default", translate("Default"))
rsn:value("passive", translate("Passive"))
rsn:value("aggressive", translate("Aggressive"))
rsn.rmempty = false
qry = s1:taboption("resource", Flag, "query_minimize",
translate("Query Minimize:"),
translate("Break down query components for limited added privacy"))
qry.rmempty = false
qry:depends({ recursion = "passive" })
qry:depends({ recursion = "aggressive" })
qry = s1:taboption("resource", Flag, "query_minimize", translate("Query Minimize:"),
translate("Break down query components for limited added privacy"))
qry.rmempty = false
qry:depends({ recursion = "passive" })
qry:depends({ recursion = "aggressive" })
qrs = s1:taboption("resource", Flag, "query_min_strict",
translate("Strict Minimize:"),
translate("Strict version of 'query minimize' but it can break DNS"))
qrs.rmempty = false
qrs:depends({ query_minimize = true })
qrs = s1:taboption("resource", Flag, "query_min_strict", translate("Strict Minimize:"),
translate("Strict version of 'query minimize' but it can break DNS"))
qrs.rmempty = false
qrs:depends({ query_minimize = true })
eds = s1:taboption("resource", Value, "edns_size",
translate("EDNS Size:"),
translate("Limit extended DNS packet size"))
eds.datatype = "and(uinteger,min(512),max(4096))"
eds.rmempty = false
ath = s1:taboption("resource", Flag, "prefetch_root", translate("Prefetch Root:"),
translate("Obtain complete root zone files and install in auth-zone: clause"))
ath.rmempty = false
tlm = s1:taboption("resource", Value, "ttl_min",
translate("TTL Minimum:"),
translate("Prevent excessively short cache periods"))
tlm.datatype = "and(uinteger,min(0),max(600))"
tlm.rmempty = false
eds = s1:taboption("resource", Value, "edns_size", translate("EDNS Size:"),
translate("Limit extended DNS packet size"))
eds.datatype = "and(uinteger,min(512),max(4096))"
eds.rmempty = false
stt = s1:taboption("resource", Flag, "extended_stats",
translate("Extended Statistics:"),
translate("Extended statistics are printed from unbound-control"))
stt.rmempty = false
tlm = s1:taboption("resource", Value, "ttl_min", translate("TTL Minimum:"),
translate("Prevent excessively short cache periods"))
tlm.datatype = "and(uinteger,min(0),max(600))"
tlm.rmempty = false
else
ag2 = s1:taboption("basic", Value, "root_age",
translate("Root DSKEY Age:"),
translate("Limit days between RFC5011 copies to reduce flash writes"))
ag2.datatype = "and(uinteger,min(1),max(99))"
ag2:value("3", "3")
ag2:value("9", "9 ("..translate("default")..")")
ag2:value("12", "12")
ag2:value("24", "24")
ag2:value("99", "99 ("..translate("never")..")")
stt = s1:taboption("resource", Flag, "extended_stats", translate("Extended Statistics:"),
translate("Extended statistics are printed from unbound-control"))
stt.rmempty = false
tgr = s1:taboption("basic", Value, "trigger_interface",
translate("Trigger Networks:"),
translate("Networks that may trigger Unbound to reload (avoid wan6)"))
tgr.template = "cbi/network_netlist"
tgr.widget = "checkbox"
tgr.rmempty = true
tgr.cast = "string"
tgr.nocreate = true
end
--Trigger Tab, always unconditional
ag2 = s1:taboption("trigger", Value, "root_age", translate("Root DSKEY Age:"),
translate("Limit days between RFC5011 copies to reduce flash writes"))
ag2.datatype = "and(uinteger,min(1),max(99))"
ag2:value("3", "3")
ag2:value("9", "9 ("..translate("default")..")")
ag2:value("12", "12")
ag2:value("24", "24")
ag2:value("99", "99 ("..translate("never")..")")
tgr = s1:taboption("trigger", Value, "trigger_interface", translate("Trigger Networks:"),
translate("Networks that may trigger Unbound to reload (avoid wan6)"))
tgr.template = "cbi/network_netlist"
tgr.widget = "checkbox"
tgr.rmempty = true
tgr.cast = "string"
tgr.nocreate = true
function ena.cfgvalue(self, section)
return luci.sys.init.enabled("unbound") and self.enabled or self.disabled
return sy.init.enabled("unbound") and self.enabled or self.disabled
end
function ena.write(self, section, value)
if value == "1" then
luci.sys.init.enable("unbound")
luci.sys.call("/etc/init.d/unbound start >/dev/null")
else
luci.sys.call("/etc/init.d/unbound stop >/dev/null")
luci.sys.init.disable("unbound")
end
if (value == "1") then
sy.init.enable("unbound")
sy.call("/etc/init.d/unbound start >/dev/null 2>&1")
return Flag.write(self, section, value)
else
sy.call("/etc/init.d/unbound stop >/dev/null 2>&1")
sy.init.disable("unbound")
end
return Flag.write(self, section, value)
end
function m1.on_commit(self)
if sy.init.enabled("unbound") then
-- Restart Unbound with configuration
sy.call("/etc/init.d/unbound restart >/dev/null 2>&1")
else
sy.call("/etc/init.d/unbound stop >/dev/null 2>&1")
end
end
function m1.on_apply(self)
function ena.validate(self, value)
if value ~= "0" then
luci.sys.call("/etc/init.d/unbound restart >/dev/null 2>&1")
else
luci.sys.call("/etc/init.d/unbound stop >/dev/null 2>&1")
end
end
-- Restart Unbound with configuration and reload the page (some options hide)
luci.http.redirect(luci.dispatcher.build_url("admin", "services", "unbound"))
-- reload the page because some options hide
ht.redirect(ds.build_url("admin", "services", "unbound", "configure"))
end

View file

@ -1,28 +1,31 @@
-- Copyright 2016 Eric Luehrsen <ericluehrsen@hotmail.com>
-- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m4, s4, frm
local filename = "/etc/unbound/unbound_ext.conf"
local description = translatef("Here you may edit 'forward:' and 'remote-control:' in an extended 'include:'")
description = description .. " (" .. filename .. ")"
local fs = require "nixio.fs"
local ut = require "luci.util"
m4 = SimpleForm("editing", nil)
m4:append(Template("unbound/css-editing"))
m4.submit = translate("Save")
m4.reset = false
s4 = m4:section(SimpleSection, "Unbound Extended Conf", description)
s4 = m4:section(SimpleSection, "",
translatef(
"Edit clauses such as 'forward-zone:' for 'include: " .. filename .. "'"))
frm = s4:option(TextValue, "data")
frm.datatype = "string"
frm.rows = 20
function frm.cfgvalue()
return nixio.fs.readfile(filename) or ""
return fs.readfile(filename) or ""
end
function frm.write(self, section, data)
return nixio.fs.writefile(filename, luci.util.trim(data:gsub("\r\n", "\n")))
return fs.writefile(filename, ut.trim(data:gsub("\r\n", "\n")))
end

View file

@ -1,28 +1,31 @@
-- Copyright 2016 Eric Luehrsen <ericluehrsen@hotmail.com>
-- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m2, s2, frm
local filename = "/etc/unbound/unbound.conf"
local description = translatef("Here you may edit raw 'unbound.conf' when you don't use UCI:")
description = description .. " (" .. filename .. ")"
local fs = require "nixio.fs"
local ut = require "luci.util"
m2 = SimpleForm("editing", nil)
m2:append(Template("unbound/css-editing"))
m2.submit = translate("Save")
m2.reset = false
s2 = m2:section(SimpleSection, "Unbound Conf", description)
s2 = m2:section(SimpleSection, "",
translatef(
"Edit '" .. filename .. "' when you do not use UCI."))
frm = s2:option(TextValue, "data")
frm.datatype = "string"
frm.rows = 20
function frm.cfgvalue()
return nixio.fs.readfile(filename) or ""
return fs.readfile(filename) or ""
end
function frm.write(self, section, data)
return nixio.fs.writefile(filename, luci.util.trim(data:gsub("\r\n", "\n")))
return fs.writefile(filename, ut.trim(data:gsub("\r\n", "\n")))
end

View file

@ -1,28 +1,31 @@
-- Copyright 2016 Eric Luehrsen <ericluehrsen@hotmail.com>
-- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m3, s3, frm
local filename = "/etc/unbound/unbound_srv.conf"
local description = translatef("Here you may edit the 'server:' clause in an internal 'include:'")
description = description .. " (" .. filename .. ")"
local fs = require "nixio.fs"
local ut = require "luci.util"
m3 = SimpleForm("editing", nil)
m3:append(Template("unbound/css-editing"))
m3.submit = translate("Save")
m3.reset = false
s3 = m3:section(SimpleSection, "Unbound Server Conf", description)
s3 = m3:section(SimpleSection, "",
translatef(
"Edit 'server:' clause options for 'include: " .. filename .. "'"))
frm = s3:option(TextValue, "data")
frm.datatype = "string"
frm.rows = 20
function frm.cfgvalue()
return nixio.fs.readfile(filename) or ""
return fs.readfile(filename) or ""
end
function frm.write(self, section, data)
return nixio.fs.writefile(filename, luci.util.trim(data:gsub("\r\n", "\n")))
return fs.writefile(filename, ut.trim(data:gsub("\r\n", "\n")))
end

View file

@ -0,0 +1,37 @@
-- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m6, s6, frm
local filename = "/etc/config/unbound"
local fs = require "nixio.fs"
local ut = require "luci.util"
m6 = SimpleForm("editing", nil)
m6:append(Template("unbound/css-editing"))
m6.submit = translate("Save")
m6.reset = false
s6 = m6:section(SimpleSection, "",
translatef("Edit '" .. filename .. "' "
.. "and help can be found in OpenWrt "
.. "<a href=\"%s\" target=\"_blank\">Guides</a> "
.. "and <a href=\"%s\" target=\"_blank\">Github</a>.",
"https://openwrt.org/docs/guide-user/services/dns/unbound",
"https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md"))
frm = s6:option(TextValue, "data")
frm.datatype = "string"
frm.rows = 20
function frm.cfgvalue()
return fs.readfile(filename) or ""
end
function frm.write(self, section, data)
return fs.writefile(filename, ut.trim(data:gsub("\r\n", "\n")))
end
return m6

View file

@ -0,0 +1,207 @@
-- Copyright 2017 Eric Luehrsen <ericluehrsen@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local m5, s5
local ztype, zones, servers, fallback, enabled
local fs = require "nixio.fs"
local ut = require "luci.util"
local sy = require "luci.sys"
local resolvfile = "/tmp/resolv.conf.auto"
m5 = Map("unbound")
s5 = m5:section(TypedSection, "zone", "Zones",
translatef("This shows extended zones and more details can be "
.. "changed in Files tab and <a href=\"%s\">Edit:UCI</a> subtab.",
"/cgi-bin/luci/admin/services/unbound/files" ))
s5.addremove = false
s5.anonymous = true
s5.sortable = true
s5.template = "cbi/tblsection"
ztype = s5:option(DummyValue, "DummyType", translate("Type"))
ztype.rawhtml = true
zones = s5:option(DummyValue, "DummyZones", translate("Zones"))
zones.rawhtml = true
servers = s5:option(DummyValue, "DummyServers", translate("Servers"))
servers.rawhtml = true
fallback = s5:option(Flag, "fallback", translate("Fallback"))
fallback.rmempty = false
enabled = s5:option(Flag, "enabled", translate("Enable"))
enabled.rmempty = false
function ztype.cfgvalue(self, s)
-- Format a meaninful tile for the Zone Type column
local itxt = self.map:get(s, "zone_type")
local itls = self.map:get(s, "tls_upstream")
if itxt and itxt:match("forward") then
if itls and (itls == "1") then
return translate("Forward TLS")
else
return translate("Forward")
end
elseif itxt and itxt:match("stub") then
return translate("Recurse")
elseif itxt and itxt:match("auth") then
return translate("AXFR")
else
return translate("Error")
end
end
function zones.cfgvalue(self, s)
-- Format a meaninful sentence for the Zones viewed column
local xtxt, otxt
local itxt = self.map:get(s, "zone_name")
local itype = self.map:get(s, "zone_type")
for xtxt in ut.imatch(itxt) do
if (xtxt == ".") then
-- zone_name lists
xtxt = translate("(root)")
end
if otxt and (#otxt > 0) then
otxt = otxt .. ", <var>%s</var>" % xtxt
else
otxt = "<var>%s</var>" % xtxt
end
end
if itype and itype:match("forward") then
-- from zone_type create a readable hint for the action
otxt = translate("accept upstream results for ") .. otxt
elseif itype and itype:match("stub") then
otxt = translate("select recursion for ") .. otxt
elseif itype and itype:match("auth") then
otxt = translate("prefetch zone files for ") .. otxt
else
otxt = translate("unknown action for ") .. otxt
end
if otxt and (#otxt > 0) then
return otxt
else
return "(empty)"
end
end
function servers.cfgvalue(self, s)
-- Format a meaninful sentence for the Servers (and URL) column
local xtxt, otxt, rtxt, found
local itxt = self.map:get(s, "server")
local iurl = self.map:get(s, "url_dir")
local itype = self.map:get(s, "zone_type")
local itls = self.map:get(s, "tls_upstream")
local iidx = self.map:get(s, "tls_index")
local irslv = self.map:get(s, "resolv_conf")
for xtxt in ut.imatch(itxt) do
if otxt and (#otxt > 0) then
-- bundle and make pretty the server list
otxt = otxt .. ", <var>%s</var>" % xtxt
else
otxt = "<var>%s</var>" % xtxt
end
end
if otxt and (#otxt > 0)
and itls and (itls == "1")
and iidx and (#iidx > 0) then
-- show TLS certificate name index if provided
otxt = translatef("use nameservers by <var>%s</var> at ", iidx) .. otxt
elseif otxt and (#otxt > 0) then
otxt = translate("use nameservers ") .. otxt
end
if iurl and (#iurl > 0) and itype and itype:match("auth") then
if otxt and (#otxt > 0) then
-- include optional URL filed for auth-zone: type
otxt = otxt .. translatef(", and try <var>%s</var>", iurl)
else
otxt = translatef("download from <var>%s</var>", iurl)
end
end
if irslv and (irslv == "1") and itype and itype:match("forward") then
for xtxt in ut.imatch(fs.readfile(resolvfile)) do
if xtxt:match("nameserver") then
found = true
elseif (found == true) then
if rtxt and (#rtxt > 0) then
-- fetch name servers from resolv.conf
rtxt = rtxt .. ", <var>%s</var>" % xtxt
else
rtxt = "<var>%s</var>" % xtxt
end
found = false
end
end
if otxt and (#otxt > 0) and rtxt and (#rtxt > 0) then
otxt = otxt
.. translatef(", and <var>%s</var> entries ", resolvfile) .. rtxt
elseif rtxt and (#rtxt > 0) then
otxt = translatef("use <var>%s</var> nameservers ", resolvfile) .. rtxt
end
end
if otxt and (#otxt > 0) then
return otxt
else
return "(empty)"
end
end
function m5.on_commit(self)
if sy.init.enabled("unbound") then
-- Restart Unbound with configuration
sy.call("/etc/init.d/unbound restart >/dev/null 2>&1")
else
sy.call("/etc/init.d/unbound stop >/dev/null 2>&1")
end
end
return m5