From 3dfe000282e3a32bbecff9fcb480866645e07f02 Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 24 Mar 2026 14:05:37 +0100 Subject: [PATCH 1/8] feat: allow acl to be delegated --- pkg/cfg/config.go | 7 ++++++ pkg/waf/waf.go | 64 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/pkg/cfg/config.go b/pkg/cfg/config.go index a13c534..83dd63d 100644 --- a/pkg/cfg/config.go +++ b/pkg/cfg/config.go @@ -45,6 +45,7 @@ type AclConfig struct { CloudWatchMetricName string `yaml:"cloudwatch_metric_name"` SampleRequests bool `yaml:"sample_requests"` CleanOnStart bool `yaml:"remove_sets_on_start"` + DelegateAclManagement bool `yaml:"delegate_acl_management"` } var ValidActions = []string{"ban", "captcha", "count"} @@ -130,6 +131,12 @@ func getConfigFromEnv(config *bouncerConfig) { log.Warnf("Invalid value for %s: %s, defaulting to false", key, value) acl.CleanOnStart = false } + case "DELEGATE_ACL_MANAGEMENT": + acl.DelegateAclManagement, err = strconv.ParseBool(value) + if err != nil { + log.Warnf("Invalid value for %s: %s, defaulting to false", key, value) + acl.DelegateAclManagement = false + } } } else { switch key { diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index b4c8529..ef29580 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -221,6 +221,23 @@ func (w *WAF) DeleteRuleGroup(ctx context.Context, ruleGroupName string, token s return err } +func (w *WAF) UnassignRulesFromRuleGroup(ctx context.Context, ruleGroupName string, token string, id string) error { + _, err := w.client.UpdateRuleGroup(ctx, &wafv2.UpdateRuleGroupInput{ + Name: aws.String(ruleGroupName), + Scope: wafv2types.Scope(w.config.Scope), + LockToken: aws.String(token), + Id: aws.String(id), + Rules: []wafv2types.Rule{}, + VisibilityConfig: &wafv2types.VisibilityConfig{ + SampledRequestsEnabled: false, + CloudWatchMetricsEnabled: false, + MetricName: aws.String(ruleGroupName), + }, + }) + + return err +} + func (w *WAF) ListWebACL(ctx context.Context) (map[string]Acl, error) { acls := make(map[string]Acl) @@ -416,9 +433,11 @@ func (w *WAF) GetRuleGroup(ctx context.Context, ruleGroupname string) (string, w } func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *string, allSets bool) error { - err := w.RemoveRuleGroupFromACL(ctx, acl, token) - if err != nil { - return fmt.Errorf("error removing rule group from ACL: %w", err) + if !w.config.DelegateAclManagement { + err := w.RemoveRuleGroupFromACL(ctx, acl, token) + if err != nil { + return fmt.Errorf("error removing rule group from ACL: %w", err) + } } if _, ok := w.ruleGroupsInfos[w.config.RuleGroupName]; ok { @@ -429,9 +448,16 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str w.Logger.Debugf("Deleting RuleGroup %s", w.config.RuleGroupName) - err = w.DeleteRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) - if err != nil { - return fmt.Errorf("failed to delete RuleGroup %s: %w", w.config.RuleGroupName, err) + if !w.config.DelegateAclManagement { + err = w.DeleteRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) + if err != nil { + return fmt.Errorf("failed to delete RuleGroup %s: %w", w.config.RuleGroupName, err) + } + } else { + err = w.UnassignRulesFromRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) + if err != nil { + return fmt.Errorf("failed to unassign Rules from RuleGroup %s: %w", w.config.RuleGroupName, err) + } } } else { log.Debugf("RuleGroup %s not found, nothing to do", w.config.RuleGroupName) @@ -526,24 +552,26 @@ func (w *WAF) Init(ctx context.Context) error { return fmt.Errorf("failed to list resources: %w", err) } - err = w.CreateRuleGroup(ctx, w.config.RuleGroupName) + if !w.config.DelegateAclManagement { + err = w.CreateRuleGroup(ctx, w.config.RuleGroupName) - if err != nil { - return fmt.Errorf("failed to create RuleGroup %s: %w", w.config.RuleGroupName, err) - } + if err != nil { + return fmt.Errorf("failed to create RuleGroup %s: %w", w.config.RuleGroupName, err) + } - w.Logger.Infof("RuleGroup %s created", w.config.RuleGroupName) + w.Logger.Infof("RuleGroup %s created", w.config.RuleGroupName) - acl, lockTocken, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) + acl, lockTocken, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) - if err != nil { - return fmt.Errorf("failed to get WebACL %s: %w", w.config.WebACLName, err) - } + if err != nil { + return fmt.Errorf("failed to get WebACL %s: %w", w.config.WebACLName, err) + } - err = w.AddRuleGroupToACL(ctx, acl, lockTocken) + err = w.AddRuleGroupToACL(ctx, acl, lockTocken) - if err != nil { - return fmt.Errorf("failed to add RuleGroup %s to WebACL %s: %w", w.config.RuleGroupName, w.config.WebACLName, err) + if err != nil { + return fmt.Errorf("failed to add RuleGroup %s to WebACL %s: %w", w.config.RuleGroupName, w.config.WebACLName, err) + } } return nil From 851f2f64d8b6d084fbb3cf53db5729032bcea7cc Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 11:04:22 +0200 Subject: [PATCH 2/8] chore: move var name to use_existing_rule_group --- pkg/cfg/config.go | 8 ++++---- pkg/waf/waf.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/cfg/config.go b/pkg/cfg/config.go index 83dd63d..00e969f 100644 --- a/pkg/cfg/config.go +++ b/pkg/cfg/config.go @@ -45,7 +45,7 @@ type AclConfig struct { CloudWatchMetricName string `yaml:"cloudwatch_metric_name"` SampleRequests bool `yaml:"sample_requests"` CleanOnStart bool `yaml:"remove_sets_on_start"` - DelegateAclManagement bool `yaml:"delegate_acl_management"` + UseExistingRuleGroup bool `yaml:"use_existing_rule_group"` } var ValidActions = []string{"ban", "captcha", "count"} @@ -131,11 +131,11 @@ func getConfigFromEnv(config *bouncerConfig) { log.Warnf("Invalid value for %s: %s, defaulting to false", key, value) acl.CleanOnStart = false } - case "DELEGATE_ACL_MANAGEMENT": - acl.DelegateAclManagement, err = strconv.ParseBool(value) + case "USE_EXISTING_RULE_GROUP": + acl.UseExistingRuleGroup, err = strconv.ParseBool(value) if err != nil { log.Warnf("Invalid value for %s: %s, defaulting to false", key, value) - acl.DelegateAclManagement = false + acl.UseExistingRuleGroup = false } } } else { diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index ef29580..9dee349 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -433,7 +433,7 @@ func (w *WAF) GetRuleGroup(ctx context.Context, ruleGroupname string) (string, w } func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *string, allSets bool) error { - if !w.config.DelegateAclManagement { + if !w.config.UseExistingRuleGroup { err := w.RemoveRuleGroupFromACL(ctx, acl, token) if err != nil { return fmt.Errorf("error removing rule group from ACL: %w", err) @@ -448,7 +448,7 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str w.Logger.Debugf("Deleting RuleGroup %s", w.config.RuleGroupName) - if !w.config.DelegateAclManagement { + if !w.config.UseExistingRuleGroup { err = w.DeleteRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) if err != nil { return fmt.Errorf("failed to delete RuleGroup %s: %w", w.config.RuleGroupName, err) @@ -552,7 +552,7 @@ func (w *WAF) Init(ctx context.Context) error { return fmt.Errorf("failed to list resources: %w", err) } - if !w.config.DelegateAclManagement { + if !w.config.UseExistingRuleGroup { err = w.CreateRuleGroup(ctx, w.config.RuleGroupName) if err != nil { From fddad5ae7eec463b57a13db1080d321a7975c3e1 Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 11:06:57 +0200 Subject: [PATCH 3/8] fix: add specific logger when cleaning rule group --- pkg/waf/waf.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index 9dee349..75486f6 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -446,14 +446,16 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str return fmt.Errorf("failed to get RuleGroup %s: %w", w.config.RuleGroupName, err) } - w.Logger.Debugf("Deleting RuleGroup %s", w.config.RuleGroupName) - if !w.config.UseExistingRuleGroup { + w.Logger.Debugf("Deleting RuleGroup %s", w.config.RuleGroupName) + err = w.DeleteRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) if err != nil { return fmt.Errorf("failed to delete RuleGroup %s: %w", w.config.RuleGroupName, err) } } else { + w.Logger.Debugf("Cleaning RuleGroup %s", w.config.RuleGroupName) + err = w.UnassignRulesFromRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) if err != nil { return fmt.Errorf("failed to unassign Rules from RuleGroup %s: %w", w.config.RuleGroupName, err) From a769b5943b3acdedd42a35a62dba6641179f888f Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 11:13:16 +0200 Subject: [PATCH 4/8] chore: add check on rulegroup when using existing rulegroup --- pkg/waf/waf.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index 75486f6..c710811 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -574,6 +574,12 @@ func (w *WAF) Init(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to add RuleGroup %s to WebACL %s: %w", w.config.RuleGroupName, w.config.WebACLName, err) } + } else { + err = w.GetRuleGroup(ctx, w.config.RuleGroupName) + + if err != nil { + return fmt.Errorf("failed to get RuleGroup %s: %w", w.config.RuleGroupName, err) + } } return nil From 72f0b89e58f2d04cc09474b4c66e9c3dd51b18a7 Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 11:25:08 +0200 Subject: [PATCH 5/8] chore: make acl optionnal when using existing rulegroup --- pkg/waf/waf.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index c710811..75fbbca 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -484,9 +484,11 @@ func (w *WAF) Cleanup(ctx context.Context) error { return fmt.Errorf("failed to list WAF resources: %w", err) } - acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) - if err != nil { - return fmt.Errorf("failed to get WebACL: %w", err) + if !w.config.DelegateAclManagement { + acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) + if err != nil { + return fmt.Errorf("failed to get WebACL: %w", err) + } } return w.CleanupAcl(ctx, acl, token, false) @@ -495,9 +497,11 @@ func (w *WAF) Cleanup(ctx context.Context) error { func (w *WAF) ListResources(ctx context.Context) (map[string]Acl, map[string]IpSet, map[string]RuleGroup, error) { var err error - acls, err := w.ListWebACL(ctx) - if err != nil { - return nil, nil, nil, err + if !w.config.DelegateAclManagement { + acls, err := w.ListWebACL(ctx) + if err != nil { + return nil, nil, nil, err + } } sets, err := w.ListIpSet(ctx) @@ -521,8 +525,10 @@ func (w *WAF) Init(ctx context.Context) error { return fmt.Errorf("failed to list resources: %w", err) } - w.Logger.Tracef("Found %d WebACLs", len(w.aclsInfo)) - w.Logger.Tracef("ACLs: %+v", w.aclsInfo) + if !w.config.UseExistingRuleGroup { + w.Logger.Tracef("Found %d WebACLs", len(w.aclsInfo)) + w.Logger.Tracef("ACLs: %+v", w.aclsInfo) + } w.Logger.Tracef("Found %d IPSets", len(w.setsInfos)) w.Logger.Tracef("IPSets: %+v", w.setsInfos) @@ -530,14 +536,16 @@ func (w *WAF) Init(ctx context.Context) error { w.Logger.Tracef("Found %d RuleGroups", len(w.ruleGroupsInfos)) w.Logger.Tracef("RuleGroups: %+v", w.ruleGroupsInfos) - if _, ok := w.aclsInfo[w.config.WebACLName]; !ok { - return fmt.Errorf("WebACL %s does not exist in region %s", w.config.WebACLName, w.config.Region) - } + if !w.config.UseExistingRuleGroup { + if _, ok := w.aclsInfo[w.config.WebACLName]; !ok { + return fmt.Errorf("WebACL %s does not exist in region %s", w.config.WebACLName, w.config.Region) + } - acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) + acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) - if err != nil { - return fmt.Errorf("failed to get WebACL: %w", err) + if err != nil { + return fmt.Errorf("failed to get WebACL: %w", err) + } } w.ipsetManager = NewIPSetManager(w.config.IpsetPrefix, w.config.Scope, w.client, w.Logger) From 5f56cbaefd359a1ae5f364a220a66c5c13d906df Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 11:30:45 +0200 Subject: [PATCH 6/8] fix: fix issues --- pkg/waf/waf.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index 75fbbca..00685ba 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -475,6 +475,8 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str func (w *WAF) Cleanup(ctx context.Context) error { var err error + var acl *wafv2types.WebACL + var token *string w.lock.Lock() defer w.lock.Unlock() @@ -484,8 +486,8 @@ func (w *WAF) Cleanup(ctx context.Context) error { return fmt.Errorf("failed to list WAF resources: %w", err) } - if !w.config.DelegateAclManagement { - acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) + if !w.config.UseExistingRuleGroup { + acl, token, err = w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) if err != nil { return fmt.Errorf("failed to get WebACL: %w", err) } @@ -496,9 +498,10 @@ func (w *WAF) Cleanup(ctx context.Context) error { func (w *WAF) ListResources(ctx context.Context) (map[string]Acl, map[string]IpSet, map[string]RuleGroup, error) { var err error + var acls map[string]Acl - if !w.config.DelegateAclManagement { - acls, err := w.ListWebACL(ctx) + if !w.config.UseExistingRuleGroup { + acls, err = w.ListWebACL(ctx) if err != nil { return nil, nil, nil, err } @@ -519,6 +522,9 @@ func (w *WAF) ListResources(ctx context.Context) (map[string]Acl, map[string]IpS func (w *WAF) Init(ctx context.Context) error { var err error + var acl *wafv2types.WebACL + var token *string + w.aclsInfo, w.setsInfos, w.ruleGroupsInfos, err = w.ListResources(ctx) if err != nil { @@ -541,7 +547,7 @@ func (w *WAF) Init(ctx context.Context) error { return fmt.Errorf("WebACL %s does not exist in region %s", w.config.WebACLName, w.config.Region) } - acl, token, err := w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) + acl, token, err = w.GetWebACL(ctx, w.config.WebACLName, w.aclsInfo[w.config.WebACLName].Id) if err != nil { return fmt.Errorf("failed to get WebACL: %w", err) @@ -583,7 +589,7 @@ func (w *WAF) Init(ctx context.Context) error { return fmt.Errorf("failed to add RuleGroup %s to WebACL %s: %w", w.config.RuleGroupName, w.config.WebACLName, err) } } else { - err = w.GetRuleGroup(ctx, w.config.RuleGroupName) + _, _, err = w.GetRuleGroup(ctx, w.config.RuleGroupName) if err != nil { return fmt.Errorf("failed to get RuleGroup %s: %w", w.config.RuleGroupName, err) From 49a99b156589718e111e47eec31bf5c6529b04aa Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Tue, 7 Apr 2026 15:07:38 +0200 Subject: [PATCH 7/8] chore: edit visibility config management when unassigning rules from rulegroup --- pkg/waf/waf.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index 00685ba..d0d14b9 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -221,18 +221,14 @@ func (w *WAF) DeleteRuleGroup(ctx context.Context, ruleGroupName string, token s return err } -func (w *WAF) UnassignRulesFromRuleGroup(ctx context.Context, ruleGroupName string, token string, id string) error { +func (w *WAF) UnassignRulesFromRuleGroup(ctx context.Context, ruleGroup wafv2types.RuleGroup, token string) error { _, err := w.client.UpdateRuleGroup(ctx, &wafv2.UpdateRuleGroupInput{ - Name: aws.String(ruleGroupName), - Scope: wafv2types.Scope(w.config.Scope), - LockToken: aws.String(token), - Id: aws.String(id), - Rules: []wafv2types.Rule{}, - VisibilityConfig: &wafv2types.VisibilityConfig{ - SampledRequestsEnabled: false, - CloudWatchMetricsEnabled: false, - MetricName: aws.String(ruleGroupName), - }, + Name: ruleGroup.Name, + Id: ruleGroup.Id, + Scope: wafv2types.Scope(w.config.Scope), + LockToken: aws.String(token), + Rules: []wafv2types.Rule{}, + VisibilityConfig: ruleGroup.VisibilityConfig, }) return err @@ -441,7 +437,7 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str } if _, ok := w.ruleGroupsInfos[w.config.RuleGroupName]; ok { - token, _, err := w.GetRuleGroup(ctx, w.config.RuleGroupName) + token, rg, err := w.GetRuleGroup(ctx, w.config.RuleGroupName) if err != nil { return fmt.Errorf("failed to get RuleGroup %s: %w", w.config.RuleGroupName, err) } @@ -456,7 +452,7 @@ func (w *WAF) CleanupAcl(ctx context.Context, acl *wafv2types.WebACL, token *str } else { w.Logger.Debugf("Cleaning RuleGroup %s", w.config.RuleGroupName) - err = w.UnassignRulesFromRuleGroup(ctx, w.config.RuleGroupName, token, w.ruleGroupsInfos[w.config.RuleGroupName].Id) + err = w.UnassignRulesFromRuleGroup(ctx, rg, token) if err != nil { return fmt.Errorf("failed to unassign Rules from RuleGroup %s: %w", w.config.RuleGroupName, err) } From 746936d01d7c70e7b5d791eb493094bc0086ac6c Mon Sep 17 00:00:00 2001 From: Arthur Garnotel Date: Mon, 13 Apr 2026 10:24:49 +0200 Subject: [PATCH 8/8] chore: fix indent for linter --- pkg/waf/waf.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/waf/waf.go b/pkg/waf/waf.go index d0d14b9..9d43d6f 100644 --- a/pkg/waf/waf.go +++ b/pkg/waf/waf.go @@ -224,11 +224,11 @@ func (w *WAF) DeleteRuleGroup(ctx context.Context, ruleGroupName string, token s func (w *WAF) UnassignRulesFromRuleGroup(ctx context.Context, ruleGroup wafv2types.RuleGroup, token string) error { _, err := w.client.UpdateRuleGroup(ctx, &wafv2.UpdateRuleGroupInput{ Name: ruleGroup.Name, - Id: ruleGroup.Id, - Scope: wafv2types.Scope(w.config.Scope), - LockToken: aws.String(token), - Rules: []wafv2types.Rule{}, - VisibilityConfig: ruleGroup.VisibilityConfig, + Id: ruleGroup.Id, + Scope: wafv2types.Scope(w.config.Scope), + LockToken: aws.String(token), + Rules: []wafv2types.Rule{}, + VisibilityConfig: ruleGroup.VisibilityConfig, }) return err