From 7e68058a731feeb17dd6a609acb26bb68ba06ff8 Mon Sep 17 00:00:00 2001 From: Ted Trask Date: Tue, 6 Nov 2012 23:02:06 +0000 Subject: First cut at awall ACF --- Makefile | 44 +++++++ README | 1 + awall-controller.lua | 39 +++++++ awall-editpolicy-html.lsp | 1 + awall-listpolicies-html.lsp | 51 ++++++++ awall-model.lua | 275 ++++++++++++++++++++++++++++++++++++++++++++ awall-status-html.lsp | 1 + awall-viewpolicy-html.lsp | 1 + awall.menu | 4 + awall.roles | 3 + config.mk | 10 ++ 11 files changed, 430 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 awall-controller.lua create mode 120000 awall-editpolicy-html.lsp create mode 100644 awall-listpolicies-html.lsp create mode 100644 awall-model.lua create mode 120000 awall-status-html.lsp create mode 120000 awall-viewpolicy-html.lsp create mode 100644 awall.menu create mode 100644 awall.roles create mode 100644 config.mk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aee4a0a --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +APP_NAME=awall +PACKAGE=acf-$(APP_NAME) +VERSION=0.0.1 + +APP_DIST=\ + awall* \ + + +EXTRA_DIST=README Makefile config.mk + +DISTFILES=$(APP_DIST) $(EXTRA_DIST) + +TAR=tar + +P=$(PACKAGE)-$(VERSION) +tarball=$(P).tar.bz2 +install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME) + +all: +clean: + rm -rf $(tarball) $(P) + +dist: $(tarball) + +install: + mkdir -p "$(install_dir)" + cp -a $(APP_DIST) "$(install_dir)" + +$(tarball): $(DISTFILES) + rm -rf $(P) + mkdir -p $(P) + cp -a $(DISTFILES) $(P) + $(TAR) -jcf $@ $(P) + rm -rf $(P) + +# target that creates a tar package, unpacks is and install from package +dist-install: $(tarball) + $(TAR) -jxf $(tarball) + $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) + rm -rf $(P) + +include config.mk + +.PHONY: all clean dist install dist-install diff --git a/README b/README new file mode 100644 index 0000000..fda56b1 --- /dev/null +++ b/README @@ -0,0 +1 @@ +ACF for ppp diff --git a/awall-controller.lua b/awall-controller.lua new file mode 100644 index 0000000..9fa56a1 --- /dev/null +++ b/awall-controller.lua @@ -0,0 +1,39 @@ +module(..., package.seeall) + +default_action = "status" + +function status(self) + return self.model.getstatus() +end + +function startstop(self) + return self.handle_form(self, self.model.get_startstop, self.model.startstop_service, self.clientdata) +end + +function listpolicies(self) + return self.model.list_policies() +end + +function viewpolicy(self) + return self.model.read_policyfile(self, clientdata) +end + +function createpolicy(self) + return self.handle_form(self, self.model.get_newpolicy, self.model.create_policy, self.clientdata, "Create", "Create New Policy File", "Policy File Created") +end + +function deletepolicy(self) + return self.handle_form(self, self.model.get_delete_policy, self.model.delete_policy, self.clientdata, "Delete", "Delete Policy File", "Policy File Deleted") +end + +function editpolicy(self) + return self.handle_form(self, self.model.get_policyfile, self.model.update_policyfile, self.clientdata, "Save", "Edit Policy", "Policy File Saved") +end + +function enablepolicy(self) + return self.handle_form(self, self.model.get_enablepolicy, self.model.enable_policy, self.clientdata, "Enable", "Enable Policy", "Policy Enabled") +end + +function disablepolicy(self) + return self.handle_form(self, self.model.get_enablepolicy, self.model.disable_policy, self.clientdata, "Disable", "Disable Policy", "Policy Disabled") +end diff --git a/awall-editpolicy-html.lsp b/awall-editpolicy-html.lsp new file mode 120000 index 0000000..15b1930 --- /dev/null +++ b/awall-editpolicy-html.lsp @@ -0,0 +1 @@ +../filedetails-html.lsp \ No newline at end of file diff --git a/awall-listpolicies-html.lsp b/awall-listpolicies-html.lsp new file mode 100644 index 0000000..1d807e9 --- /dev/null +++ b/awall-listpolicies-html.lsp @@ -0,0 +1,51 @@ +<% local form, viewlibrary, page_info, session = ... +require("htmlviewfunctions") +html = require("acf.html") +%> + + + + + +<% htmlviewfunctions.displaycommandresults({"deletepolicy", "editpolicy", "viewpolicy", "enablepolicy", "disablepolicy", "startstop"}, session) %> +<% htmlviewfunctions.displaycommandresults({"createpolicy"}, session, true) %> + +

