applications/luci-firewall: complete rework

This commit is contained in:
Jo-Philipp Wich 2010-11-01 19:45:54 +00:00
parent 26ba050939
commit b04c13bdcc
6 changed files with 384 additions and 208 deletions

View file

@ -5,9 +5,9 @@ function index()
local i18n = luci.i18n.translate
entry({"admin", "network", "firewall"}, alias("admin", "network", "firewall", "zones"), i18n("Firewall"), 60).i18n = "luci-fw"
entry({"admin", "network", "firewall", "zones"}, cbi("luci_fw/zones"), i18n("Zones"), 10)
entry({"admin", "network", "firewall", "redirect"}, arcombine(cbi("luci_fw/redirect"), cbi("luci_fw/rrule")), i18n("Traffic Redirection"), 30).leaf = true
entry({"admin", "network", "firewall", "rule"}, arcombine(cbi("luci_fw/traffic"), cbi("luci_fw/trule")), i18n("Traffic Control"), 20).leaf = true
entry({"admin", "network", "firewall", "zones"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/zone")), nil, 10).leaf = true
entry({"admin", "network", "firewall", "rule"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/trule")), nil, 20).leaf = true
entry({"admin", "network", "firewall", "redirect"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/rrule")), nil, 30).leaf = true
entry({"mini", "network", "portfw"}, cbi("luci_fw/miniportfw", {autoapply=true}), i18n("Port forwarding"), 70).i18n = "luci-fw"
end
end

View file

@ -1,52 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
require("luci.sys")
m = Map("firewall", translate("Traffic Redirection"),
translate("Traffic redirection allows you to change the " ..
"destination address of forwarded packets."))
s = m:section(TypedSection, "redirect", "")
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.extedit = luci.dispatcher.build_url("admin", "network", "firewall", "redirect", "%s")
name = s:option(Value, "_name", translate("Name"), translate("(optional)"))
name.size = 10
iface = s:option(ListValue, "src", translate("Zone"))
iface.default = "wan"
luci.model.uci.cursor():foreach("firewall", "zone",
function (section)
iface:value(section.name)
end)
proto = s:option(ListValue, "proto", translate("Protocol"))
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("tcpudp", "TCP+UDP")
dport = s:option(Value, "src_dport", translate("Source port"))
dport.size = 5
to = s:option(Value, "dest_ip", translate("Destination IP"))
for i, dataset in ipairs(luci.sys.net.arptable()) do
to:value(dataset["IP address"])
end
toport = s:option(Value, "dest_port", translate("Destination port"))
toport.size = 5
return m

View file

@ -2,6 +2,7 @@
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -11,70 +12,130 @@ You may obtain a copy of the License at
$Id$
]]--
require("luci.sys")
local sys = require "luci.sys"
local dsp = require "luci.dispatcher"
arg[1] = arg[1] or ""
m = Map("firewall", translate("Traffic Redirection"),
translate("Traffic redirection allows you to change the " ..
"destination address of forwarded packets."))
m.redirect = dsp.build_url("admin", "network", "firewall")
if not m.uci:get(arg[1]) == "redirect" then
luci.http.redirect(m.redirect)
return
end
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
local wan_zone = nil
m.uci:foreach("firewall", "zone",
function(s)
local n = s.network or s.name
if n then
local i
for i in n:gmatch("%S+") do
if i == "wan" then
wan_zone = s.name
return false
end
end
end
end)
s = m:section(NamedSection, arg[1], "redirect", "")
s.anonymous = true
s.addremove = false
back = s:option(DummyValue, "_overview", translate("Overview"))
s:tab("general", translate("General Settings"))
s:tab("advanced", translate("Advanced Settings"))
back = s:taboption("general", DummyValue, "_overview", translate("Overview"))
back.value = ""
back.titleref = luci.dispatcher.build_url("admin", "network", "firewall", "redirect")
name = s:option(Value, "_name", translate("Name"))
name = s:taboption("general", Value, "_name", translate("Name"))
name.rmempty = true
name.size = 10
iface = s:option(ListValue, "src", translate("Source zone"))
iface.default = "wan"
luci.model.uci.cursor():foreach("firewall", "zone",
function (section)
iface:value(section.name)
end)
s:option(Value, "src_ip", translate("Source IP address")).optional = true
s:option(Value, "src_mac", translate("Source MAC-address")).optional = true
src = s:taboption("general", Value, "src", translate("Source zone"))
src.nocreate = true
src.default = "wan"
src.template = "cbi/firewall_zonelist"
sport = s:option(Value, "src_port", translate("Source port"),
translate("Match incoming traffic originating from the given " ..
"source port or port range on the client host"))
sport.optional = true
sport:depends("proto", "tcp")
sport:depends("proto", "udp")
sport:depends("proto", "tcpudp")
proto = s:option(ListValue, "proto", translate("Protocol"))
proto = s:taboption("general", ListValue, "proto", translate("Protocol"))
proto.optional = true
proto:value("")
proto:value("tcpudp", "TCP+UDP")
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("tcpudp", "TCP+UDP")
dport = s:option(Value, "src_dport", translate("External port"),
dport = s:taboption("general", Value, "src_dport", translate("External port"),
translate("Match incoming traffic directed at the given " ..
"destination port or port range on this host"))
dport.size = 5
dport.datatype = "portrange"
dport:depends("proto", "tcp")
dport:depends("proto", "udp")
dport:depends("proto", "tcpudp")
to = s:option(Value, "dest_ip", translate("Internal IP address"),
to = s:taboption("general", Value, "dest_ip", translate("Internal IP address"),
translate("Redirect matched incoming traffic to the specified " ..
"internal host"))
to.datatype = "ip4addr"
for i, dataset in ipairs(luci.sys.net.arptable()) do
to:value(dataset["IP address"])
end
toport = s:option(Value, "dest_port", translate("Internal port (optional)"),
toport = s:taboption("general", Value, "dest_port", translate("Internal port (optional)"),
translate("Redirect matched incoming traffic to the given port on " ..
"the internal host"))
toport.optional = true
toport.size = 5
target = s:taboption("advanced", ListValue, "target", translate("Redirection type"))
target:value("DNAT")
target:value("SNAT")
dest = s:taboption("advanced", Value, "dest", translate("Destination zone"))
dest.nocreate = true
dest.default = "lan"
dest.template = "cbi/firewall_zonelist"
src_dip = s:taboption("advanced", Value, "src_dip",
translate("Intended destination address"),
translate(
"For DNAT, match incoming traffic directed at the given destination "..
"ip address. For SNAT rewrite the source address to the given address."
))
src_dip.optional = true
src_dip.datatype = "ip4addr"
src_mac = s:taboption("advanced", Value, "src_mac", translate("Source MAC address"))
src_mac.optional = true
src_mac.datatype = "macaddr"
src_ip = s:taboption("advanced", Value, "src_ip", translate("Source IP address"))
src_ip.optional = true
src_ip.datatype = "ip4addr"
sport = s:taboption("advanced", Value, "src_port", translate("Source port"),
translate("Match incoming traffic originating from the given " ..
"source port or port range on the client host"))
sport.optional = true
sport.datatype = "portrange"
sport:depends("proto", "tcp")
sport:depends("proto", "udp")
sport:depends("proto", "tcpudp")
reflection = s:taboption("advanced", Flag, "reflection", translate("Enable NAT Loopback"))
reflection.rmempty = true
reflection:depends({ target = "DNAT", src = wan_zone })
reflection.cfgvalue = function(...)
return Flag.cfgvalue(...) or "1"
end
return m

View file

@ -1,88 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
m = Map("firewall", translate("Traffic Control"))
s = m:section(TypedSection, "forwarding", translate("Zone-to-Zone traffic"),
translate("Here you can specify which network traffic is allowed " ..
"to flow between network zones. Only new connections will " ..
"be matched. Packets belonging to already open " ..
"connections are automatically allowed to pass the " ..
"firewall. If you experience occasional connection " ..
"problems try enabling MSS Clamping otherwise disable it " ..
"for performance reasons."))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
iface = s:option(ListValue, "src", translate("Source"))
oface = s:option(ListValue, "dest", translate("Destination"))
luci.model.uci.cursor():foreach("firewall", "zone",
function (section)
iface:value(section.name)
oface:value(section.name)
end)
s = m:section(TypedSection, "rule", translate("Rules"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
s.extedit = luci.dispatcher.build_url("admin", "network", "firewall", "rule", "%s")
s.defaults.target = "ACCEPT"
local created = nil
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
if created then
m.uci:save("firewall")
luci.http.redirect(luci.dispatcher.build_url(
"admin", "network", "firewall", "rule", created
))
end
end
s:option(DummyValue, "_name", translate("Name"))
s:option(DummyValue, "proto", translate("Protocol"))
src = s:option(DummyValue, "src", translate("Source"))
function src.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "src") or "*",
self.map:get(s, "src_ip") or "0.0.0.0/0",
self.map:get(s, "src_port") or "*"
}
end
dest = s:option(DummyValue, "dest", translate("Destination"))
function dest.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "dest") or translate("Device"),
self.map:get(s, "dest_ip") or "0.0.0.0/0",
self.map:get(s, "dest_port") or "*"
}
end
s:option(DummyValue, "target", translate("Action"))
return m

View file

@ -2,6 +2,7 @@
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -11,62 +12,121 @@ You may obtain a copy of the License at
$Id$
]]--
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
local dsp = require "luci.dispatcher"
arg[1] = arg[1] or ""
m = Map("firewall", translate("Advanced Rules"),
translate("Advanced rules let you customize the firewall to your " ..
"needs. Only new connections will be matched. Packets " ..
"belonging to already open connections are automatically " ..
"allowed to pass the firewall."))
m.redirect = dsp.build_url("admin", "network", "firewall")
if not m.uci:get(arg[1]) == "rule" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, arg[1], "rule", "")
s.anonymous = true
s.addremove = false
s:tab("general", translate("General Settings"))
s:tab("advanced", translate("Advanced Options"))
back = s:option(DummyValue, "_overview", translate("Overview"))
back.value = ""
back.titleref = luci.dispatcher.build_url("admin", "network", "firewall", "rule")
back.titleref = dsp.build_url("admin", "network", "firewall", "rule")
name = s:option(Value, "_name", translate("Name").." "..translate("(optional)"))
name = s:taboption("general", Value, "_name", translate("Name").." "..translate("(optional)"))
name.rmempty = true
iface = s:option(ListValue, "src", translate("Source zone"))
iface.rmempty = true
src = s:taboption("general", Value, "src", translate("Source zone"))
src.nocreate = true
src.default = "wan"
src.template = "cbi/firewall_zonelist"
oface = s:option(ListValue, "dest", translate("Destination zone"))
oface:value("", translate("any"))
oface.rmempty = true
dest = s:taboption("advanced", Value, "dest", translate("Destination zone"))
dest.nocreate = true
dest.default = "lan"
dest.template = "cbi/firewall_zonelist"
luci.model.uci.cursor():foreach("firewall", "zone",
function (section)
iface:value(section.name)
oface:value(section.name)
end)
proto = s:option(Value, "proto", translate("Protocol"))
proto = s:taboption("general", Value, "proto", translate("Protocol"))
proto.optional = true
proto:value("")
proto:value("all", translate("Any"))
proto:value("tcpudp", "TCP+UDP")
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("icmp", "ICMP")
s:option(Value, "src_ip", translate("Source address")).optional = true
s:option(Value, "dest_ip", translate("Destination address")).optional = true
s:option(Value, "src_mac", translate("Source MAC-address")).optional = true
icmpt = s:taboption("general", Value, "icmp_type", translate("Match ICMP type"))
icmpt:depends("proto", "icmp")
icmpt:value("any")
icmpt:value("echo-reply")
icmpt:value("destination-unreachable")
icmpt:value("network-unreachable")
icmpt:value("host-unreachable")
icmpt:value("protocol-unreachable")
icmpt:value("port-unreachable")
icmpt:value("fragmentation-needed")
icmpt:value("source-route-failed")
icmpt:value("network-unknown")
icmpt:value("host-unknown")
icmpt:value("network-prohibited")
icmpt:value("host-prohibited")
icmpt:value("TOS-network-unreachable")
icmpt:value("TOS-host-unreachable")
icmpt:value("communication-prohibited")
icmpt:value("host-precedence-violation")
icmpt:value("precedence-cutoff")
icmpt:value("source-quench")
icmpt:value("redirect")
icmpt:value("network-redirect")
icmpt:value("host-redirect")
icmpt:value("TOS-network-redirect")
icmpt:value("TOS-host-redirect")
icmpt:value("echo-request")
icmpt:value("router-advertisement")
icmpt:value("router-solicitation")
icmpt:value("time-exceeded")
icmpt:value("ttl-zero-during-transit")
icmpt:value("ttl-zero-during-reassembly")
icmpt:value("parameter-problem")
icmpt:value("ip-header-bad")
icmpt:value("required-option-missing")
icmpt:value("timestamp-request")
icmpt:value("timestamp-reply")
icmpt:value("address-mask-request")
icmpt:value("address-mask-reply")
sport = s:option(Value, "src_port", translate("Source port"))
src_ip = s:taboption("general", Value, "src_ip", translate("Source address"))
src_ip.optional = true
src_ip.datatype = has_v2 and "ipaddr" or "ip4addr"
sport = s:taboption("general", Value, "src_port", translate("Source port"))
sport.optional = true
sport.datatype = "portrange"
sport:depends("proto", "tcp")
sport:depends("proto", "udp")
sport:depends("proto", "tcpudp")
dport = s:option(Value, "dest_port", translate("Destination port"))
dest_ip = s:taboption("general", Value, "dest_ip", translate("Destination address"))
dest_ip.optional = true
dest_ip.datatype = has_v2 and "ipaddr" or "ip4addr"
dport = s:taboption("general", Value, "dest_port", translate("Destination port"))
dport.optional = true
dport.datatype = "portrange"
dport:depends("proto", "tcp")
dport:depends("proto", "udp")
dport:depends("proto", "tcpudp")
jump = s:option(ListValue, "target", translate("Action"))
jump = s:taboption("general", ListValue, "target", translate("Action"))
jump.rmempty = true
jump.default = "ACCEPT"
jump:value("DROP", translate("drop"))
@ -74,4 +134,14 @@ jump:value("ACCEPT", translate("accept"))
jump:value("REJECT", translate("reject"))
s:taboption("advanced", Value, "src_mac", translate("Source MAC-address")).optional = true
if has_v2 then
family = s:taboption("advanced", ListValue, "family", translate("Restrict to address family"))
family.rmempty = true
family:value("", translate("IPv4 and IPv6"))
family:value("ipv4", translate("IPv4 only"))
family:value("ipv6", translate("IPv6 only"))
end
return m

View file

@ -14,6 +14,9 @@ $Id$
local nw = require "luci.model.network"
local fw = require "luci.model.firewall"
local ds = require "luci.dispatcher"
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
require("luci.tools.webadmin")
m = Map("firewall", translate("Firewall"), translate("The firewall creates zones over your network interfaces to control network traffic flow."))
@ -25,18 +28,22 @@ s = m:section(TypedSection, "defaults")
s.anonymous = true
s.addremove = false
s:option(Flag, "syn_flood", translate("Enable SYN-flood protection"))
s:tab("general", translate("General Settings"))
s:tab("custom", translate("Custom Rules"))
local di = s:option(Flag, "drop_invalid", translate("Drop invalid packets"))
s:taboption("general", Flag, "syn_flood", translate("Enable SYN-flood protection"))
local di = s:taboption("general", Flag, "drop_invalid", translate("Drop invalid packets"))
di.rmempty = false
function di.cfgvalue(...)
return AbstractValue.cfgvalue(...) or "1"
end
p = {}
p[1] = s:option(ListValue, "input", translate("Input"))
p[2] = s:option(ListValue, "output", translate("Output"))
p[3] = s:option(ListValue, "forward", translate("Forward"))
p[1] = s:taboption("general", ListValue, "input", translate("Input"))
p[2] = s:taboption("general", ListValue, "output", translate("Output"))
p[3] = s:taboption("general", ListValue, "forward", translate("Forward"))
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
@ -44,14 +51,41 @@ for i, v in ipairs(p) do
v:value("ACCEPT", translate("accept"))
end
custom = s:taboption("custom", Value, "_custom",
translate("Custom Rules (/etc/firewall.user)"))
custom.template = "cbi/tvalue"
custom.rows = 20
function custom.cfgvalue(self, section)
return nixio.fs.readfile("/etc/firewall.user")
end
function custom.write(self, section, value)
nixio.fs.writefile("/etc/firewall.user", value)
end
s = m:section(TypedSection, "zone", translate("Zones"))
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s.extedit = ds.build_url("admin", "network", "firewall", "zones", "%s")
name = s:option(Value, "name", translate("Name"))
name.size = 8
function s.create(self)
local z = fw:new_zone()
if z then
luci.http.redirect(
ds.build_url("admin", "network", "firewall", "zones", z.sid)
)
end
end
info = s:option(DummyValue, "_info", translate("Zone ⇒ Forwardings"))
info.template = "cbi/firewall_zoneforwards"
function info.cfgvalue(self, section)
return self.map:get(section, "name")
end
p = {}
p[1] = s:option(ListValue, "input", translate("Input"))
@ -67,15 +101,166 @@ end
s:option(Flag, "masq", translate("Masquerading"))
s:option(Flag, "mtu_fix", translate("MSS clamping"))
net = s:option(MultiValue, "network", translate("Network"))
net.template = "cbi/network_netlist"
net.widget = "checkbox"
net.rmempty = true
luci.tools.webadmin.cbi_add_networks(net)
function net.cfgvalue(self, section)
local value = MultiValue.cfgvalue(self, section)
return value or name:cfgvalue(section)
local created = nil
--
-- Redirects
--
s = m:section(TypedSection, "redirect", translate("Redirections"))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.extedit = ds.build_url("admin", "network", "firewall", "redirect", "%s")
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "redirect", created
))
end
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
function proto.cfgvalue(self, s)
local p = self.map:get(s, "proto")
if not p or p == "tcpudp" then
return "TCP+UDP"
else
return p:upper()
end
end
src = s:option(DummyValue, "src", translate("Source"))
function src.cfgvalue(self, s)
local rv = "%s:%s:%s" % {
self.map:get(s, "src") or "*",
self.map:get(s, "src_ip") or "0.0.0.0/0",
self.map:get(s, "src_port") or "*"
}
local mac = self.map:get(s, "src_mac")
if mac then
rv = rv .. ", MAC " .. mac
end
return rv
end
via = s:option(DummyValue, "via", translate("Via"))
function via.cfgvalue(self, s)
return "%s:%s:%s" % {
translate("Device"),
self.map:get(s, "src_dip") or "0.0.0.0/0",
self.map:get(s, "src_dport") or "*"
}
end
dest = s:option(DummyValue, "dest", translate("Destination"))
function dest.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "dest") or "*",
self.map:get(s, "dest_ip") or "0.0.0.0/0",
self.map:get(s, "dest_port") or "*"
}
end
target = s:option(DummyValue, "target", translate("Action"))
function target.cfgvalue(self, s)
return self.map:get(s, "target") or "DNAT"
end
--
-- Rules
--
s = m:section(TypedSection, "rule", translate("Rules"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
s.extedit = ds.build_url("admin", "network", "firewall", "rule", "%s")
s.defaults.target = "ACCEPT"
function s.create(self, section)
local created = TypedSection.create(self, section)
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "rule", created
))
return
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
if has_v2 then
family = s:option(DummyValue, "family", translate("Family"))
function family.cfgvalue(self, s)
local f = self.map:get(s, "family")
if f and f:match("4") then
return translate("IPv4 only")
elseif f and f:match("6") then
return translate("IPv6 only")
else
return translate("IPv4 and IPv6")
end
end
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
function proto.cfgvalue(self, s)
local p = self.map:get(s, "proto")
local t = self.map:get(s, "icmp_type")
if p == "icmp" and t then
return "ICMP (%s)" % t
elseif p == "tcpudp" or not p then
return "TCP+UDP"
else
return p:upper()
end
end
src = s:option(DummyValue, "src", translate("Source"))
function src.cfgvalue(self, s)
local rv = "%s:%s:%s" % {
self.map:get(s, "src") or "*",
self.map:get(s, "src_ip") or "0.0.0.0/0",
self.map:get(s, "src_port") or "*"
}
local mac = self.map:get(s, "src_mac")
if mac then
rv = rv .. ", MAC " .. mac
end
return rv
end
dest = s:option(DummyValue, "dest", translate("Destination"))
function dest.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "dest") or translate("Device"),
self.map:get(s, "dest_ip") or "0.0.0.0/0",
self.map:get(s, "dest_port") or "*"
}
end
s:option(DummyValue, "target", translate("Action"))
return m