From dec88df41c938de1064d0bf2001c0ec7002e7ab2 Mon Sep 17 00:00:00 2001 From: Eric Luehrsen Date: Sun, 12 Aug 2018 11:13:31 -0400 Subject: [PATCH] luci-app-unbound: add zone-details combined tab Signed-off-by: Eric Luehrsen --- .../luasrc/controller/unbound.lua | 5 +- .../luasrc/model/cbi/unbound/configure.lua | 125 +++++++++--------- .../luasrc/model/cbi/unbound/uciedit.lua | 2 +- .../luasrc/model/cbi/unbound/zone-details.lua | 95 +++++++++++++ .../luasrc/model/cbi/unbound/zones.lua | 73 ++++++---- 5 files changed, 214 insertions(+), 86 deletions(-) create mode 100644 applications/luci-app-unbound/luasrc/model/cbi/unbound/zone-details.lua diff --git a/applications/luci-app-unbound/luasrc/controller/unbound.lua b/applications/luci-app-unbound/luasrc/controller/unbound.lua index ea3d26b91..771385b79 100644 --- a/applications/luci-app-unbound/luasrc/controller/unbound.lua +++ b/applications/luci-app-unbound/luasrc/controller/unbound.lua @@ -28,7 +28,8 @@ function index() if (valman == "0") then entry({"admin", "services", "unbound", "zones"}, - cbi("unbound/zones"), _("Zones"), 15) + arcombine(cbi("unbound/zones"), cbi("unbound/zone-details")), + _("Zones"), 15).leaf = true end @@ -106,7 +107,7 @@ end function QuerySysLog() - local lcldata = luci.util.exec("logread | grep -i unbound") + local lcldata = luci.util.exec("logread -e 'unbound'") local lcldesc = luci.i18n.translate( "This shows syslog filtered for events involving Unbound.") diff --git a/applications/luci-app-unbound/luasrc/model/cbi/unbound/configure.lua b/applications/luci-app-unbound/luasrc/model/cbi/unbound/configure.lua index f665a2c9d..8170f3d2b 100644 --- a/applications/luci-app-unbound/luasrc/model/cbi/unbound/configure.lua +++ b/applications/luci-app-unbound/luasrc/model/cbi/unbound/configure.lua @@ -19,10 +19,12 @@ 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"), +s1 = m1:section(TypedSection, "unbound", translate("Recursive DNS"), translatef("Unbound (NLnet Labs)" - .. " is a validating, recursive, and caching DNS resolver.", - "https://www.unbound.net/")) + .. " is a validating, recursive, and caching DNS resolver" + .. " (help).", + "https://www.unbound.net/", + "https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md")) s1.addremove = false s1.anonymous = true @@ -40,11 +42,11 @@ end --Basic Tab, unconditional pieces -ena = s1:taboption("basic", Flag, "enabled", translate("Enable Unbound:"), +ena = s1:taboption("basic", Flag, "enabled", translate("Enable Unbound"), translate("Enable the initialization scripts for Unbound")) ena.rmempty = false -mcf = s1:taboption("basic", Flag, "manual_conf", translate("Manual Conf:"), +mcf = s1:taboption("basic", Flag, "manual_conf", translate("Manual Conf"), translate("Skip UCI and use /etc/unbound/unbound.conf")) mcf.rmempty = false @@ -53,60 +55,60 @@ if (valman == "0") then -- Not in manual configuration mode; show UCI --Basic Tab lsv = s1:taboption("basic", Flag, "localservice", - translate("Local Service:"), + translate("Local Service"), translate("Accept queries only from local subnets")) lsv.rmempty = false vld = s1:taboption("basic", Flag, "validator", - translate("Enable DNSSEC:"), + translate("Enable DNSSEC"), translate("Enable the DNSSEC validator module")) vld.rmempty = false nvd = s1:taboption("basic", Flag, "validator_ntp", - translate("DNSSEC NTP Fix:"), + translate("DNSSEC NTP Fix"), translate("Break the loop where DNSSEC needs NTP and NTP needs DNS")) - nvd.rmempty = false - nvd:depends({ validator = true }) + nvd.optional = true + nvd:depends("validator", true) prt = s1:taboption("basic", Value, "listen_port", - translate("Listening Port:"), + translate("Listening Port"), translate("Choose Unbounds listening port")) prt.datatype = "port" - prt.rmempty = false + prt.placeholder = "53" --Avanced Tab rlh = s1:taboption("advanced", Flag, "rebind_localhost", - translate("Filter Localhost Rebind:"), + translate("Filter Localhost Rebind"), translate("Protect against upstream response of 127.0.0.0/8")) rlh.rmempty = false rpv = s1:taboption("advanced", ListValue, "rebind_protection", - translate("Filter Private Rebind:"), + 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("1", translate("Filter Private Address")) rpv:value("2", translate("Filter Entire Subnet")) rpv.rmempty = false - d64 = s1:taboption("advanced", Flag, "dns64", translate("Enable DNS64:"), + d64 = s1:taboption("advanced", Flag, "dns64", translate("Enable DNS64"), translate("Enable the DNS64 module")) d64.rmempty = false pfx = s1:taboption("advanced", Value, "dns64_prefix", - translate("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 }) + pfx:depends("dns64", true) din = s1:taboption("advanced", DynamicList, "domain_insecure", - translate("Domain Insecure:"), + translate("Domain Insecure"), translate("List domains to bypass checks of DNSSEC")) - din:depends({ validator = true }) + din:depends("validator", true) ag2 = s1:taboption("advanced", Value, "root_age", - translate("Root DSKEY 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") @@ -116,7 +118,7 @@ if (valman == "0") then ag2:value("99", "99 ("..translate("never")..")") tgr = s1:taboption("advanced", Value, "trigger_interface", - translate("Trigger Networks:"), + translate("Trigger Networks"), translate("Networks that may trigger Unbound to reload (avoid wan6)")) tgr.template = "cbi/network_netlist" tgr.widget = "checkbox" @@ -126,7 +128,7 @@ if (valman == "0") then --DHCP Tab dlk = s1:taboption("DHCP", ListValue, "dhcp_link", - translate("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") @@ -134,65 +136,70 @@ if (valman == "0") then dlk.rmempty = false dp6 = s1:taboption("DHCP", Flag, "dhcp4_slaac6", - translate("DHCPv4 to SLAAC:"), + translate("DHCPv4 to SLAAC"), translate("Use DHCPv4 MAC to discover IP6 hosts SLAAC (EUI64)")) - dp6.rmempty = false - dp6:depends({ dhcp_link = "odhcpd" }) + dp6.optional = true + dp6:depends("dhcp_link", "odhcpd") dom = s1:taboption("DHCP", Value, "domain", - translate("Local 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.optional = true + dom:depends("dhcp_link", "none") + dom:depends("dhcp_link", "odhcpd") dty = s1:taboption("DHCP", ListValue, "domain_type", - translate("Local Domain Type:"), + translate("Local Domain Type"), translate("How to treat queries of this local domain")) + dty.optional = true 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:depends("dhcp_link", "none") + dty:depends("dhcp_link", "odhcpd") lfq = s1:taboption("DHCP", ListValue, "add_local_fqdn", - translate("LAN DNS:"), + translate("LAN DNS"), translate("How to enter the LAN or local network router in DNS")) + lfq.optional = true 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:depends("dhcp_link", "none") + lfq:depends("dhcp_link", "odhcpd") wfq = s1:taboption("DHCP", ListValue, "add_wan_fqdn", - translate("WAN DNS:"), + translate("WAN DNS"), translate("Override the WAN side router entry in DNS")) + wfq.optional = true 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:depends("dhcp_link", "none") + wfq:depends("dhcp_link", "odhcpd") exa = s1:taboption("DHCP", ListValue, "add_extra_dns", - translate("Extra DNS:"), + translate("Extra DNS"), translate("Use extra DNS entries found in /etc/config/dhcp")) + exa.optional = true 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:depends("dhcp_link", "none") + exa:depends("dhcp_link", "odhcpd") --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("Unbound Control App"), translate("Enable access for unbound-control")) ctl.rmempty = false ctl:value("0", translate("No Remote Control")) @@ -202,7 +209,7 @@ if (valman == "0") then ctl:value("4", translate("Local Subnet, Static Encryption")) pro = s1:taboption("resource", ListValue, "protocol", - translate("Recursion Protocol:"), + translate("Recursion Protocol"), translate("Chose the protocol recursion queries leave on")) pro:value("default", translate("Default")) pro:value("ip4_only", translate("IP4 Only")) @@ -212,7 +219,7 @@ if (valman == "0") then pro.rmempty = false rsc = s1:taboption("resource", ListValue, "resource", - translate("Memory Resource:"), + translate("Memory Resource"), translate("Use menu System/Processes to observe any memory growth")) rsc:value("default", translate("Default")) rsc:value("tiny", translate("Tiny")) @@ -222,7 +229,7 @@ if (valman == "0") then rsc.rmempty = false rsn = s1:taboption("resource", ListValue, "recursion", - translate("Recursion Strength:"), + translate("Recursion Strength"), translate("Recursion activity affects memory growth and CPU load")) rsn:value("default", translate("Default")) rsn:value("passive", translate("Passive")) @@ -230,38 +237,38 @@ if (valman == "0") then rsn.rmempty = false qry = s1:taboption("resource", Flag, "query_minimize", - translate("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.optional = true + qry:depends("recursion", "passive") + qry:depends("recursion", "aggressive") qrs = s1:taboption("resource", Flag, "query_min_strict", - translate("Strict Minimize:"), + translate("Strict Minimize"), translate("Strict version of 'query minimize' but it can break DNS")) - qrs.rmempty = false - qrs:depends({ query_minimize = true }) + qrs.optional = true + 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 + eds.placeholder = "1280" tlm = s1:taboption("resource", Value, "ttl_min", - translate("TTL Minimum:"), + translate("TTL Minimum"), translate("Prevent excessively short cache periods")) - tlm.datatype = "and(uinteger,min(0),max(600))" - tlm.rmempty = false + tlm.datatype = "and(uinteger,min(0),max(1200))" + tlm.placeholder = "120" stt = s1:taboption("resource", Flag, "extended_stats", - translate("Extended Statistics:"), + translate("Extended Statistics"), translate("Extended statistics are printed from unbound-control")) stt.rmempty = false else ag2 = s1:taboption("basic", Value, "root_age", - translate("Root DSKEY 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") @@ -271,7 +278,7 @@ else ag2:value("99", "99 ("..translate("never")..")") tgr = s1:taboption("basic", Value, "trigger_interface", - translate("Trigger Networks:"), + translate("Trigger Networks"), translate("Networks that may trigger Unbound to reload (avoid wan6)")) tgr.template = "cbi/network_netlist" tgr.widget = "checkbox" diff --git a/applications/luci-app-unbound/luasrc/model/cbi/unbound/uciedit.lua b/applications/luci-app-unbound/luasrc/model/cbi/unbound/uciedit.lua index 3aef18965..f1e1d842a 100644 --- a/applications/luci-app-unbound/luasrc/model/cbi/unbound/uciedit.lua +++ b/applications/luci-app-unbound/luasrc/model/cbi/unbound/uciedit.lua @@ -12,7 +12,7 @@ m6.submit = translate("Save") m6.reset = false s6 = m6:section(SimpleSection, "", translatef("Edit '" .. filename .. "' " - .. "and help can be found in OpenWrt " + .. "and recipes can be found in OpenWrt " .. "Guides " .. "and Github.", "https://openwrt.org/docs/guide-user/services/dns/unbound", diff --git a/applications/luci-app-unbound/luasrc/model/cbi/unbound/zone-details.lua b/applications/luci-app-unbound/luasrc/model/cbi/unbound/zone-details.lua new file mode 100644 index 000000000..dcaa877fd --- /dev/null +++ b/applications/luci-app-unbound/luasrc/model/cbi/unbound/zone-details.lua @@ -0,0 +1,95 @@ +-- Copyright 2018 Eric Luehrsen +-- Licensed to the public under the Apache License 2.0. + +local sy = require "luci.sys" +local ds = require "luci.dispatcher" +local hp = require "luci.http" +local m7, s7 +local ena, flb, zty, znm, srv, rlv, tlu +local prt, tlp, tli, url + +arg[1] = arg[1] or "" +m7 = Map("unbound") +m7.redirect = ds.build_url("admin/services/unbound/zones") + + +if (arg[1] == "") then + hp.redirect(m7.redirect) + return + +else + s7 = m7:section(NamedSection, arg[1], "zone", + translatef("Directed Zone"), + translatef("Edit a forward, stub, or zone-file-cache zone " + .. "for Unbound to use instead of recursion.")) + + s7.anonymous = true + s7.addremove = false + + ena = s7:option(Flag, "enabled", translate("Enabled"), + translate("Enable this directed zone")) + ena.rmempty = false + + flb = s7:option(Flag, "fallback", translate("Fall Back"), + translate("Allow open recursion when record not in zone")) + flb.rmempty = false + + zty = s7:option(ListValue, "zone_type", translate("Zone Type")) + zty:value("auth_zone", translate("Authoritative (zone file)")) + zty:value("stub_zone", translate("Stub (forced recursion)")) + zty:value("forward_zone", translate("Forward (simple handoff)")) + zty.rmempty = false + + znm = s7:option(DynamicList, "zone_name", translate("Zone Names"), + translate("Zone (Domain) names included in this zone combination")) + znm.placeholder="new.example.net." + + srv = s7:option(DynamicList, "server", translate("Servers"), + translate("Servers for this zone; see README.md for optional form")) + srv.placeholder="192.0.2.53" + + rlv = s7:option(Flag, "resolv_conf", translate("Use 'resolv.conf.auto'"), + translate("Forward to upstream nameservers (ISP)")) + rlv:depends("zone_type", "forward_zone") + + tlu = s7:option(Flag, "tls_upstream", translate("DNS over TLS"), + translate("Connect to servers using TLS")) + tlu:depends("zone_type", "forward_zone") + + prt = s7:option(Value, "port", translate("Server Port"), + translate("Port servers will receive queries on")) + prt:depends("tls_upstream", false) + prt.datatype = "port" + prt.placeholder="53" + + tlp = s7:option(Value, "tls_port", translate("Server TLS Port"), + translate("Port servers will receive queries on")) + tlp:depends("tls_upstream", true) + tlp.datatype = "port" + tlp.placeholder="853" + + tli = s7:option(Value, "tls_index", translate("TLS Name Index"), + translate("Domain name to verify TLS certificate")) + tli:depends("tls_upstream", true) + tli.placeholder="dns.example.net" + + url = s7:option(Value, "url_dir", translate("Zone Download URL"), + translate("Directory only part of URL")) + url:depends("zone_type", "auth_zone") + url.placeholder="https://www.example.net/dl/zones/" +end + + +function m7.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 m7 + diff --git a/applications/luci-app-unbound/luasrc/model/cbi/unbound/zones.lua b/applications/luci-app-unbound/luasrc/model/cbi/unbound/zones.lua index bbc0e2335..798ca6a45 100644 --- a/applications/luci-app-unbound/luasrc/model/cbi/unbound/zones.lua +++ b/applications/luci-app-unbound/luasrc/model/cbi/unbound/zones.lua @@ -1,4 +1,4 @@ --- Copyright 2017 Eric Luehrsen +-- Copyright 2018 Eric Luehrsen -- Licensed to the public under the Apache License 2.0. local m5, s5 @@ -7,18 +7,22 @@ local ztype, zones, servers, fallback, enabled local fs = require "nixio.fs" local ut = require "luci.util" local sy = require "luci.sys" +local ds = require "luci.dispatcher" local resolvfile = "/tmp/resolv.conf.auto" +local logerr = ut.exec("logread -e 'unbound.*error.*ssl library'") m5 = Map("unbound") s5 = m5:section(TypedSection, "zone", "Zones", - translatef("This shows extended zones and more details can be " - .. "changed in Files tab and Edit:UCI subtab.", - "/cgi-bin/luci/admin/services/unbound/files" )) + translatef("Organize directed forward, stub, and authoritative zones" + .. " (help).", + "https://www.unbound.net/", + "https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md")) -s5.addremove = false +s5.addremove = true s5.anonymous = true s5.sortable = true s5.template = "cbi/tblsection" +s5.extedit = ds.build_url("admin/services/unbound/zones/%s") ztype = s5:option(DummyValue, "DummyType", translate("Type")) ztype.rawhtml = true @@ -36,6 +40,23 @@ enabled = s5:option(Flag, "enabled", translate("Enable")) enabled.rmempty = false +if logerr and (#logerr > 0) then + logerr = logerr:sub((1 + #logerr - math.min(#logerr, 250)), #logerr) + m5.message = translatef( "Note: SSL/TLS library is missing an API. " + .. "Please review syslog. >> logread ... " .. logerr ) +end + + +function s5.create(self, section) + created = TypedSection.create(self, section) +end + + +function s5.parse(self, ...) + TypedSection.parse(self, ...) +end + + function ztype.cfgvalue(self, s) -- Format a meaninful tile for the Zone Type column local itxt = self.map:get(s, "zone_type") @@ -57,7 +78,7 @@ function ztype.cfgvalue(self, s) return translate("AXFR") else - return translate("Error") + return translate("Undefined") end end @@ -85,22 +106,22 @@ function zones.cfgvalue(self, s) 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 + if otxt and (#otxt > 0) then + 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("stub") then + otxt = translate("select recursion for ") .. otxt - elseif itype and itype:match("auth") then - otxt = translate("prefetch zone files for ") .. otxt + elseif itype and itype:match("auth") then + otxt = translate("prefetch zone files for ") .. otxt - else - otxt = translate("unknown action for ") .. otxt - end + else + otxt = translate("unknown action for ") .. otxt + end - if otxt and (#otxt > 0) then return otxt else @@ -131,14 +152,17 @@ function servers.cfgvalue(self, s) end + if otxt and (#otxt > 0) then + otxt = translate("use nameservers ") .. otxt + 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 %s at ", iidx) .. otxt - - elseif otxt and (#otxt > 0) then - otxt = translate("use nameservers ") .. otxt + otxt = otxt .. translatef( + " with default certificate for %s", iidx) end @@ -174,11 +198,12 @@ function servers.cfgvalue(self, s) if otxt and (#otxt > 0) and rtxt and (#rtxt > 0) then - otxt = otxt - .. translatef(", and %s entries ", resolvfile) .. rtxt + otxt = otxt .. translatef( + ", and %s entries ", resolvfile) .. rtxt elseif rtxt and (#rtxt > 0) then - otxt = translatef("use %s nameservers ", resolvfile) .. rtxt + otxt = translatef( + "use %s nameservers ", resolvfile) .. rtxt end end -- 2.25.1