@@ -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+
2124func (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