parent
3cb9dbdb21
commit
bfda483c0a
|
@ -3,7 +3,9 @@ package csconfig
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -331,43 +333,59 @@ type capiWhitelists struct {
|
||||||
Cidrs []string `yaml:"cidrs"`
|
Cidrs []string `yaml:"cidrs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseCapiWhitelists(fd io.Reader) (*CapiWhitelist, error) {
|
||||||
|
fromCfg := capiWhitelists{}
|
||||||
|
|
||||||
|
decoder := yaml.NewDecoder(fd)
|
||||||
|
if err := decoder.Decode(&fromCfg); err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return nil, fmt.Errorf("empty file")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret := &CapiWhitelist{
|
||||||
|
Ips: make([]net.IP, len(fromCfg.Ips)),
|
||||||
|
Cidrs: make([]*net.IPNet, len(fromCfg.Cidrs)),
|
||||||
|
}
|
||||||
|
for idx, v := range fromCfg.Ips {
|
||||||
|
ip := net.ParseIP(v)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, fmt.Errorf("invalid IP address: %s", v)
|
||||||
|
}
|
||||||
|
ret.Ips[idx] = ip
|
||||||
|
}
|
||||||
|
for idx, v := range fromCfg.Cidrs {
|
||||||
|
_, tnet, err := net.ParseCIDR(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Cidrs[idx] = tnet
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *LocalApiServerCfg) LoadCapiWhitelists() error {
|
func (s *LocalApiServerCfg) LoadCapiWhitelists() error {
|
||||||
if s.CapiWhitelistsPath == "" {
|
if s.CapiWhitelistsPath == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(s.CapiWhitelistsPath); os.IsNotExist(err) {
|
if _, err := os.Stat(s.CapiWhitelistsPath); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("capi whitelist file '%s' does not exist", s.CapiWhitelistsPath)
|
return fmt.Errorf("capi whitelist file '%s' does not exist", s.CapiWhitelistsPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := os.Open(s.CapiWhitelistsPath)
|
fd, err := os.Open(s.CapiWhitelistsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to open capi whitelist file '%s': %s", s.CapiWhitelistsPath, err)
|
return fmt.Errorf("while opening capi whitelist file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromCfg capiWhitelists
|
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
decoder := yaml.NewDecoder(fd)
|
|
||||||
if err := decoder.Decode(&fromCfg); err != nil {
|
s.CapiWhitelists, err = parseCapiWhitelists(fd)
|
||||||
return fmt.Errorf("while parsing capi whitelist file '%s': %s", s.CapiWhitelistsPath, err)
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("while parsing capi whitelist file '%s': %w", s.CapiWhitelistsPath, err)
|
||||||
s.CapiWhitelists = &CapiWhitelist{
|
|
||||||
Ips: make([]net.IP, len(fromCfg.Ips)),
|
|
||||||
Cidrs: make([]*net.IPNet, len(fromCfg.Cidrs)),
|
|
||||||
}
|
|
||||||
for _, v := range fromCfg.Ips {
|
|
||||||
ip := net.ParseIP(v)
|
|
||||||
if ip == nil {
|
|
||||||
return fmt.Errorf("unable to parse ip whitelist '%s'", v)
|
|
||||||
}
|
|
||||||
s.CapiWhitelists.Ips = append(s.CapiWhitelists.Ips, ip)
|
|
||||||
}
|
|
||||||
for _, v := range fromCfg.Cidrs {
|
|
||||||
_, tnet, err := net.ParseCIDR(v)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse cidr whitelist '%s' : %v", v, err)
|
|
||||||
}
|
|
||||||
s.CapiWhitelists.Cidrs = append(s.CapiWhitelists.Cidrs, tnet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package csconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -234,7 +235,7 @@ func TestLoadAPIServer(t *testing.T) {
|
||||||
DisableAPI: false,
|
DisableAPI: false,
|
||||||
},
|
},
|
||||||
expected: &LocalApiServerCfg{
|
expected: &LocalApiServerCfg{
|
||||||
Enable: ptr.Of(true),
|
Enable: ptr.Of(true),
|
||||||
PapiLogLevel: &logLevel,
|
PapiLogLevel: &logLevel,
|
||||||
},
|
},
|
||||||
expectedErr: "no database configuration provided",
|
expectedErr: "no database configuration provided",
|
||||||
|
@ -259,3 +260,67 @@ func TestLoadAPIServer(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustParseCIDRNet(s string) *net.IPNet {
|
||||||
|
_, ipNet, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("MustParseCIDR: parsing %q: %v", s, err))
|
||||||
|
}
|
||||||
|
return ipNet
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCapiWhitelists(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected *CapiWhitelist
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty file",
|
||||||
|
input: "",
|
||||||
|
expected: &CapiWhitelist{
|
||||||
|
Ips: []net.IP{},
|
||||||
|
Cidrs: []*net.IPNet{},
|
||||||
|
},
|
||||||
|
expectedErr: "empty file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty ip and cidr",
|
||||||
|
input: `{"ips": [], "cidrs": []}`,
|
||||||
|
expected: &CapiWhitelist{
|
||||||
|
Ips: []net.IP{},
|
||||||
|
Cidrs: []*net.IPNet{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "some ip",
|
||||||
|
input: `{"ips": ["1.2.3.4"]}`,
|
||||||
|
expected: &CapiWhitelist{
|
||||||
|
Ips: []net.IP{net.IPv4(1, 2, 3, 4)},
|
||||||
|
Cidrs: []*net.IPNet{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "some cidr",
|
||||||
|
input: `{"cidrs": ["1.2.3.0/24"]}`,
|
||||||
|
expected: &CapiWhitelist{
|
||||||
|
Ips: []net.IP{},
|
||||||
|
Cidrs: []*net.IPNet{mustParseCIDRNet("1.2.3.0/24")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
wl, err := parseCapiWhitelists(strings.NewReader(tc.input))
|
||||||
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
|
if tc.expectedErr != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, tc.expected, wl)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
76
test/bats/13_capi_whitelists.bats
Normal file
76
test/bats/13_capi_whitelists.bats
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
# vim: ft=bats:list:ts=8:sts=4:sw=4:et:ai:si:
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
load "../lib/setup_file.sh"
|
||||||
|
CONFIG_DIR=$(dirname "$CONFIG_YAML")
|
||||||
|
CAPI_WHITELISTS_YAML="$CONFIG_DIR/capi-whitelists.yaml"
|
||||||
|
export CAPI_WHITELISTS_YAML
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_file() {
|
||||||
|
load "../lib/teardown_file.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
load "../lib/setup.sh"
|
||||||
|
load "../lib/bats-file/load.bash"
|
||||||
|
./instance-data load
|
||||||
|
config_set '.api.server.capi_whitelists_path=strenv(CAPI_WHITELISTS_YAML)'
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
./instance-crowdsec stop
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------
|
||||||
|
|
||||||
|
@test "capi_whitelists: file missing" {
|
||||||
|
rune -1 timeout 1s "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "capi whitelist file '$CAPI_WHITELISTS_YAML' does not exist"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: error on open" {
|
||||||
|
echo > "$CAPI_WHITELISTS_YAML"
|
||||||
|
chmod 000 "$CAPI_WHITELISTS_YAML"
|
||||||
|
rune -1 timeout 1s "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "while opening capi whitelist file: open $CAPI_WHITELISTS_YAML: permission denied"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: empty file" {
|
||||||
|
echo > "$CAPI_WHITELISTS_YAML"
|
||||||
|
rune -1 timeout 1s "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "while parsing capi whitelist file '$CAPI_WHITELISTS_YAML': empty file"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: empty lists" {
|
||||||
|
echo '{"ips": [], "cidrs": []}' > "$CAPI_WHITELISTS_YAML"
|
||||||
|
rune -124 timeout 1s "${CROWDSEC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: bad ip" {
|
||||||
|
echo '{"ips": ["blahblah"], "cidrs": []}' > "$CAPI_WHITELISTS_YAML"
|
||||||
|
rune -1 timeout 1s "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "while parsing capi whitelist file '$CAPI_WHITELISTS_YAML': invalid IP address: blahblah"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: bad cidr" {
|
||||||
|
echo '{"ips": [], "cidrs": ["blahblah"]}' > "$CAPI_WHITELISTS_YAML"
|
||||||
|
rune -1 timeout 1s "${CROWDSEC}"
|
||||||
|
assert_stderr --partial "while parsing capi whitelist file '$CAPI_WHITELISTS_YAML': invalid CIDR address: blahblah"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "capi_whitelists: file with ip and cidr values" {
|
||||||
|
cat <<-EOT > "$CAPI_WHITELISTS_YAML"
|
||||||
|
ips:
|
||||||
|
- 1.2.3.4
|
||||||
|
- 2.3.4.5
|
||||||
|
cidrs:
|
||||||
|
- 1.2.3.0/24
|
||||||
|
EOT
|
||||||
|
|
||||||
|
config_set '.common.log_level="trace"'
|
||||||
|
rune -0 ./instance-crowdsec start
|
||||||
|
}
|
Loading…
Reference in a new issue