diff --git a/pkg/apiserver/apic.go b/pkg/apiserver/apic.go index ff45e2a8b..36ac81303 100644 --- a/pkg/apiserver/apic.go +++ b/pkg/apiserver/apic.go @@ -620,59 +620,57 @@ func (a *apic) PullTop(forcePull bool) error { return nil } +// if decisions is whitelisted: return representation of the whitelist ip or cidr +// if not whitelisted: empty string +func (a *apic) whitelistedBy(decision *models.Decision) string { + if decision.Value == nil { + return "" + } + ipval := net.ParseIP(*decision.Value) + for _, cidr := range a.whitelists.Cidrs { + if cidr.Contains(ipval) { + return cidr.String() + } + } + for _, ip := range a.whitelists.Ips { + if ip != nil && ip.Equal(ipval) { + return ip.String() + } + } + return "" +} + func (a *apic) ApplyApicWhitelists(decisions []*models.Decision) []*models.Decision { - if a.whitelists == nil { + if a.whitelists == nil || len(a.whitelists.Cidrs) == 0 && len(a.whitelists.Ips) == 0 { return decisions } //deal with CAPI whitelists for fire. We want to avoid having a second list, so we shrink in place outIdx := 0 for _, decision := range decisions { - if decision.Value == nil { + whitelister := a.whitelistedBy(decision) + if whitelister != "" { + log.Infof("%s from %s is whitelisted by %s", *decision.Value, *decision.Scenario, whitelister) continue } - skip := false - ipval := net.ParseIP(*decision.Value) - for _, cidr := range a.whitelists.Cidrs { - if skip { - break - } - if cidr.Contains(ipval) { - log.Infof("%s from %s is whitelisted by %s", *decision.Value, *decision.Scenario, cidr.String()) - skip = true - } - } - for _, ip := range a.whitelists.Ips { - if skip { - break - } - if ip != nil && ip.Equal(ipval) { - log.Infof("%s from %s is whitelisted by %s", *decision.Value, *decision.Scenario, ip.String()) - skip = true - } - } - if !skip { - decisions[outIdx] = decision - outIdx++ - } - + decisions[outIdx] = decision + outIdx++ } //shrink the list, those are deleted items - decisions = decisions[:outIdx] - return decisions + return decisions[:outIdx] } func (a *apic) SaveAlerts(alertsFromCapi []*models.Alert, add_counters map[string]map[string]int, delete_counters map[string]map[string]int) error { - for idx, alert := range alertsFromCapi { - alertsFromCapi[idx] = setAlertScenario(add_counters, delete_counters, alert) - log.Debugf("%s has %d decisions", *alertsFromCapi[idx].Source.Scope, len(alertsFromCapi[idx].Decisions)) + for _, alert := range alertsFromCapi { + setAlertScenario(alert, add_counters, delete_counters) + log.Debugf("%s has %d decisions", *alert.Source.Scope, len(alert.Decisions)) if a.dbClient.Type == "sqlite" && (a.dbClient.WalMode == nil || !*a.dbClient.WalMode) { log.Warningf("sqlite is not using WAL mode, LAPI might become unresponsive when inserting the community blocklist") } - alertID, inserted, deleted, err := a.dbClient.UpdateCommunityBlocklist(alertsFromCapi[idx]) + alertID, inserted, deleted, err := a.dbClient.UpdateCommunityBlocklist(alert) if err != nil { - return fmt.Errorf("while saving alert from %s: %w", *alertsFromCapi[idx].Source.Scope, err) + return fmt.Errorf("while saving alert from %s: %w", *alert.Source.Scope, err) } - log.Printf("%s : added %d entries, deleted %d entries (alert:%d)", *alertsFromCapi[idx].Source.Scope, inserted, deleted, alertID) + log.Printf("%s : added %d entries, deleted %d entries (alert:%d)", *alert.Source.Scope, inserted, deleted, alertID) } return nil @@ -708,6 +706,60 @@ func (a *apic) ShouldForcePullBlocklist(blocklist *modelscapi.BlocklistLink) (bo return false, nil } +func (a *apic) updateBlocklist(client *apiclient.ApiClient, blocklist *modelscapi.BlocklistLink, add_counters map[string]map[string]int) error { + if blocklist.Scope == nil { + log.Warningf("blocklist has no scope") + return nil + } + if blocklist.Duration == nil { + log.Warningf("blocklist has no duration") + return nil + } + forcePull, err := a.ShouldForcePullBlocklist(blocklist) + if err != nil { + return fmt.Errorf("while checking if we should force pull blocklist %s: %w", *blocklist.Name, err) + } + blocklistConfigItemName := fmt.Sprintf("blocklist:%s:last_pull", *blocklist.Name) + var lastPullTimestamp *string + if !forcePull { + lastPullTimestamp, err = a.dbClient.GetConfigItem(blocklistConfigItemName) + if err != nil { + return fmt.Errorf("while getting last pull timestamp for blocklist %s: %w", *blocklist.Name, err) + } + } + decisions, hasChanged, err := client.Decisions.GetDecisionsFromBlocklist(context.Background(), blocklist, lastPullTimestamp) + if err != nil { + return fmt.Errorf("while getting decisions from blocklist %s: %w", *blocklist.Name, err) + } + if !hasChanged { + if lastPullTimestamp == nil { + log.Infof("blocklist %s hasn't been modified or there was an error reading it, skipping", *blocklist.Name) + } else { + log.Infof("blocklist %s hasn't been modified since %s, skipping", *blocklist.Name, *lastPullTimestamp) + } + return nil + } + err = a.dbClient.SetConfigItem(blocklistConfigItemName, time.Now().UTC().Format(http.TimeFormat)) + if err != nil { + return fmt.Errorf("while setting last pull timestamp for blocklist %s: %w", *blocklist.Name, err) + } + if len(decisions) == 0 { + log.Infof("blocklist %s has no decisions", *blocklist.Name) + return nil + } + //apply APIC specific whitelists + decisions = a.ApplyApicWhitelists(decisions) + alert := createAlertForDecision(decisions[0]) + alertsFromCapi := []*models.Alert{alert} + alertsFromCapi = fillAlertsWithDecisions(alertsFromCapi, decisions, add_counters) + + err = a.SaveAlerts(alertsFromCapi, add_counters, nil) + if err != nil { + return fmt.Errorf("while saving alert from blocklist %s: %w", *blocklist.Name, err) + } + return nil +} + func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLinks, add_counters map[string]map[string]int) error { if links == nil { return nil @@ -722,61 +774,14 @@ func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLink return fmt.Errorf("while creating default client: %w", err) } for _, blocklist := range links.Blocklists { - if blocklist.Scope == nil { - log.Warningf("blocklist has no scope") - continue - } - if blocklist.Duration == nil { - log.Warningf("blocklist has no duration") - continue - } - forcePull, err := a.ShouldForcePullBlocklist(blocklist) - if err != nil { - return fmt.Errorf("while checking if we should force pull blocklist %s: %w", *blocklist.Name, err) - } - blocklistConfigItemName := fmt.Sprintf("blocklist:%s:last_pull", *blocklist.Name) - var lastPullTimestamp *string - if !forcePull { - lastPullTimestamp, err = a.dbClient.GetConfigItem(blocklistConfigItemName) - if err != nil { - return fmt.Errorf("while getting last pull timestamp for blocklist %s: %w", *blocklist.Name, err) - } - } - decisions, has_changed, err := defaultClient.Decisions.GetDecisionsFromBlocklist(context.Background(), blocklist, lastPullTimestamp) - if err != nil { - return fmt.Errorf("while getting decisions from blocklist %s: %w", *blocklist.Name, err) - } - if !has_changed { - if lastPullTimestamp == nil { - log.Infof("blocklist %s hasn't been modified or there was an error reading it, skipping", *blocklist.Name) - } else { - log.Infof("blocklist %s hasn't been modified since %s, skipping", *blocklist.Name, *lastPullTimestamp) - } - continue - } - err = a.dbClient.SetConfigItem(blocklistConfigItemName, time.Now().UTC().Format(http.TimeFormat)) - if err != nil { - return fmt.Errorf("while setting last pull timestamp for blocklist %s: %w", *blocklist.Name, err) - } - if len(decisions) == 0 { - log.Infof("blocklist %s has no decisions", *blocklist.Name) - continue - } - //apply APIC specific whitelists - decisions = a.ApplyApicWhitelists(decisions) - alert := createAlertForDecision(decisions[0]) - alertsFromCapi := []*models.Alert{alert} - alertsFromCapi = fillAlertsWithDecisions(alertsFromCapi, decisions, add_counters) - - err = a.SaveAlerts(alertsFromCapi, add_counters, nil) - if err != nil { - return fmt.Errorf("while saving alert from blocklist %s: %w", *blocklist.Name, err) - } + if err := a.updateBlocklist(defaultClient, blocklist, add_counters); err != nil { + return err + } } return nil } -func setAlertScenario(add_counters map[string]map[string]int, delete_counters map[string]map[string]int, alert *models.Alert) *models.Alert { +func setAlertScenario(alert *models.Alert, add_counters map[string]map[string]int, delete_counters map[string]map[string]int) { if *alert.Source.Scope == types.CAPIOrigin { *alert.Source.Scope = SCOPE_CAPI_ALIAS_ALIAS alert.Scenario = ptr.Of(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.CAPIOrigin]["all"], delete_counters[types.CAPIOrigin]["all"])) @@ -784,7 +789,6 @@ func setAlertScenario(add_counters map[string]map[string]int, delete_counters ma *alert.Source.Scope = fmt.Sprintf("%s:%s", types.ListOrigin, *alert.Scenario) alert.Scenario = ptr.Of(fmt.Sprintf("update : +%d/-%d IPs", add_counters[types.ListOrigin][*alert.Scenario], delete_counters[types.ListOrigin][*alert.Scenario])) } - return alert } func (a *apic) Pull() error { diff --git a/pkg/csconfig/api.go b/pkg/csconfig/api.go index f8447a137..c07d9be56 100644 --- a/pkg/csconfig/api.go +++ b/pkg/csconfig/api.go @@ -344,13 +344,16 @@ func (s *LocalApiServerCfg) LoadCapiWhitelists() error { } var fromCfg capiWhitelists - s.CapiWhitelists = &CapiWhitelist{} defer fd.Close() decoder := yaml.NewDecoder(fd) if err := decoder.Decode(&fromCfg); err != nil { return fmt.Errorf("while parsing capi whitelist file '%s': %s", 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 {