Skip to content

Commit

Permalink
config: apply variable expansion to all keys (#364)
Browse files Browse the repository at this point in the history
* fix ennvar expansion in configuration file
* CI: test variable expansion
* lint
  • Loading branch information
mmetc authored Mar 11, 2024
1 parent ee9cca4 commit 0d19a68
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 26 deletions.
25 changes: 16 additions & 9 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package cmd

import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"net"
Expand All @@ -20,6 +20,7 @@ import (

csbouncer "github.com/crowdsecurity/go-cs-bouncer"
"github.com/crowdsecurity/go-cs-lib/csdaemon"
"github.com/crowdsecurity/go-cs-lib/csstring"
"github.com/crowdsecurity/go-cs-lib/version"

"github.com/crowdsecurity/crowdsec/pkg/models"
Expand Down Expand Up @@ -47,9 +48,9 @@ func HandleSignals(ctx context.Context) error {
case s := <-signalChan:
switch s {
case syscall.SIGTERM:
return fmt.Errorf("received SIGTERM")
return errors.New("received SIGTERM")
case os.Interrupt: // cross-platform SIGINT
return fmt.Errorf("received interrupt")
return errors.New("received interrupt")
}
case <-ctx.Done():
return ctx.Err()
Expand All @@ -76,6 +77,7 @@ func deleteDecisions(backend *backend.BackendCTX, decisions []*models.Decision,
}

log.Debugf("deleted %s", *d.Value)

nbDeletedDecisions++
}

Expand Down Expand Up @@ -112,6 +114,7 @@ func addDecisions(backend *backend.BackendCTX, decisions []*models.Decision, con
}

log.Debugf("Adding '%s' for '%s'", *d.Value, *d.Duration)

nbNewDecisions++
}

Expand Down Expand Up @@ -149,20 +152,22 @@ func Execute() error {
}

if configPath == nil || *configPath == "" {
return fmt.Errorf("configuration file is required")
return errors.New("configuration file is required")
}

configBytes, err := cfg.MergedConfig(*configPath)
configMerged, err := cfg.MergedConfig(*configPath)
if err != nil {
return fmt.Errorf("unable to read config file: %w", err)
}

if *showConfig {
fmt.Println(string(configBytes))
fmt.Println(string(configMerged))
return nil
}

config, err := cfg.NewConfig(bytes.NewReader(configBytes))
configExpanded := csstring.StrictExpand(string(configMerged), os.LookupEnv)

config, err := cfg.NewConfig(strings.NewReader(configExpanded))
if err != nil {
return fmt.Errorf("unable to load configuration: %w", err)
}
Expand All @@ -186,7 +191,7 @@ func Execute() error {

bouncer := &csbouncer.StreamBouncer{}

err = bouncer.ConfigReader(bytes.NewReader(configBytes))
err = bouncer.ConfigReader(strings.NewReader(configExpanded))
if err != nil {
return err
}
Expand All @@ -209,7 +214,7 @@ func Execute() error {

g.Go(func() error {
bouncer.Run(ctx)
return fmt.Errorf("bouncer stream halted")
return errors.New("bouncer stream halted")
})

if config.PrometheusConfig.Enabled {
Expand All @@ -234,6 +239,7 @@ func Execute() error {

g.Go(func() error {
log.Infof("Processing new and deleted decisions . . .")

for {
select {
case <-ctx.Done():
Expand All @@ -242,6 +248,7 @@ func Execute() error {
if decisions == nil {
continue
}

deleteDecisions(backend, decisions.Deleted, config)
addDecisions(backend, decisions.New, config)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package backend

import (
"errors"
"fmt"
"runtime"

Expand Down Expand Up @@ -72,7 +73,7 @@ func NewBackend(config *cfg.BouncerConfig) (*BackendCTX, error) {
switch config.Mode {
case cfg.IptablesMode, cfg.IpsetMode:
if runtime.GOOS != "linux" {
return nil, fmt.Errorf("iptables and ipset is linux only")
return nil, errors.New("iptables and ipset is linux only")
}

b.firewall, err = iptables.NewIPTables(config)
Expand All @@ -81,7 +82,7 @@ func NewBackend(config *cfg.BouncerConfig) (*BackendCTX, error) {
}
case cfg.NftablesMode:
if runtime.GOOS != "linux" {
return nil, fmt.Errorf("nftables is linux only")
return nil, errors.New("nftables is linux only")
}

b.firewall, err = nftables.NewNFTables(config)
Expand Down
13 changes: 5 additions & 8 deletions pkg/cfg/config.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package cfg

import (
"errors"
"fmt"
"io"
"os"

log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"

"github.com/crowdsecurity/go-cs-lib/csstring"
"github.com/crowdsecurity/go-cs-lib/ptr"
"github.com/crowdsecurity/go-cs-lib/yamlpatch"
)
Expand Down Expand Up @@ -86,9 +85,7 @@ func NewConfig(reader io.Reader) (*BouncerConfig, error) {
return nil, err
}

configBuff := csstring.StrictExpand(string(fcontent), os.LookupEnv)

err = yaml.Unmarshal([]byte(configBuff), &config)
err = yaml.Unmarshal(fcontent, &config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal: %w", err)
}
Expand All @@ -98,7 +95,7 @@ func NewConfig(reader io.Reader) (*BouncerConfig, error) {
}

if config.Mode == "" {
return nil, fmt.Errorf("config does not contain 'mode'")
return nil, errors.New("config does not contain 'mode'")
}

if len(config.SupportedDecisionsTypes) == 0 {
Expand Down Expand Up @@ -152,7 +149,7 @@ func NewConfig(reader io.Reader) (*BouncerConfig, error) {
return config, nil
}

func pfConfig(config *BouncerConfig) error {
func pfConfig(_ *BouncerConfig) error {
return nil
}

Expand Down Expand Up @@ -191,7 +188,7 @@ func nftablesConfig(config *BouncerConfig) error {
}

if !*config.Nftables.Ipv4.Enabled && !*config.Nftables.Ipv6.Enabled {
return fmt.Errorf("both IPv4 and IPv6 disabled, doing nothing")
return errors.New("both IPv4 and IPv6 disabled, doing nothing")
}

if config.NftablesHooks == nil || len(config.NftablesHooks) == 0 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/cfg/logging.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cfg

import (
"fmt"
"errors"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -75,7 +75,7 @@ func (c *LoggingConfig) setDefaults() {

func (c *LoggingConfig) validate() error {
if c.LogMode != "stdout" && c.LogMode != "file" {
return fmt.Errorf("log_mode should be either 'stdout' or 'file'")
return errors.New("log_mode should be either 'stdout' or 'file'")
}

return nil
Expand Down
11 changes: 6 additions & 5 deletions pkg/iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package iptables

import (
"errors"
"fmt"
"os/exec"
"strings"
Expand Down Expand Up @@ -68,7 +69,7 @@ func NewIPTables(config *cfg.BouncerConfig) (types.Backend, error) {

ipsetBin, err := exec.LookPath("ipset")
if err != nil {
return nil, fmt.Errorf("unable to find ipset")
return nil, errors.New("unable to find ipset")
}

ipv4Ctx.ipsetBin = ipsetBin
Expand All @@ -77,7 +78,7 @@ func NewIPTables(config *cfg.BouncerConfig) (types.Backend, error) {
} else {
ipv4Ctx.iptablesBin, err = exec.LookPath("iptables")
if err != nil {
return nil, fmt.Errorf("unable to find iptables")
return nil, errors.New("unable to find iptables")
}
ipv4Ctx.Chains = config.IptablesChains
for _, v := range config.IptablesChains {
Expand Down Expand Up @@ -109,7 +110,7 @@ func NewIPTables(config *cfg.BouncerConfig) (types.Backend, error) {
} else {
ipv6Ctx.iptablesBin, err = exec.LookPath("ip6tables")
if err != nil {
return nil, fmt.Errorf("unable to find ip6tables")
return nil, errors.New("unable to find ip6tables")
}
ipv6Ctx.Chains = config.IptablesChains
for _, v := range config.IptablesChains {
Expand Down Expand Up @@ -237,15 +238,15 @@ func (ipt *iptables) Delete(decision *models.Decision) error {
}

if err := ipt.v6.delete(decision); err != nil {
return fmt.Errorf("failed deleting ban")
return errors.New("failed deleting ban")
}

done = true
}

if strings.Contains(*decision.Value, ".") {
if err := ipt.v4.delete(decision); err != nil {
return fmt.Errorf("failed deleting ban")
return errors.New("failed deleting ban")
}

done = true
Expand Down
17 changes: 17 additions & 0 deletions test/bouncer/test_yaml_local.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os


def test_yaml_local(bouncer, fw_cfg_factory):
cfg = fw_cfg_factory()
Expand All @@ -21,3 +23,18 @@ def test_yaml_local(bouncer, fw_cfg_factory):
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

# variable expansion

config_local = {
'mode': '$BOUNCER_MODE'
}

os.environ['BOUNCER_MODE'] = 'fromenv'

with bouncer(cfg, config_local=config_local) as fw:
fw.wait_for_lines_fnmatch([
"*firewall 'fromenv' is not supported*",
])
fw.proc.wait(timeout=0.2)
assert not fw.proc.is_running()

0 comments on commit 0d19a68

Please sign in to comment.