Skip to content

Commit 981e616

Browse files
authored
allowlists: apply items to existing decisions in batch (#4095)
1 parent 43aa4a0 commit 981e616

2 files changed

Lines changed: 400 additions & 37 deletions

File tree

pkg/database/allowlists.go

Lines changed: 91 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import (
1515
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlist"
1616
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlistitem"
1717
"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
18+
"github.com/crowdsecurity/crowdsec/pkg/database/ent/predicate"
1819
"github.com/crowdsecurity/crowdsec/pkg/models"
1920
)
2021

22+
const allowlistExpireDecisionsBatchSize = 300
23+
2124
func (c *Client) CreateAllowList(ctx context.Context, name string, description string, allowlistID string, fromConsole bool) (*ent.AllowList, error) {
2225
allowlist, err := c.Ent.AllowList.Create().
2326
SetName(name).
@@ -403,7 +406,6 @@ func (c *Client) ApplyAllowlistsToExistingDecisions(ctx context.Context) (int, e
403406
totalCount := 0
404407

405408
// Get all non-expired allowlist items
406-
// We will match them one by one against all decisions
407409
allowlistItems, err := c.Ent.AllowListItem.Query().
408410
Where(
409411
allowlistitem.Or(
@@ -415,78 +417,130 @@ func (c *Client) ApplyAllowlistsToExistingDecisions(ctx context.Context) (int, e
415417
return 0, fmt.Errorf("unable to get allowlist items: %w", err)
416418
}
417419

418-
now := time.Now().UTC()
420+
if len(allowlistItems) == 0 {
421+
return 0, nil
422+
}
419423

420-
for _, item := range allowlistItems {
421-
updateQuery := c.Ent.Decision.Update().SetUntil(now).Where(decision.UntilGTE(now))
424+
ipv4Items := make([]*ent.AllowListItem, 0)
425+
ipv6Items := make([]*ent.AllowListItem, 0)
422426

427+
for _, item := range allowlistItems {
423428
switch item.IPSize {
424429
case 4:
425-
updateQuery = updateQuery.Where(
426-
decision.And(
427-
decision.IPSizeEQ(4),
430+
ipv4Items = append(ipv4Items, item)
431+
case 16:
432+
ipv6Items = append(ipv6Items, item)
433+
default:
434+
c.Log.Errorf("unexpected IP size %d for allowlist item %s", item.IPSize, item.Value)
435+
}
436+
}
437+
438+
now := time.Now().UTC()
439+
440+
if len(ipv4Items) > 0 {
441+
count, err := c.applyAllowlistBatch(ctx, ipv4Items, 4, now, allowlistExpireDecisionsBatchSize)
442+
if err != nil {
443+
c.Log.Errorf("unable to apply IPv4 allowlists: %s", err)
444+
} else {
445+
totalCount += count
446+
}
447+
}
448+
449+
if len(ipv6Items) > 0 {
450+
count, err := c.applyAllowlistBatch(ctx, ipv6Items, 16, now, allowlistExpireDecisionsBatchSize)
451+
if err != nil {
452+
c.Log.Errorf("unable to apply IPv6 allowlists: %s", err)
453+
} else {
454+
totalCount += count
455+
}
456+
}
457+
458+
return totalCount, nil
459+
}
460+
461+
func (c *Client) applyAllowlistBatch(ctx context.Context, items []*ent.AllowListItem, ipSize int64, now time.Time, batchSize int) (int, error) {
462+
totalCount := 0
463+
464+
for i := 0; i < len(items); i += batchSize {
465+
end := min(i+batchSize, len(items))
466+
467+
batch := items[i:end]
468+
469+
var conditions []predicate.Decision
470+
471+
for _, item := range batch {
472+
if ipSize == 4 {
473+
conditions = append(conditions,
428474
decision.Or(
429-
decision.And(
430-
decision.StartIPLTE(item.StartIP),
431-
decision.EndIPGTE(item.EndIP),
432-
),
475+
// Decision contained inside allowlist range or exact match
433476
decision.And(
434477
decision.StartIPGTE(item.StartIP),
435478
decision.EndIPLTE(item.EndIP),
436479
),
437-
)))
438-
case 16:
439-
updateQuery = updateQuery.Where(
440-
decision.And(
441-
decision.IPSizeEQ(16),
480+
// Decision contains allowlist range
481+
decision.And(
482+
decision.StartIPLTE(item.StartIP),
483+
decision.EndIPGTE(item.EndIP),
484+
),
485+
),
486+
)
487+
} else { // ipSize == 16
488+
conditions = append(conditions,
442489
decision.Or(
490+
// Decision contained inside allowlist range or exact match
443491
decision.And(
444492
decision.Or(
445-
decision.StartIPLT(item.StartIP),
493+
decision.StartIPGT(item.StartIP),
446494
decision.And(
447495
decision.StartIPEQ(item.StartIP),
448-
decision.StartSuffixLTE(item.StartSuffix),
449-
)),
496+
decision.StartSuffixGTE(item.StartSuffix),
497+
),
498+
),
450499
decision.Or(
451-
decision.EndIPGT(item.EndIP),
500+
decision.EndIPLT(item.EndIP),
452501
decision.And(
453502
decision.EndIPEQ(item.EndIP),
454-
decision.EndSuffixGTE(item.EndSuffix),
503+
decision.EndSuffixLTE(item.EndSuffix),
455504
),
456505
),
457506
),
507+
// Decision contains allowlist range
458508
decision.And(
459509
decision.Or(
460-
decision.StartIPGT(item.StartIP),
510+
decision.StartIPLT(item.StartIP),
461511
decision.And(
462512
decision.StartIPEQ(item.StartIP),
463-
decision.StartSuffixGTE(item.StartSuffix),
464-
)),
513+
decision.StartSuffixLTE(item.StartSuffix),
514+
),
515+
),
465516
decision.Or(
466-
decision.EndIPLT(item.EndIP),
517+
decision.EndIPGT(item.EndIP),
467518
decision.And(
468519
decision.EndIPEQ(item.EndIP),
469-
decision.EndSuffixLTE(item.EndSuffix),
520+
decision.EndSuffixGTE(item.EndSuffix),
470521
),
471522
),
472523
),
473524
),
474-
),
475-
)
476-
default:
477-
// This should never happen
478-
// But better safe than sorry and just skip it instead of expiring all decisions
479-
c.Log.Errorf("unexpected IP size %d for allowlist item %s", item.IPSize, item.Value)
480-
continue
525+
)
526+
}
481527
}
482-
// Update the decisions
483-
count, err := updateQuery.Save(ctx)
528+
529+
count, err := c.Ent.Decision.Update().
530+
SetUntil(now).
531+
Where(
532+
decision.UntilGTE(now),
533+
decision.IPSizeEQ(ipSize),
534+
decision.Or(conditions...),
535+
).
536+
Save(ctx)
537+
484538
if err != nil {
485-
c.Log.Errorf("unable to expire existing decisions: %s", err)
486-
continue
539+
return totalCount, fmt.Errorf("unable to expire decisions for batch: %w", err)
487540
}
488541

489542
totalCount += count
543+
c.Log.Debugf("expired %d decisions for batch of %d allowlist items", count, len(batch))
490544
}
491545

492546
return totalCount, nil

0 commit comments

Comments
 (0)