init.lua 4.64 KB
Newer Older
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
1 2 3 4 5 6 7 8 9
--[[
Alpine Wall main module
Copyright (C) 2012 Kaarle Ritvanen
Licensed under the terms of GPL2
]]--

module(..., package.seeall)

require 'json'
10
require 'lfs'
11
require 'stringy'
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
12

13
require 'awall.ipset'
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
14
require 'awall.iptables'
15
require 'awall.model'
16
require 'awall.object'
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
17 18 19
require 'awall.util'


20 21
local procorder
local defrules
22 23

function loadmodules(path)
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
   classmap = {}
   procorder = {}
   defrules = {}

   local function readmetadata(mod)
      for i, clsdef in ipairs(mod.classes) do
	 local path, cls = unpack(clsdef)
	 classmap[path] = cls
	 table.insert(procorder, path)
      end
      util.extend(defrules, mod.defrules)
   end

   readmetadata(model)

39 40 41
   local cdir = lfs.currentdir()
   if path then lfs.chdir(path) end

42 43 44 45
   for modfile in lfs.dir((path or '/usr/share/lua/5.1')..'/awall/modules') do
      if stringy.endswith(modfile, '.lua') then
	 local name = 'awall.modules.'..string.sub(modfile, 1, -5)
	 require(name)
46
	 readmetadata(package.loaded[name])
47
      end
48
   end
49 50

   lfs.chdir(cdir)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
51 52 53
end


54
Config = object.class(object.Object)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
55

56
function Config:init(confdirs, importdirs)
57 58

   self.input = {}
59
   self.iptables = iptables.IPTables.new()
60

61 62 63
   local required = {}
   local imported = {}

64
   local function import(name, fname)
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
      local file
      if fname then
	 file = io.open(fname)
      else
	 for i, dir in ipairs(importdirs or {'/usr/share/awall/optional'}) do
	    file = io.open(dir..'/'..name..'.json')
	    if file then break end
	 end
      end
      if not file then error('Import failed: '..name) end

      local data = ''
      for line in file:lines() do data = data..line end
      file:close()
      data = json.decode(data)

      table.insert(required, name)
      for i, iname in util.listpairs(data.import) do
	 if not util.contains(imported, iname) then
	    if util.contains(required, iname) then
	       error('Circular import: ' + iname)
	    end
	    import(iname)
	 end
      end
      table.insert(imported, name)
      
      for cls, objs in pairs(data) do
	 if cls ~= 'import' then
	    if not self.input[cls] then self.input[cls] = objs
	    elseif objs[1] then util.extend(self.input[cls], objs)
	    else
	       for k, v in pairs(objs) do self.input[cls][k] = v end
98 99 100 101
	    end
	 end
      end
   end
102 103 104 105 106 107 108 109 110 111 112 113
   
   for i, dir in ipairs(confdirs or
			{'/usr/share/awall/mandatory', '/etc/awall'}) do
      local names = {}
      for fname in lfs.dir(dir) do
	 local si, ei, name = string.find(fname, '^([%w-]+)%.json$')
	 if name then table.insert(names, name) end
      end
      table.sort(names)

      for i, name in ipairs(names) do import(name, dir..'/'..name..'.json') end
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
114

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
115

116
   local function expandvars(obj)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
117 118 119 120 121 122 123 124
      for k, v in pairs(obj) do
	 if type(v) == 'table' then
	    expandvars(v)

	 else
	    local visited = {}
	    local val = v

125 126 127 128
	    local pattern = '%$(%a[%w_]*)'

	    while type(val) == 'string' and string.find(val, pattern) do
	       local si, ei, name = string.find(val, pattern)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
129 130 131 132 133
		  
	       if util.contains(visited, name) then
		  error('Circular variable definition: '..name)
	       end
	       table.insert(visited, name)
134 135 136 137 138 139 140 141 142 143

	       local var = self.input.variable[name]
	       if not var then error('Invalid variable reference: '..name) end

	       if si == 1 and ei == string.len(val) then val = var
	       elseif util.contains({'number', 'string'}, type(var)) then
		  val = string.sub(val, 1, si - 1)..var..string.sub(val, ei + 1, -1)
	       else
		  error('Attempted to concatenate complex variable: '..name)
	       end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
144 145
	    end

146
	    obj[k] = val ~= '' and val or nil
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
147 148 149 150
	 end
      end
   end

151 152 153
   for k, v in pairs(self.input) do
      if k ~= 'variable' then expandvars(v) end
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
154 155


156
   local function insertrule(trule)
157
      local t = self.iptables.config[trule.family][trule.table][trule.chain]
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
158 159 160 161 162 163 164 165 166
      if trule.position == 'prepend' then
	 table.insert(t, 1, trule.opts)
      else
	 table.insert(t, trule.opts)
      end
   end

   local locations = {}

167 168 169 170 171
   for i, path in ipairs(procorder) do
      if self.input[path] then
	 util.map(self.input[path],
		  function(obj) return classmap[path].morph(obj, self) end)
	 table.insert(locations, self.input[path])
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
172 173 174
      end
   end

175
   for i, rule in ipairs(defrules) do insertrule(rule) end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
176 177 178 179 180 181

   for i, location in ipairs(locations) do
      for i, rule in ipairs(location) do
	 for i, trule in ipairs(rule:trules()) do insertrule(trule) end
      end
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
182

183
   self.ipset = ipset.IPSet.new(self.input.ipset)
184
end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
185

186 187 188
function Config:dump(iptdir, ipsfile)
   self.ipset:dump(ipsfile or '/etc/ipset.d/awall')
   self.iptables:dump(iptdir or '/etc/iptables')
189
end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
190

191 192 193
function Config:test()
   self.ipset:create()
   self.iptables:test()
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
194
end
195 196 197 198 199

function Config:activate()
   self:test()
   self.iptables:activate()
end