diff options
Diffstat (limited to 'iptables-model.lua')
-rw-r--r-- | iptables-model.lua | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/iptables-model.lua b/iptables-model.lua new file mode 100644 index 0000000..3f55965 --- /dev/null +++ b/iptables-model.lua @@ -0,0 +1,268 @@ +module(..., package.seeall) + +-- Load libraries +require("modelfunctions") +require("fs") +require("format") + +-- Set variables +local packagename = "iptables" +local servicename = "iptables" +local rulesfile = "/var/lib/iptables/rules-save" + +local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " +local tables = {"filter", "nat", "mangle"} +local details + +-- ################################################################################ +-- LOCAL FUNCTIONS + +local getdetails = function() + if not details then + details = {} + local cmd = path .. "iptables -t filter -n -L" + for i,tab in ipairs(tables) do + local f = io.popen( (string.gsub(cmd, "filter", tab)) ) + details[tab] = {table=tab} + local file = f:read("*a") + f:close() + for line in string.gmatch(file, "([^\n]+)") do + if string.match(line, "^Chain") then + local name = string.match(line, "^%w+%s+(%S+)") + local policy = string.match(line, "policy (%w+)") + local references = string.match(line, "(%d+) references") + table.insert(details[tab], {name=name, policy=policy, references=references}) + elseif not string.match(line, "^target%s+prot") then + table.insert(details[tab][#details[tab]], line) + elseif not details[tab].header then + details[tab].header = line + end + end + end + end +end + +local find_chain = function(tab, chain) + getdetails() + if details[tab] then + for i,chn in ipairs(details[tab]) do + if chain == chn.name then + return chn + end + end + end + return nil +end + +local save = function() + local cmd = path .. "/etc/init.d/"..servicename.." save 2>&1" + local f = io.popen( cmd ) + f:close() + details = nil +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS + +function getstatus() + local status = {} + + local value, errtxt = processinfo.package_version(packagename) + status.version = cfe({ + label="Program version", + value=value, + errtxt=errtxt, + }) + + return cfe({ type="group", value=status, label="IPtables Status" }) +end + +function getstatusdetails() + getdetails() + local retval = {} + for i,tab in ipairs(tables) do + local chains = 0 + local rules = 0 + for i,chain in ipairs(details[tab]) do + chains = chains + 1 + rules = rules + #chain + end + retval[tab] = {chains=chains, rules=rules} + end + + return cfe({ type="structure", value=retval, label="IPtables Status Details" }) +end + +function getrules(tab) + getdetails() + tab = tab or "filter" + + return cfe({ type="structure", value=details[tab] or {}, label=string.gsub(tab, "^.", string.upper).." Rules" }) +end + +function read_chain(tab, chain) + local retval = {} + retval.table = cfe({ type="select", value=tab or "filter", label="Table", option=tables }) + retval.chain = cfe({ value=chain or "", label="Chain" }) + getdetails() + if tab and not details[tab] then + retval.table.errtxt = "Invalid table" + end + if chain then + local chn = find_chain(retval.table.value, chain) + if not chn then + retval.chain.errtxt = "Cannot find chain" + elseif chn.policy then + -- only built-in chains can have policies, and the target can only be DROP or ACCEPT + retval.policy = cfe({ type="select", value=chn.policy, label="Policy", option={"DROP", "ACCEPT"} }) + end + end + return cfe({ type="group", value=retval, label="Chain" }) +end + +function update_chain(chain) + local success = true + getdetails() + if not details[chain.value.table.value] then + chain.value.table.errtxt = "Invalid table" + success = false + elseif not find_chain(chain.value.table.value, chain.value.chain.value) then + chain.value.chain.errtxt = "Invalid chain" + success = false + end + + if success then + if chain.value.policy then + local cmd = path .. "iptables -t "..chain.value.table.value.." -P "..chain.value.chain.value.." "..chain.value.policy.value.." 2>&1" + local f = io.popen(cmd) + local errtxt = f:read("*a") + f:close() + if errtxt ~= "" then + chain.errtxt = errtxt + end + save() + end + else + chain.errtxt = "Failed to update chain" + end + + return chain +end + +function create_chain(chain) + local success = true + getdetails() + if not details[chain.value.table.value] then + chain.value.table.errtxt = "Invalid table" + success = false + elseif find_chain(chain.value.table.value, chain.value.chain.value) then + chain.value.chain.errtxt = "Chain already exists" + success = false + end + if string.find(chain.value.chain.value, "[%s\'\"]") then + chain.value.chain.errtxt = "Chain cannot contain spaces or quotes" + success = false + end + + if success then + local cmd = path .. "iptables -t "..chain.value.table.value.." -N "..chain.value.chain.value.." 2>&1" + local f = io.popen(cmd) + local errtxt = f:read("*a") + if errtxt ~= "" then + chain.errtxt = errtxt + end + f:close() + save() + else + chain.errtxt = "Failed to create chain" + end + + return chain +end + +function delete_chain(tab, chain) + local retval = cfe({ label="Delete Chain result" }) + tab = tab or "filter" + local chn = find_chain(tab, chain) + if not chn then + retval.errtxt = "Could not find chain" + elseif chn.policy then + retval.errtxt = "Cannot delete built-in chain" + elseif chn.references and tonumber(chn.references) > 0 then + retval.errtxt = "Cannot delete chain with references" + else + local cmd = path .. "iptables -t "..tab.." -X "..chain.." 2>&1" + local f = io.popen(cmd) + local errtxt = f:read("*a") + if errtxt ~= "" then + retval.errtxt = errtxt + else + retval.value = "Deleted Chain" + end + save() + end + + return retval +end + +function read_rule(tab, chain, pos) + local retval = {} + retval.table = cfe({ type="select", value=tab or "filter", label="Table", option=tables }) + retval.chain = cfe({ value=chain or "", label="Chain" }) + retval.position = cfe({ value=pos or "", label="Position" }) + retval.protocol = cfe({ value="all", label="Protocol", descr="One of tcp, udp, icmp, or all, or a numeric value representing a protocol, or a protocol name from /etc/protocols. A '!' before the protocol inverts the test." }) + retval.source = cfe({ label="Source", descr="A network name or IP address (may have mask of type /xxx.xxx.xxx.xxx or /xx). A '!' before the address spcification inverts the sense of the address." }) + retval.destination = cfe({ label="Destination", descr="A network name or IP address (may have mask of type /xxx.xxx.xxx.xxx or /xx). A '!' before the address spcification inverts the sense of the address." }) + retval.jump = cfe({ label="Target", descr="Specify the target of the rule - one of ACCEPT, DROP, QUEUE, or RETURN, or the name of a user-defined chain." }) + retval.goto = cfe({ label="Goto", descr="Processing should continue in the specified chain" }) + retval.in_interface = cfe({ label="In Interface", descr="Name of an interface via which a packet was received. A '!' before the interface inverts the sense. A '+' ending the interface will match any interface that begins with this name." }) + retval.out_interface = cfe({ label="Out Interface", descr="Name of an interface via which a packet is going to be sent. A '!' before the interface inverts the sense. A '+' ending the interface will match any interface that begins with this name." }) + retval.fragment = cfe({ label="Fragment", descr="A '+' specifies the second and further packets of fragmented packets. A '!' specifies only head fragments or unfragmented packets." }) + retval.set_counters = cfe({ label="Set Counters", descr="'Number, number' to initialize the packet and byte counters."}) + + getdetails() + if tab and not details[tab] then + retval.table.errtxt = "Invalid table" + end + local chn + if chain then + chn = find_chain(retval.table.value, chain) + if not chn then + retval.chain.errtxt = "Cannot find chain" + end + end + if pos and chn then + if not chn[tonumber(pos)] then + retval.position.errtxt = "Cannot find rule" + else + -- We found the rule, update the settings + end + end + return cfe({ type="group", value=retval, label="Rule" }) +end + +function readrulesfile() + return modelfunctions.getfiledetails(rulesfile) +end + +function updaterulesfile(filedetails) + return modelfunctions.setfiledetails(filedetails, {rulesfile}) +end + +-- local implementation to add save, reload, and panic actions +function startstop_service(action) + local cmdresult = cfe({ label="Start/Stop result" }) + + if (string.lower(action) == "start") or (string.lower(action) == "stop") or (string.lower(action) == "restart") or (string.lower(action) == "save") or (string.lower(action) == "reload") or (string.lower(action) == "panic") then + local file = io.popen( "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin /etc/init.d/" .. + servicename .. " " .. string.lower(action) .. " 2>&1" ) + if file ~= nil then + cmdresult.value = file:read( "*a" ) + file:close() + end + posix.sleep(2) -- Wait for the process to start|stop + else + cmdresult.errtxt = "Unknown command!" + end + return cmdresult +end |