Skip to content

Commit

Permalink
iptables: allow deny_action=TARPIT (#320)
Browse files Browse the repository at this point in the history
* iptables: allow deny_action=TARPIT
* nftables: validate deny_action (must be DROP or REJECT, default DROP)
* added log.Trace for consistency
  • Loading branch information
mmetc authored Aug 24, 2023
1 parent b8252c0 commit 9b7047a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 12 deletions.
15 changes: 11 additions & 4 deletions pkg/iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

log "github.com/sirupsen/logrus"
"golang.org/x/exp/slices"

"github.com/crowdsecurity/crowdsec/pkg/models"

Expand Down Expand Up @@ -52,13 +53,19 @@ func NewIPTables(config *cfg.BouncerConfig) (types.Backend, error) {
Chains: []string{},
}

var target string
if strings.EqualFold(config.DenyAction, "REJECT") {
target = "REJECT"
} else {
allowedActions := []string{"DROP", "REJECT", "TARPIT"}

target := strings.ToUpper(config.DenyAction)
if target == "" {
target = "DROP"
}

if !slices.Contains(allowedActions, target) {
return nil, fmt.Errorf("invalid deny_action '%s', must be one of %s", config.DenyAction, strings.Join(allowedActions, ", "))
}

log.Tracef("using '%s' as deny_action", target)

ipsetBin, err := exec.LookPath("ipset")
if err != nil {
return nil, fmt.Errorf("unable to find ipset")
Expand Down
29 changes: 21 additions & 8 deletions pkg/nftables/nftables_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,10 @@ func (c *nftContext) initOwnTable(hooks []string, denyLog bool, denyLogPrefix st
})

log.Debugf("nftables: ip%s chain '%s' created", c.version, chain.Name)
r := c.createRule(chain, set, denyLog, denyLogPrefix, denyAction)
r, err := c.createRule(chain, set, denyLog, denyLogPrefix, denyAction)
if err != nil {
return err
}
c.conn.AddRule(r)
}

Expand Down Expand Up @@ -251,7 +254,7 @@ func (c *nftContext) lookupTable() (*nftables.Table, error) {

func (c *nftContext) createRule(chain *nftables.Chain, set *nftables.Set,
denyLog bool, denyLogPrefix string, denyAction string,
) *nftables.Rule {
) (*nftables.Rule, error) {
r := &nftables.Rule{
Table: c.table,
Chain: chain,
Expand Down Expand Up @@ -280,18 +283,28 @@ func (c *nftContext) createRule(chain *nftables.Chain, set *nftables.Set,
})
}

if strings.EqualFold(denyAction, "REJECT") {
action := strings.ToUpper(denyAction)
if action == "" {
action = "DROP"
}

switch action {
case "DROP":
r.Exprs = append(r.Exprs, &expr.Verdict{
Kind: expr.VerdictDrop,
})
case "REJECT":
r.Exprs = append(r.Exprs, &expr.Reject{
Type: unix.NFT_REJECT_ICMP_UNREACH,
Code: unix.NFT_REJECT_ICMPX_ADMIN_PROHIBITED,
})
} else {
r.Exprs = append(r.Exprs, &expr.Verdict{
Kind: expr.VerdictDrop,
})
default:
return nil, fmt.Errorf("invalid deny_action '%s', must be one of DROP, REJECT", action)
}

return r
log.Tracef("using '%s' as deny_action", action)

return r, nil
}

func (c *nftContext) deleteElementChunk(els []nftables.SetElement) error {
Expand Down
1 change: 1 addition & 0 deletions test/bouncer/test_firewall_bouncer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import json


def test_backend_mode(bouncer, fw_cfg_factory):
cfg = fw_cfg_factory()

Expand Down
49 changes: 49 additions & 0 deletions test/bouncer/test_iptables_deny_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

def test_iptables_deny_action(bouncer, fw_cfg_factory):
cfg = fw_cfg_factory()

cfg['log_level'] = 'trace'
cfg['mode'] = 'iptables'

with bouncer(cfg) as fw:
fw.wait_for_lines_fnmatch([
"*using 'DROP' as deny_action*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

cfg['deny_action'] = 'drop'

with bouncer(cfg) as fw:
fw.wait_for_lines_fnmatch([
"*using 'DROP' as deny_action*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

cfg['deny_action'] = 'reject'

with bouncer(cfg) as fw:
fw.wait_for_lines_fnmatch([
"*using 'REJECT' as deny_action*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

cfg['deny_action'] = 'tarpit'

with bouncer(cfg) as fw:
fw.wait_for_lines_fnmatch([
"*using 'TARPIT' as deny_action*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

cfg['deny_action'] = 'somethingelse'

with bouncer(cfg) as fw:
fw.wait_for_lines_fnmatch([
"*invalid deny_action 'somethingelse', must be one of DROP, REJECT, TARPIT*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

0 comments on commit 9b7047a

Please sign in to comment.