Commit 2f489cc6 authored by Kaarle Ritvanen's avatar Kaarle Ritvanen

secure use of connection tracking helpers

enable connection tracking helpers when required, fixes #1540
service-specific RELATED rules
parent 24baf0b3
...@@ -13,10 +13,29 @@ local combinations = require('awall.optfrag').combinations ...@@ -13,10 +13,29 @@ local combinations = require('awall.optfrag').combinations
local util = require('awall.util') local util = require('awall.util')
local extend = util.extend local extend = util.extend
local listpairs = util.listpairs
local RECENT_MAX_COUNT = 20 local RECENT_MAX_COUNT = 20
local RelatedRule = model.class(model.Rule)
function RelatedRule:servoptfrags()
local helpers = {}
for i, serv in listpairs(self.service) do
for i, sdef in listpairs(serv) do
local helper = sdef['ct-helper']
if helper then
helpers[helper] = {
opts='-m conntrack --ctstate RELATED --helper '..helper
}
end
end
end
return util.values(helpers)
end
local Filter = model.class(model.Rule) local Filter = model.class(model.Rule)
function Filter:init(...) function Filter:init(...)
...@@ -54,11 +73,13 @@ end ...@@ -54,11 +73,13 @@ end
function Filter:trules() function Filter:trules()
local res = {} local res = {}
local function extrarules(cls, extra) local function extrarules(cls, extra, src)
if not src then src = self end
local params = {} local params = {}
for i, attr in ipairs({'in', 'out', 'src', 'dest', for i, attr in ipairs(
'ipset', 'ipsec', 'service'}) do {'in', 'out', 'src', 'dest', 'ipset', 'ipsec', 'service'}
params[attr] = self[attr] ) do
params[attr] = src[attr]
end end
util.update(params, extra) util.update(params, extra)
return extend(res, self:create(cls, params):trules()) return extend(res, self:create(cls, params):trules())
...@@ -105,9 +126,31 @@ function Filter:trules() ...@@ -105,9 +126,31 @@ function Filter:trules()
extend(res, model.Rule.trules(self)) extend(res, model.Rule.trules(self))
if self['no-track'] and self.action == 'accept' then if self.action == 'accept' then
extrarules('no-track', {reverse=true}) local nr = #res
extrarules('filter', {reverse=true, action='accept', log=false})
if self.related then
for i, rule in listpairs(self.related) do
extrarules(
RelatedRule,
{service=self.service, action='accept'},
rule
)
end
else
-- TODO avoid creating unnecessary RELATED rules by introducing
-- helper direction attributes to service definitions
extrarules(RelatedRule, {action='accept'})
extrarules(RelatedRule, {reverse=true, action='accept'})
end
if self['no-track'] then
if #res > nr then
self:error('Tracking required by service')
end
extrarules('no-track', {reverse=true})
extrarules('filter', {reverse=true, action='accept', log=false})
end
end end
return res return res
...@@ -205,18 +248,57 @@ function Policy:servoptfrags() return nil end ...@@ -205,18 +248,57 @@ function Policy:servoptfrags() return nil end
local fchains = {{chain='FORWARD'}, {chain='INPUT'}, {chain='OUTPUT'}} local fchains = {{chain='FORWARD'}, {chain='INPUT'}, {chain='OUTPUT'}}
local dar = combinations(fchains, function stateful(config)
{{opts='-m conntrack --ctstate RELATED,ESTABLISHED'}}) local res = {}
for i, chain in ipairs({'INPUT', 'OUTPUT'}) do
table.insert(dar, local families = {{family='inet'}, {family='inet6'}}
{chain=chain,
opts='-'..string.lower(string.sub(chain, 1, 1))..' lo'}) local er = combinations(
fchains,
{{opts='-m conntrack --ctstate ESTABLISHED'}}
)
for i, chain in ipairs({'INPUT', 'OUTPUT'}) do
table.insert(
er,
{
chain=chain,
opts='-'..string.lower(string.sub(chain, 1, 1))..' lo'
}
)
end
extend(res, combinations(families, er, {{table='filter', target='ACCEPT'}}))
-- TODO avoid creating unnecessary CT rules by inspecting the
-- filter rules' target families and chains
local visited = {}
local ofrags = {}
for i, rule in listpairs(config.filter) do
for i, serv in listpairs(rule.service) do
if not visited[serv] then
for i, sdef in listpairs(serv) do
if sdef['ct-helper'] then
local of = model.Rule.morph({service={sdef}}):servoptfrags()
assert(#of == 1)
of[1].target = 'CT --helper '..sdef['ct-helper']
table.insert(ofrags, of[1])
end
end
visited[serv] = true
end
end
end
extend(
res,
combinations(
families,
{{table='raw'}},
{{chain='PREROUTING'}, {chain='OUTPUT'}},
ofrags
)
)
return res
end end
dar = combinations(
dar,
{{table='filter', target='ACCEPT'}},
{{family='inet'}, {family='inet6'}}
)
local icmp = {{family='inet', table='filter', opts='-p icmp'}} local icmp = {{family='inet', table='filter', opts='-p icmp'}}
local icmp6 = {{family='inet6', table='filter', opts='-p icmpv6'}} local icmp6 = {{family='inet6', table='filter', opts='-p icmpv6'}}
...@@ -245,7 +327,7 @@ icmprules(icmp6, 'icmpv6-type', {1, 2, 3, 4}) ...@@ -245,7 +327,7 @@ icmprules(icmp6, 'icmpv6-type', {1, 2, 3, 4})
export = { export = {
filter={class=Filter, before={'dnat', 'no-track'}}, filter={class=Filter, before={'dnat', 'no-track'}},
policy={class=Policy, after='%filter-after'}, policy={class=Policy, after='%filter-after'},
['%filter-before']={rules=dar, before='filter'}, ['%filter-before']={rules=stateful, before='filter'},
['%filter-after']={rules=ir, after='filter'} ['%filter-after']={rules=ir, after='filter'}
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
{ "proto": "tcp", "port": 135 }, { "proto": "tcp", "port": 135 },
{ "proto": "udp", "port": 135 } { "proto": "udp", "port": 135 }
], ],
"ftp": { "proto": "tcp", "port": 21 }, "ftp": { "proto": "tcp", "port": 21, "ct-helper": "ftp" },
"gre": { "proto": "gre" }, "gre": { "proto": "gre" },
"hp-pdl": { "proto": "tcp", "port": 9100 }, "hp-pdl": { "proto": "tcp", "port": 9100 },
"http": { "proto": "tcp", "port": 80 }, "http": { "proto": "tcp", "port": 80 },
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
{ "proto": "esp" }, { "proto": "esp" },
{ "proto": "udp", "port": [ 500, 4500 ] } { "proto": "udp", "port": [ 500, 4500 ] }
], ],
"irc": { "proto": "tcp", "port": 6667 }, "irc": { "proto": "tcp", "port": 6667, "ct-helper": "irc" },
"kerberos": [ "kerberos": [
{ "proto": "tcp", "port": 88 }, { "proto": "tcp", "port": 88 },
{ "proto": "udp", "port": 88 } { "proto": "udp", "port": 88 }
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
], ],
"netbios-ns": [ "netbios-ns": [
{ "proto": "tcp", "port": 137 }, { "proto": "tcp", "port": 137 },
{ "proto": "udp", "port": 137 } { "proto": "udp", "port": 137, "ct-helper": "netbios_ns" }
], ],
"netbios-ssn": [ "netbios-ssn": [
{ "proto": "tcp", "port": 139 }, { "proto": "tcp", "port": 139 },
...@@ -90,8 +90,8 @@ ...@@ -90,8 +90,8 @@
], ],
"rdp": { "proto": "tcp", "port": 3389 }, "rdp": { "proto": "tcp", "port": 3389 },
"sip": [ "sip": [
{ "proto": "udp", "port": 5060 }, { "proto": "udp", "port": 5060, "ct-helper": "sip" },
{ "proto": "tcp", "port": 5060 } { "proto": "tcp", "port": 5060, "ct-helper": "sip" }
], ],
"sip-tls": [ "sip-tls": [
{ "proto": "udp", "port": 5061 }, { "proto": "udp", "port": 5061 },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment