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


module(..., package.seeall)

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
10 11
require 'awall'
require 'awall.host'
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
12
require 'awall.model'
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
13 14 15
require 'awall.optfrag'
require 'awall.util'

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
16 17 18 19
local model = awall.model

local Filter = model.class(model.Rule)

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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
function Filter:defaultzones()
   return self.dnat and {nil} or model.Rule.defaultzones(self)
end

function Filter:destoptfrags()
   local ofrags = model.Rule.destoptfrags(self)
   if not self.dnat then return ofrags end

   ofrags = awall.optfrag.combinations(ofrags, {{family='inet6'}}) or {}
   local natof = model.Zone.morph({addr=self.dnat}):optfrags('out')
   assert(#natof == 1)
   table.insert(ofrags, natof[1])
   return ofrags
end

function Filter:trules()
   local res = {}

   if self.dnat then
      if not self.dest then
	 error('Destination address must be specified with DNAT')
      end
      if string.find(self.dnat, '/') then
	 error('DNAT target cannot be a network address')
      end
      for i, attr in ipairs({'ipsec', 'ipset'}) do
	 if self[attr] then
	    error('dnat and '..attr..' options cannot be used simultaneously')
	 end
      end

      local dnataddr
      for i, addr in ipairs(awall.host.resolve(self.dnat)) do
	 if addr[1] == 'inet' then
	    if dnataddr then
	       error(self.dnat..' resolves to multiple IPv4 addresses')
	    end
	    dnataddr = addr[2]
	 end
      end
      if not dnataddr then
	 error(self.dnat..' does not resolve to any IPv4 address')
      end

      local dnat = {['ip-range']=dnataddr}
      for i, attr in ipairs({'in', 'src', 'dest', 'service'}) do
	 dnat[attr] = self[attr]
      end

      if not awall.classmap.dnat then error('NAT module not installed') end

      awall.util.extend(res, awall.classmap.dnat.morph(dnat,
						       self.context):trules())
   end

   awall.util.extend(res, model.Rule.trules(self))

   return res
end

Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
function Filter:limit()
   local res
   for i, limit in ipairs({'conn-limit', 'flow-limit'}) do
      if self[limit] then
	 if res then
	    error('Cannot specify multiple limits for a single filter rule')
	 end
	 res = limit
      end
   end
   return res
end

function Filter:position()
   return self:limit() == 'flow-limit' and 'prepend' or 'append'
end

function Filter:target()
   if not self:limit() then return model.Rule.target(self) end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
99
   if not self['limit-target'] then self['limit-target'] = self:newchain('limit') end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
   return self['limit-target']
end

function Filter:extraoptfrags()
   local res = {}
   local limit = self:limit()
   if limit then
      if self.action ~= 'accept' then
	 error('Cannot specify limit for '..self.action..' filter')
      end
      local optbase = '-m recent --name '..self:target()
      table.insert(res, {chain=self:target(),
			 opts=optbase..' --update --hitcount '..self[limit].count..' --seconds '..self[limit].interval..' -j LOGDROP'})
      table.insert(res, {chain=self:target(),
			 opts=optbase..' --set -j ACCEPT'})
   end
   return res
end



local Policy = model.class(Filter)

function Policy:servoptfrags() return nil end


126 127
classes = {{'filter', Filter},
	   {'policy', Policy}}
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
128 129

defrules = {}
130
for i, family in ipairs({'inet', 'inet6'}) do
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
131 132 133 134 135 136 137 138 139
   for i, target in ipairs({'DROP', 'REJECT'}) do
      for i, opts in ipairs({'-m limit --limit 1/second -j LOG', '-j '..target}) do
	 table.insert(defrules,
		      {family=family,
		       table='filter',
		       chain='LOG'..target,
		       opts=opts})
      end
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
140

141
   for i, chain in ipairs({'FORWARD', 'INPUT', 'OUTPUT'}) do
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
142 143 144 145 146 147
      table.insert(defrules,
		   {family=family,
		    table='filter',
		    chain=chain,
		    opts='-m state --state RELATED,ESTABLISHED -j ACCEPT'})
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
148 149 150 151 152 153 154 155

   for i, chain in ipairs({'INPUT', 'OUTPUT'}) do
      table.insert(defrules,
		   {family=family,
		    table='filter',
		    chain=chain,
		    opts='-'..string.lower(string.sub(chain, 1, 1))..' lo -j ACCEPT'})
   end
Kaarle Ritvanen's avatar
Kaarle Ritvanen committed
156
end