Skip to content

Commit

Permalink
Merge pull request #20 from crowdsecurity/configure_chains
Browse files Browse the repository at this point in the history
Configurable chains
  • Loading branch information
buixor authored Jan 4, 2021
2 parents 1fe80ec + c8bd52d commit d065585
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 39 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,16 @@ log_dir: /var/log/
log_level: info
api_url: <API_URL> # when install, default is "localhost:8080"
api_key: <API_KEY> # Add your API key generated with `cscli bouncers add --name <bouncer_name>`
#if present, insert rule in those chains
iptables_chains:
- INPUT
- FORWARD
```
- `mode` can be set to `iptables` or `nftables`
- `update_frequency` controls how often the bouncer is going to query the local API
- `api_url` and `api_key` control local API parameters.
- `iptables_chains` allows (in _iptables_ mode) to control in which chain rules are going to be inserted. (if empty,the bouncer will only maintain ipset lists)

You can then start the service:

Expand Down
15 changes: 8 additions & 7 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ func (b *backendCTX) Delete(decision *models.Decision) error {
return nil
}

func newBackend(backendType string, disableIPV6 bool) (*backendCTX, error) {
func newBackend(config *bouncerConfig) (*backendCTX, error) {
var ok bool

b := &backendCTX{}
log.Printf("backend type : %s", backendType)
if disableIPV6 {
log.Printf("backend type : %s", config.Mode)
if config.DisableIPV6 {
log.Println("IPV6 is disabled")
}
switch backendType {
switch config.Mode {
case "iptables":
tmpCtx, err := newIPTables(disableIPV6)
tmpCtx, err := newIPTables(config)
if err != nil {
return nil, err
}
Expand All @@ -66,7 +67,7 @@ func newBackend(backendType string, disableIPV6 bool) (*backendCTX, error) {
return nil, fmt.Errorf("unexpected type '%T' for iptables context", tmpCtx)
}
case "nftables":
tmpCtx, err := newNFTables(disableIPV6)
tmpCtx, err := newNFTables(config)
if err != nil {
return nil, err
}
Expand All @@ -75,7 +76,7 @@ func newBackend(backendType string, disableIPV6 bool) (*backendCTX, error) {
return nil, fmt.Errorf("unexpected type '%T' for nftables context", tmpCtx)
}
default:
return b, fmt.Errorf("firewall '%s' is not supported", backendType)
return b, fmt.Errorf("firewall '%s' is not supported", config.Mode)
}
return b, nil
}
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type bouncerConfig struct {
APIUrl string `yaml:"api_url"`
APIKey string `yaml:"api_key"`
DisableIPV6 bool `yaml:"disable_ipv6"`
//specific to iptables, following https://github.com/crowdsecurity/cs-firewall-bouncer/issues/19
IptablesChains []string `yaml:"iptables_chains"`
}

func NewConfig(configPath string) (*bouncerConfig, error) {
Expand Down
10 changes: 9 additions & 1 deletion config/cs-firewall-bouncer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@ log_dir: /var/log/
log_level: info
api_url: http://localhost:8080/
api_key: ${API_KEY}
disable_ipv6: false
disable_ipv6: false
#if present, insert rule in those chains
iptables_chains:
- INPUT
# - FORWARD
# - DOCKER-USER



33 changes: 24 additions & 9 deletions iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ type iptables struct {

var iptablesCtx = &iptables{}

func newIPTables(disableIPV6 bool) (interface{}, error) {
func newIPTables(config *bouncerConfig) (interface{}, error) {
var err error
ipv4Ctx := &ipTablesContext{
Name: "ipset",
version: "v4",
SetName: "crowdsec-blacklists",
StartupCmds: []string{"-I", "INPUT", "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"},
ShutdownCmds: []string{"-D", "INPUT", "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"},
CheckIptableCmds: []string{"-C", "INPUT", "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"},
StartupCmds: [][]string{},
ShutdownCmds: [][]string{},
CheckIptableCmds: [][]string{},
}
for _, v := range config.IptablesChains {
ipv4Ctx.StartupCmds = append(ipv4Ctx.StartupCmds,
[]string{"-I", v, "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"})
ipv4Ctx.ShutdownCmds = append(ipv4Ctx.ShutdownCmds,
[]string{"-D", v, "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"})
ipv4Ctx.CheckIptableCmds = append(ipv4Ctx.CheckIptableCmds,
[]string{"-C", v, "-m", "set", "--match-set", "crowdsec-blacklists", "src", "-j", "DROP"})
}

ipsetBin, err := exec.LookPath("ipset")
if err != nil {
return nil, fmt.Errorf("unable to find ipset")
Expand All @@ -42,14 +49,22 @@ func newIPTables(disableIPV6 bool) (interface{}, error) {
v4: ipv4Ctx,
}

if !disableIPV6 {
if !config.DisableIPV6 {
ipv6Ctx := &ipTablesContext{
Name: "ipset",
version: "v6",
SetName: "crowdsec6-blacklists",
StartupCmds: []string{"-I", "INPUT", "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"},
ShutdownCmds: []string{"-D", "INPUT", "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"},
CheckIptableCmds: []string{"-C", "INPUT", "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"},
StartupCmds: [][]string{},
ShutdownCmds: [][]string{},
CheckIptableCmds: [][]string{},
}
for _, v := range config.IptablesChains {
ipv6Ctx.StartupCmds = append(ipv6Ctx.StartupCmds,
[]string{"-I", v, "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"})
ipv6Ctx.ShutdownCmds = append(ipv6Ctx.ShutdownCmds,
[]string{"-D", v, "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"})
ipv6Ctx.CheckIptableCmds = append(ipv6Ctx.CheckIptableCmds,
[]string{"-C", v, "-m", "set", "--match-set", "crowdsec6-blacklists", "src", "-j", "DROP"})
}
ipv6Ctx.ipsetBin = ipsetBin
ipv6Ctx.iptablesBin, err = exec.LookPath("ip6tables")
Expand Down
64 changes: 45 additions & 19 deletions iptables_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"strings"
"time"

"github.com/pkg/errors"

"github.com/crowdsecurity/crowdsec/pkg/models"
log "github.com/sirupsen/logrus"
)
Expand All @@ -15,15 +17,16 @@ type ipTablesContext struct {
version string
ipsetBin string
iptablesBin string
SetName string //crowdsec-netfilter
StartupCmds []string //-I INPUT -m set --match-set myset src -j DROP
ShutdownCmds []string //-D INPUT -m set --match-set myset src -j DROP
CheckIptableCmds []string
SetName string //crowdsec-netfilter
StartupCmds [][]string //-I INPUT -m set --match-set myset src -j DROP
ShutdownCmds [][]string //-D INPUT -m set --match-set myset src -j DROP
CheckIptableCmds [][]string
}

func (ctx *ipTablesContext) CheckAndCreate() error {
var err error

log.Infof("Checking existing set")
/* check if the set already exist */
cmd := exec.Command(ctx.ipsetBin, "-L", ctx.SetName)
if _, err = cmd.CombinedOutput(); err != nil { // if doesn't exist, create it
Expand All @@ -42,15 +45,32 @@ func (ctx *ipTablesContext) CheckAndCreate() error {
time.Sleep(1 * time.Second)

// checking if iptables rules exist
cmd = exec.Command(ctx.iptablesBin, ctx.CheckIptableCmds...)
if _, err := cmd.CombinedOutput(); err != nil { // if doesn't exist, create it
cmd = exec.Command(ctx.iptablesBin, ctx.StartupCmds...)
log.Infof("iptables set-up : %s", cmd.String())
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("Error while insert set in iptables (%s): %v --> %s", cmd.String(), err, string(out))
checkOk := true
for _, checkCmd := range ctx.CheckIptableCmds {
cmd = exec.Command(ctx.iptablesBin, checkCmd...)
if stdout, err := cmd.CombinedOutput(); err != nil {
checkOk = false
/*rule doesn't exist, avoid alarming error messages*/
if strings.Contains(string(stdout), "iptables: Bad rule") {
log.Infof("Rule doesn't exist (%s)", cmd.String())
} else {
log.Warningf("iptables check command (%s) failed : %s", cmd.String(), err)
log.Debugf("output: %s", string(stdout))
}
}
}
/*if any of the check command error'ed, exec the setup command*/
if !checkOk {
// if doesn't exist, create it
for _, startCmd := range ctx.StartupCmds {
cmd = exec.Command(ctx.iptablesBin, startCmd...)
log.Infof("iptables set-up : %s", cmd.String())
if out, err := cmd.CombinedOutput(); err != nil {
log.Warningf("Error inserting set in iptables (%s): %v : %s", cmd.String(), err, string(out))
return errors.Wrapf(err, "while inserting set in iptables")
}
}
}

return nil
}

Expand All @@ -73,16 +93,22 @@ func (ctx *ipTablesContext) add(decision *models.Decision) error {

func (ctx *ipTablesContext) shutDown() error {
/*clean iptables rules*/
cmd := exec.Command(ctx.iptablesBin, ctx.ShutdownCmds...)
log.Infof("iptables clean-up : %s", cmd.String())
if out, err := cmd.CombinedOutput(); err != nil {
/*if the set doesn't exist, don't frigthen user with error messages*/
if strings.Contains(string(out), "Set crowdsec-blacklists doesn't exist.") {
log.Infof("ipset 'crowdsec-blacklists' doesn't exist, skip")
} else {
log.Errorf("error while removing set entry in iptables : %v --> %s", err, string(out))
var cmd *exec.Cmd
// if doesn't exist, create it
for _, startCmd := range ctx.ShutdownCmds {
cmd = exec.Command(ctx.iptablesBin, startCmd...)
log.Infof("iptables clean-up : %s", cmd.String())
if out, err := cmd.CombinedOutput(); err != nil {
if strings.Contains(string(out), "Set crowdsec-blacklists doesn't exist.") {
log.Infof("ipset 'crowdsec-blacklists' doesn't exist, skip")
} else if strings.Contains(string(out), "Set crowdsec6-blacklists doesn't exist.") {
log.Infof("ipset 'crowdsec6-blacklists' doesn't exist, skip")
} else {
log.Errorf("error while removing set entry in iptables : %v --> %s", err, string(out))
}
}
}

/*clean ipset set*/
cmd = exec.Command(ctx.ipsetBin, "-exist", "destroy", ctx.SetName)
log.Infof("ipset clean-up : %s", cmd.String())
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func main() {
log.SetLevel(log.DebugLevel)
}

backend, err := newBackend(config.Mode, config.DisableIPV6)
backend, err := newBackend(config)
if err != nil {
log.Fatalf(err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions nftables.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ type nft struct {
table6 *nftables.Table
}

func newNFTables(disableIPV6 bool) (interface{}, error) {
func newNFTables(config *bouncerConfig) (interface{}, error) {
ret := &nft{}

ret.conn = &nftables.Conn{}
if !disableIPV6 {
if !config.DisableIPV6 {
ret.conn6 = &nftables.Conn{}
}
return ret, nil
Expand Down

0 comments on commit d065585

Please sign in to comment.