Skip to content

Commit

Permalink
manage force_pull message for one blocklist (#2615)
Browse files Browse the repository at this point in the history
* manage force_pull message for one blocklist

* fix info message on force pull blocklist
  • Loading branch information
nitescuc authored Nov 29, 2023
1 parent 6b0bdc5 commit 7c5cbef
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 10 deletions.
29 changes: 22 additions & 7 deletions pkg/apiserver/apic.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,23 @@ func (a *apic) PullTop(forcePull bool) error {
}

// update blocklists
if err := a.UpdateBlocklists(data.Links, add_counters); err != nil {
if err := a.UpdateBlocklists(data.Links, add_counters, forcePull); err != nil {
return fmt.Errorf("while updating blocklists: %w", err)
}
return nil
}

// we receive a link to a blocklist, we pull the content of the blocklist and we create one alert
func (a *apic) PullBlocklist(blocklist *modelscapi.BlocklistLink, forcePull bool) error {
add_counters, _ := makeAddAndDeleteCounters()
if err := a.UpdateBlocklists(&modelscapi.GetDecisionsStreamResponseLinks{
Blocklists: []*modelscapi.BlocklistLink{blocklist},
}, add_counters, forcePull); err != nil {
return fmt.Errorf("while pulling blocklist: %w", err)
}
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 {
Expand Down Expand Up @@ -710,7 +721,7 @@ 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 {
func (a *apic) updateBlocklist(client *apiclient.ApiClient, blocklist *modelscapi.BlocklistLink, add_counters map[string]map[string]int, forcePull bool) error {
if blocklist.Scope == nil {
log.Warningf("blocklist has no scope")
return nil
Expand All @@ -719,12 +730,16 @@ func (a *apic) updateBlocklist(client *apiclient.ApiClient, blocklist *modelscap
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)
if !forcePull {
_forcePull, err := a.ShouldForcePullBlocklist(blocklist)
if err != nil {
return fmt.Errorf("while checking if we should force pull blocklist %s: %w", *blocklist.Name, err)
}
forcePull = _forcePull
}
blocklistConfigItemName := fmt.Sprintf("blocklist:%s:last_pull", *blocklist.Name)
var lastPullTimestamp *string
var err error
if !forcePull {
lastPullTimestamp, err = a.dbClient.GetConfigItem(blocklistConfigItemName)
if err != nil {
Expand Down Expand Up @@ -764,7 +779,7 @@ func (a *apic) updateBlocklist(client *apiclient.ApiClient, blocklist *modelscap
return nil
}

func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLinks, add_counters map[string]map[string]int) error {
func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLinks, add_counters map[string]map[string]int, forcePull bool) error {
if links == nil {
return nil
}
Expand All @@ -778,7 +793,7 @@ func (a *apic) UpdateBlocklists(links *modelscapi.GetDecisionsStreamResponseLink
return fmt.Errorf("while creating default client: %w", err)
}
for _, blocklist := range links.Blocklists {
if err := a.updateBlocklist(defaultClient, blocklist, add_counters); err != nil {
if err := a.updateBlocklist(defaultClient, blocklist, add_counters, forcePull); err != nil {
return err
}
}
Expand Down
31 changes: 31 additions & 0 deletions pkg/apiserver/apic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,37 @@ func TestAPICPullTopBLCacheForceCall(t *testing.T) {
require.NoError(t, err)
}

func TestAPICPullBlocklistCall(t *testing.T) {
api := getAPIC(t)
httpmock.Activate()
defer httpmock.DeactivateAndReset()

httpmock.RegisterResponder("GET", "http://api.crowdsec.net/blocklist1", func(req *http.Request) (*http.Response, error) {
assert.Equal(t, "", req.Header.Get("If-Modified-Since"))
return httpmock.NewStringResponse(200, "1.2.3.4"), nil
})
url, err := url.ParseRequestURI("http://api.crowdsec.net/")
require.NoError(t, err)

apic, err := apiclient.NewDefaultClient(
url,
"/api",
fmt.Sprintf("crowdsec/%s", version.String()),
nil,
)
require.NoError(t, err)

api.apiClient = apic
err = api.PullBlocklist(&modelscapi.BlocklistLink{
URL: ptr.Of("http://api.crowdsec.net/blocklist1"),
Name: ptr.Of("blocklist1"),
Scope: ptr.Of("Ip"),
Remediation: ptr.Of("ban"),
Duration: ptr.Of("24h"),
}, true)
require.NoError(t, err)
}

func TestAPICPush(t *testing.T) {
tests := []struct {
name string
Expand Down
48 changes: 45 additions & 3 deletions pkg/apiserver/papi_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/crowdsecurity/crowdsec/pkg/apiclient"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/crowdsec/pkg/modelscapi"
"github.com/crowdsecurity/crowdsec/pkg/types"
)

Expand All @@ -19,6 +20,23 @@ type deleteDecisions struct {
Decisions []string `json:"decisions"`
}

type blocklistLink struct {
// blocklist name
Name string `json:"name"`
// blocklist url
Url string `json:"url"`
// blocklist remediation
Remediation string `json:"remediation"`
// blocklist scope
Scope string `json:"scope,omitempty"`
// blocklist duration
Duration string `json:"duration,omitempty"`
}

type forcePull struct {
Blocklist *blocklistLink `json:"blocklist,omitempty"`
}

func DecisionCmd(message *Message, p *Papi, sync bool) error {
switch message.Header.OperationCmd {
case "delete":
Expand Down Expand Up @@ -144,11 +162,35 @@ func ManagementCmd(message *Message, p *Papi, sync bool) error {
log.Infof("Received reauth command from PAPI, resetting token")
p.apiClient.GetClient().Transport.(*apiclient.JWTTransport).ResetToken()
case "force_pull":
log.Infof("Received force_pull command from PAPI, pulling community and 3rd-party blocklists")
err := p.apic.PullTop(true)
data, err := json.Marshal(message.Data)
if err != nil {
return fmt.Errorf("failed to force pull operation: %s", err)
return err
}
forcePullMsg := forcePull{}
if err := json.Unmarshal(data, &forcePullMsg); err != nil {
return fmt.Errorf("message for '%s' contains bad data format: %s", message.Header.OperationType, err)
}

if forcePullMsg.Blocklist == nil {
log.Infof("Received force_pull command from PAPI, pulling community and 3rd-party blocklists")
err = p.apic.PullTop(true)
if err != nil {
return fmt.Errorf("failed to force pull operation: %s", err)
}
} else {
log.Infof("Received force_pull command from PAPI, pulling blocklist %s", forcePullMsg.Blocklist.Name)
err = p.apic.PullBlocklist(&modelscapi.BlocklistLink{
Name: &forcePullMsg.Blocklist.Name,
URL: &forcePullMsg.Blocklist.Url,
Remediation: &forcePullMsg.Blocklist.Remediation,
Scope: &forcePullMsg.Blocklist.Scope,
Duration: &forcePullMsg.Blocklist.Duration,
}, true)
if err != nil {
return fmt.Errorf("failed to force pull operation: %s", err)
}
}

default:
return fmt.Errorf("unknown command '%s' for operation type '%s'", message.Header.OperationCmd, message.Header.OperationType)
}
Expand Down

0 comments on commit 7c5cbef

Please sign in to comment.