Policies

+
+<% if form.errtxt then %>

<%= string.gsub(html.html_escape(form.errtxt), "\n", "
") %>

<% end %> + + + + + + + +<% for i,pol in ipairs(form.value) do %> + + + + + + +<% end %> +
ActionStatusNameDescription
+ <% + if viewlibrary.check_permission("deletepolicy") and pol.editable and pol.status ~= "enabled" and pol.status ~= "required" then io.write(html.link{value = "deletepolicy?submit=true&filename=" .. pol.filename, label="Delete ", class="deletepolicy" }) end + if viewlibrary.check_permission("editpolicy") and pol.editable then io.write(html.link{value = "editpolicy?filename=" .. pol.filename.."&redir="..page_info.orig_action, label="Edit " }) end + if viewlibrary.check_permission("viewpolicy") and not pol.editable and pol.filename then io.write(html.link{value = "viewpolicy?filename=" .. pol.filename.."&redir="..page_info.orig_action, label="View " }) end + if viewlibrary.check_permission("enablepolicy") and (pol.status == "disabled" or pol.status == "required") then io.write(html.link{value = "enablepolicy?submit=true&name=" .. pol.name, label="Enable " }) end + if viewlibrary.check_permission("disablepolicy") and pol.status == "enabled" then io.write(html.link{value = "disablepolicy?submit=true&name=" .. pol.name, label="Disable " }) end + %> + <%= html.html_escape(string.gsub(pol.status or "", "^%l", string.upper)) %><%= html.html_escape(pol.name) %><%= html.html_escape(pol.description) %>
+ +<% if viewlibrary and viewlibrary.dispatch_component and viewlibrary.check_permission("createpolicy") then + viewlibrary.dispatch_component("createpolicy") +end %> + +<% if viewlibrary and viewlibrary.dispatch_component and viewlibrary.check_permission("startstop") then + viewlibrary.dispatch_component("startstop") +end %> diff --git a/awall-model.lua b/awall-model.lua new file mode 100644 index 0000000..7e79813 --- /dev/null +++ b/awall-model.lua @@ -0,0 +1,275 @@ +module(..., package.seeall) + +-- Load libraries +require("posix") +require("json") +require("modelfunctions") +processinfo = require("acf.processinfo") +fs = require("acf.fs") +--validator = require("acf.validator") + +-- Set variables +local packagename = "awall" + +local etcpath = "/etc/awall/" +local etcoptionalpath = "/etc/awall/optional/" +local usrpath = "/usr/share/awall/mandatory/" +local usroptionalpath = "/usr/share/awall/optional/" + +local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " + +-- ################################################################################ +-- LOCAL FUNCTIONS + +local findfiles = function(where) + local files = {} + for d in posix.files(where) do + if string.find(d, "%.json$") then + if fs.is_file(where .. d) and string.find(d, "%.json$") then + files[#files+1] = where..d + elseif fs.is_link(where .. d) then + -- awall will use links, but we want to drop the links from /etc/awall/ to one of the optional dirs + -- because those are the ones handled by enable/disable + local link = posix.dirname(posix.readlink(where..d) or "").."/" +logevent("link = "..link) + if where == etcpath and (string.find(link, "^"..etcoptionalpath.."$") or string.find(link, "^"..usroptionalpath.."$")) then + -- do nothing + else + files[#files+1] = where..d + end + + end + end + end + return files +end + +local validatefiledetails = function(filedetails) + local success = true + local res, err = pcall(function() + local jsonoutput, count = json.decode(filedetails.value.filecontent.value) + end) + if not res and err then + success = false + end + + if not success then + filedetails.value.filecontent.errtxt = "Invalid json code\n"..(err or "") + end + return success, filedetails +end + +local validateeditablefile = function(filename) + -- get/setfiledetails only work for files (not links), this prevents editing/deleting the enable links + local dir = posix.dirname(filename or "").."/" + if fs.is_file(filename) and string.find(filename, "%.json$") and (dir==etcpath or dir==etcoptionalpath) then + return true + else + return false + end +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS + +function getstatus() + local status = {} + + local value, errtxt = processinfo.package_version(packagename) + status.version = cfe({ + label="Program version", + value=value, + errtxt=errtxt, + name=packagename + }) + + return cfe({ type="group", value=status, label="AWall Status" }) +end + +function get_startstop(self, clientdata) + local actions = {"Verify", "Translate", "Activate"} + return cfe({ type="group", label="Management", value={}, option=actions }) +end + +function startstop_service(self, startstop, action) + if not action then + startstop.errtxt = "Invalid Action" + else + local cmd + if action == "Verify" then + cmd = path.."awall translate -V 2>&1" + elseif action == "Translate" then + cmd = path.."awall translate 2>&1" + else + cmd = path.."awall activate -f 2>&1" + end + local f = io.popen(cmd) + startstop.descr = f:read("*a") + f:close() + end + return startstop +end + +function list_policies() + -- core-router disabled BSN core router + local policies = {} + local reversepolicies = {} + local errtxt + local cmd = path.."awall list 2>&1" + local f = io.popen(cmd) + for l in f:lines() do + local a,b,c = string.match(l, "(%S+)%s+(%S+)%s*(.*)") + if a and a == "/usr/bin/lua:" then + errtxt = b.." "..c + break + end + if a then + policies[#policies+1] = {name=a, status=b, description=c} + reversepolicies[a] = #policies + end + end + f:close() + -- Since awall seems to crash (and not give results) when there is any error + -- Let's show the actual files too + local addfiles = function(files, editable, mandatory) + for i,d in ipairs(files) do + local name = string.match(d, "([^/]*)%.json$") + if reversepolicies[name] then + policies[reversepolicies[name]].filename = d + policies[reversepolicies[name]].editable = editable + if mandatory then policies[reversepolicies[name]].status = "mandatory" end + else + policies[#policies+1] = {name=name, filename=d, editable=editable} + if mandatory then policies[#policies].status = "mandatory" end + reversepolicies[name] = #policies + end + end + end + + local files = findfiles(usroptionalpath) + addfiles(files, false, false) + files = findfiles(etcoptionalpath) + addfiles(files, true, false) + files = findfiles(usrpath) + addfiles(files, false, true) + files = findfiles(etcpath) + addfiles(files, true, true) + + table.sort(policies, function(a,b) return a.name < b.name end) + + return cfe({ type="list", value=policies, label="Policies", errtxt=errtxt }) +end + +function get_newpolicy() + local newpolicy = {} + newpolicy.name = cfe({ label="Name", seq=1 }) + newpolicy.optional = cfe({ type="boolean", label="Optional", seq=2 }) + return cfe({ type="group", value=newpolicy, label="New Policy" }) +end + +function create_policy(self, newpolicy) + local success = true + local name = newpolicy.value.name.value + + if name == "" then + newpolicy.value.name.errtxt = "Invalid name" + success = false + else + local policies = list_policies() + for i,pol in ipairs(policies.value) do + if pol.name == newpolicy.value.name.value then + newpolicy.value.name.errtxt = "Name already exists" + success = false + break + end + end + end + + if success then + local path + if newpolicy.value.optional.value then + path = etcoptionalpath + else + path = etcpath + end + path = path..name..".json" + if posix.stat(path) then + newpolicy.value.name.errtxt = path.." already exists" + success = false + else + fs.write_file(path, "{}") + end + end + + if not success then + newpolicy.errtxt = "Failed to create policy file" + end + + return newpolicy +end + +function get_delete_policy(self, clientdata) + retval = {} + retval.filename = cfe({ value=clientdata.filename or "", label="File Name" }) + return cfe({ type="group", value=retval, label="Delete Policy File" }) +end + +function delete_policy(self, delpolicy) + if validateeditablefile(delpolicy.value.filename.value) then + os.remove(delpolicy.value.filename.value) + else + delpolicy.errtxt = "Failed to delete policy" + delpolicy.value.filename.errtxt="Policy not found" + end + + return delpolicy +end + +function read_policyfile(self, clientdata) + -- Can read from all 4 locations + return modelfunctions.getfiledetails(clientdata.filename, function(filename) + local dir = posix.dirname(filename or "").."/" + if string.find(filename, "%.json$") and (dir==etcpath or dir==etcoptionalpath or dir==usrpath or dir==usroptionalpath) then + return true + else + return false + end + end) +end + +function get_policyfile(self, clientdata) + -- Can only get (for editing) from /etc/ locations + return modelfunctions.getfiledetails(clientdata.filename, validateeditablefile) +end + +function update_policyfile(self, filedetails) + return modelfunctions.setfiledetails(self, filedetails, validateeditablefile, validatefiledetails) +end + +function get_enablepolicy(self, clientdata) + local policy = {} + policy.name = cfe({ value=clientdata.name or "", label="Name", seq=1 }) + return cfe({ type="group", value=policy, label="Policy" }) +end + +function enable_policy(self, enable) + local cmd = path.."awall enable "..format.escapespecialcharacters(enable.value.name.value).." 2>&1" + local f = io.popen(cmd) + local result = f:read("*a") + f:close() + if result ~= "" then + enable.errtxt = result + end + return enable +end + +function disable_policy(self, disable) + local cmd = path.."awall disable "..format.escapespecialcharacters(disable.value.name.value).." 2>&1" + local f = io.popen(cmd) + local result = f:read("*a") + f:close() + if result ~= "" then + disable.errtxt = result + end + return disable +end diff --git a/awall-status-html.lsp b/awall-status-html.lsp new file mode 120000 index 0000000..b2f8480 --- /dev/null +++ b/awall-status-html.lsp @@ -0,0 +1 @@ +../status-html.lsp \ No newline at end of file diff --git a/awall-viewpolicy-html.lsp b/awall-viewpolicy-html.lsp new file mode 120000 index 0000000..15b1930 --- /dev/null +++ b/awall-viewpolicy-html.lsp @@ -0,0 +1 @@ +../filedetails-html.lsp \ No newline at end of file diff --git a/awall.menu b/awall.menu new file mode 100644 index 0000000..dc4ccb3 --- /dev/null +++ b/awall.menu @@ -0,0 +1,4 @@ +#CAT GROUP/DESC TAB ACTION +Networking 25Alpine_Wall Status status +Networking 25Alpine_Wall Policies listpolicies +Networking 25Alpine_Wall Expert expert diff --git a/awall.roles b/awall.roles new file mode 100644 index 0000000..c6ee6a0 --- /dev/null +++ b/awall.roles @@ -0,0 +1,3 @@ +USER=awall:status,awall:startstop +EXPERT=awall:listpolicies,awall:createpolicy,awall:deletepolicy,awall:editpolicy,awall:viewpolicy,awall:enablepolicy,awall:disablepolicy +ADMIN=awall:status,awall:startstop,awall:listpolicies,awall:createpolicy,awall:deletepolicy,awall:editpolicy,awall:viewpolicy,awall:enablepolicy,awall:disablepolicy diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..45f4d21 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ +prefix=/usr +datadir=${prefix}/share +sysconfdir=${prefix}/etc +localstatedir=${prefix}/var +acfdir=${datadir}/acf +wwwdir=${acfdir}/www +cgibindir=${acfdir}/cgi-bin +appdir=${acfdir}/app +acflibdir=${acfdir}/lib +sessionsdir=${localstatedir}/lib/acf/sessions -- cgit v1.2.3