init.lua 4.34 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
local modules = {package.loaded['awall.model']}
21 22

function loadmodules(path)
23 24 25
   local cdir = lfs.currentdir()
   if path then lfs.chdir(path) end

26 27 28 29 30 31
   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)
	 table.insert(modules, package.loaded[name])
      end
32
   end
33 34

   lfs.chdir(cdir)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
35 36 37
end


38
Config = awall.object.class(awall.object.Object)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
39

40
function Config:init(confdirs, importdirs)
41 42

   self.input = {}
43
   self.iptables = awall.iptables.IPTables.new()
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
   local required = {}
   local imported = {}

   function import(name, fname)
      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
82 83 84 85
	    end
	 end
      end
   end
86 87 88 89 90 91 92 93 94 95 96 97
   
   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
98

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
99 100 101 102 103 104 105 106 107 108

   function expandvars(obj)
      for k, v in pairs(obj) do
	 if type(v) == 'table' then
	    expandvars(v)

	 else
	    local visited = {}
	    local val = v

109 110 111 112
	    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
113 114 115 116 117
		  
	       if util.contains(visited, name) then
		  error('Circular variable definition: '..name)
	       end
	       table.insert(visited, name)
118 119 120 121 122 123 124 125 126 127

	       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
128 129 130 131 132 133 134
	    end

	    obj[k] = val
	 end
      end
   end

135
   expandvars(self.input)
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
136 137


Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
138
   function insertrule(trule)
139
      local t = self.iptables.config[trule.family][trule.table][trule.chain]
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
140 141 142 143 144 145 146 147 148 149 150
      if trule.position == 'prepend' then
	 table.insert(t, 1, trule.opts)
      else
	 table.insert(t, trule.opts)
      end
   end

   local locations = {}

   for i, mod in ipairs(modules) do
      for path, cls in pairs(mod.classmap) do
151 152 153 154
	 if self.input[path] then	    
	    awall.util.map(self.input[path],
			   function(obj) return cls.morph(obj, self) end)
	    table.insert(locations, self.input[path])
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
155 156 157 158 159 160 161 162 163 164 165 166
	 end
      end

      for i, rule in ipairs(mod.defrules) do insertrule(rule) end
   end


   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
167

168
   self.ipset = awall.ipset.IPSet.new(self.input.ipset)
169
end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
170

171 172 173
function Config:dump(iptdir, ipsfile)
   self.ipset:dump(ipsfile or '/etc/ipset.d/awall')
   self.iptables:dump(iptdir or '/etc/iptables')
174
end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
175

176 177 178
function Config:test()
   self.ipset:create()
   self.iptables:test()
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
179
end
180 181 182 183 184

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