Skip to content

Commit 7987076

Browse files
cscli: Add allowlist check (#3684)
1 parent 12d2e32 commit 7987076

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed

cmd/crowdsec-cli/cliallowlists/allowlists.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ func (cli *cliAllowLists) NewCommand() *cobra.Command {
239239
cmd.AddCommand(cli.newAddCmd())
240240
cmd.AddCommand(cli.newRemoveCmd())
241241
cmd.AddCommand(cli.newInspectCmd())
242+
cmd.AddCommand(cli.newCheckCmd())
242243

243244
return cmd
244245
}
@@ -441,6 +442,20 @@ func (cli *cliAllowLists) newAddCmd() *cobra.Command {
441442
return cmd
442443
}
443444

445+
func (cli *cliAllowLists) newCheckCmd() *cobra.Command {
446+
cmd := &cobra.Command{
447+
Use: "check [value...]",
448+
Short: "Check if a value is in an allowlist",
449+
Example: `cscli allowlists check 1.2.3.4`,
450+
Args: args.MinimumNArgs(1),
451+
RunE: func(cmd *cobra.Command, args []string) error {
452+
return cli.check(cmd.Context(), args)
453+
},
454+
}
455+
456+
return cmd
457+
}
458+
444459
func (cli *cliAllowLists) add(ctx context.Context, db *database.Client, name string, values []string, expiration time.Duration, comment string) error {
445460
allowlist, err := db.GetAllowList(ctx, name, true)
446461
if err != nil {
@@ -639,3 +654,50 @@ func (cli *cliAllowLists) remove(ctx context.Context, db *database.Client, name
639654

640655
return nil
641656
}
657+
658+
func (cli *cliAllowLists) check(ctx context.Context, ips []string) error {
659+
cfg := cli.cfg()
660+
661+
if err := require.LAPI(cfg); err != nil {
662+
return err
663+
}
664+
665+
if err := cfg.LoadAPIClient(); err != nil {
666+
return fmt.Errorf("loading api client: %w", err)
667+
}
668+
669+
apiURL, err := url.Parse(cfg.API.Client.Credentials.URL)
670+
if err != nil {
671+
return fmt.Errorf("parsing api url: %w", err)
672+
}
673+
674+
client, err := apiclient.NewClient(&apiclient.Config{
675+
MachineID: cfg.API.Client.Credentials.Login,
676+
Password: strfmt.Password(cfg.API.Client.Credentials.Password),
677+
URL: apiURL,
678+
VersionPrefix: "v1",
679+
})
680+
if err != nil {
681+
return fmt.Errorf("creating api client: %w", err)
682+
}
683+
684+
for _, ip := range ips {
685+
resp, _, err := client.Allowlists.CheckIfAllowlistedWithReason(ctx, ip)
686+
if err != nil {
687+
return fmt.Errorf("cannot check if %s is in allowlist: %w", ip, err)
688+
}
689+
690+
reason := ""
691+
if resp.Allowlisted {
692+
reason = resp.Reason
693+
}
694+
if reason == "" {
695+
fmt.Fprintf(os.Stdout, "%s is not allowlisted\n", ip)
696+
return nil
697+
}
698+
699+
fmt.Fprintf(os.Stdout, "%s is allowlisted by item %s\n", ip, reason)
700+
}
701+
702+
return nil
703+
}

pkg/database/allowlists.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"net"
7+
"sort"
78
"strings"
89
"time"
910

@@ -237,13 +238,16 @@ func (c *Client) ReplaceAllowlist(ctx context.Context, list *ent.AllowList, item
237238
return added, nil
238239
}
239240

240-
func (c *Client) IsAllowlistedBy(ctx context.Context, value string) ([]string, error) {
241-
/*
242-
Few cases:
243-
- value is an IP/range directly is in allowlist
244-
- value is an IP/range in a range in allowlist
245-
- value is a range and an IP/range belonging to it is in allowlist
246-
*/
241+
// IsAllowlistedBy returns a list of human-readable reasons explaining which allowlists
242+
// the given value (IP or CIDR) matches.
243+
//
244+
// Few cases:
245+
// - value is an IP/range directly is in allowlist
246+
// - value is an IP/range in a range in allowlist
247+
// - value is a range and an IP/range belonging to it is in allowlist
248+
//
249+
// The result is sorted by the name of the associated allowlist for consistent presentation.
250+
func (c *Client) IsAllowlistedBy(ctx context.Context, value string) (reasons []string, err error) {
247251
rng, err := csnet.NewRange(value)
248252
if err != nil {
249253
return nil, err
@@ -320,7 +324,10 @@ func (c *Client) IsAllowlistedBy(ctx context.Context, value string) ([]string, e
320324
return nil, fmt.Errorf("unable to check if value is allowlisted: %w", err)
321325
}
322326

323-
reasons := make([]string, 0)
327+
// doing this in ent is not worth the complexity
328+
sort.SliceStable(items, func(i, j int) bool {
329+
return items[i].Edges.Allowlist[0].Name < items[j].Edges.Allowlist[0].Name
330+
})
324331

325332
for _, item := range items {
326333
if len(item.Edges.Allowlist) == 0 {

test/bats/cscli-allowlists.bats

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ teardown() {
167167
assert_stderr --partial 'Decision successfully added'
168168
}
169169

170+
@test "cscli allowlists check" {
171+
rune -0 cscli allowlist create foo -d 'a foo'
172+
rune -0 cscli allowlist add foo 192.168.0.0/16
173+
rune -0 cscli allowlist check 192.168.0.1
174+
assert_output "192.168.0.1 is allowlisted by item 192.168.0.0/16 from foo"
175+
rune -0 cscli allowlist check 192.169.0.1
176+
assert_output "192.169.0.1 is not allowlisted"
177+
rune -0 cscli allowlist create bar -d 'a bar'
178+
rune -0 cscli allowlist add bar 192.168.0.0/24
179+
rune -0 cscli allowlist create Uppercase -d 'a uppercase'
180+
rune -0 cscli allowlist add Uppercase 192.168.0.0/28
181+
rune -0 cscli allowlist check 192.168.0.1 1.1.1.1
182+
assert_line "192.168.0.1 is allowlisted by item 192.168.0.0/28 from Uppercase, 192.168.0.0/24 from bar, 192.168.0.0/16 from foo"
183+
assert_line "1.1.1.1 is not allowlisted"
184+
refute_stderr
185+
}
170186
@test "cscli allowlists delete" {
171187
rune -1 cscli allowlist delete
172188
assert_stderr 'Error: accepts 1 arg(s), received 0'

0 commit comments

Comments
 (0)