Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PagerDuty Events v2 API #200

Merged
merged 2 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,8 @@ kafka:
# minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default)

pagerduty:
# apikey: # Pagerduty API Key, if not empty, Pagerduty output is enabled
service: "" # Service to create an incident (mandatory)
assignee: "" # A list of comma separated users to assign. Cannot be provided if pagerduty.escalationpolicy is already specified.
escalationpolicy: "" # Escalation policy to assign. Cannot be provided if pagerduty.escalationpolicy is already specified
# minimumpriority: "debug" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default)
routingKey: "" # Pagerduty Routing Key, if not empty, Pagerduty output is enabled
minimumpriority: "" # minimum priority of event for using this output, order is emergency|alert|critical|error|warning|notice|informational|debug or "" (default)

kubeless:
function: "" # Name of Kubeless function, if not empty, Kubeless is enabled
Expand Down
5 changes: 1 addition & 4 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,7 @@ func getConfig() *types.Configuration {
v.SetDefault("Kafka.Topic", "")
v.SetDefault("Kafka.Partition", 0)
v.SetDefault("Kafka.MinimumPriority", "")
v.SetDefault("Pagerduty.APIKey", "")
v.SetDefault("Pagerduty.Service", "")
v.SetDefault("Pagerduty.Assignee", []string{})
v.SetDefault("Pagerduty.EscalationPolicy", "")
v.SetDefault("Pagerduty.RoutingKey", "")
v.SetDefault("Pagerduty.MinimumPriority", "")
v.SetDefault("Kubeless.Namespace", "")
v.SetDefault("Kubeless.Function", "")
Expand Down
4 changes: 2 additions & 2 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ func forwardEvent(falcopayload types.FalcoPayload) {
go kafkaClient.KafkaProduce(falcopayload)
}

if config.Pagerduty.APIKey != "" && config.Pagerduty.Service != "" && (falcopayload.Priority >= types.Priority(config.Pagerduty.MinimumPriority) || falcopayload.Rule == testRule) {
go pagerdutyClient.PagerdutyCreateIncident(falcopayload)
if config.Pagerduty.RoutingKey != "" && (falcopayload.Priority >= types.Priority(config.Pagerduty.MinimumPriority) || falcopayload.Rule == testRule) {
go pagerdutyClient.PagerdutyPost(falcopayload)
}

if config.Kubeless.Namespace != "" && config.Kubeless.Function != "" && (falcopayload.Priority >= types.Priority(config.Kubeless.MinimumPriority) || falcopayload.Rule == testRule) {
Expand Down
13 changes: 8 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,17 @@ func init() {
}
}

if config.Pagerduty.APIKey != "" && config.Pagerduty.Service != "" {
if config.Pagerduty.RoutingKey != "" {
var err error
pagerdutyClient, err = outputs.NewPagerdutyClient(config, stats, promStats, statsdClient, dogstatsdClient)
var url = "https://events.pagerduty.com/v2/enqueue"
var outputName = "Pagerduty"

pagerdutyClient, err = outputs.NewClient(outputName, url, config, stats, promStats, statsdClient, dogstatsdClient)

if err != nil {
config.Pagerduty.APIKey = ""
config.Pagerduty.Service = ""
config.Pagerduty.RoutingKey = ""
} else {
outputs.EnabledOutputs = append(outputs.EnabledOutputs, "Pagerduty")
outputs.EnabledOutputs = append(outputs.EnabledOutputs, outputName)
}
}

Expand Down
2 changes: 0 additions & 2 deletions outputs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

"cloud.google.com/go/pubsub"
"github.com/DataDog/datadog-go/statsd"
"github.com/PagerDuty/go-pagerduty"
"github.com/aws/aws-sdk-go/aws/session"
cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/google/uuid"
Expand Down Expand Up @@ -61,7 +60,6 @@ type Client struct {
DogstatsdClient *statsd.Client
GCPTopicClient *pubsub.Topic
KafkaProducer *kafka.Conn
PagerdutyClient *pagerduty.Client
CloudEventsClient cloudevents.Client
KubernetesClient kubernetes.Interface
}
Expand Down
70 changes: 20 additions & 50 deletions outputs/pagerduty.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,20 @@
package outputs

import (
"fmt"
"log"
"time"

"github.com/DataDog/datadog-go/statsd"
"github.com/PagerDuty/go-pagerduty"
"github.com/falcosecurity/falcosidekick/types"
)

// NewPagerdutyClient returns a new output.Client for accessing the Pagerduty API.
func NewPagerdutyClient(config *types.Configuration, stats *types.Statistics, promStats *types.PromStatistics, statsdClient, dogstatsdClient *statsd.Client) (*Client, error) {
if len(config.Pagerduty.Assignee) > 0 && config.Pagerduty.EscalationPolicy != "" {
return nil, fmt.Errorf("assignee and escalation policy cannot be both configured")
}

return &Client{
OutputType: "PagerDuty",
Config: config,
Stats: stats,
PromStats: promStats,
StatsdClient: statsdClient,
DogstatsdClient: dogstatsdClient,
PagerdutyClient: pagerduty.NewClient(config.Pagerduty.APIKey),
}, nil
}

// PagerdutyCreateIncident posts incident to Pagerduty
func (c *Client) PagerdutyCreateIncident(falcopayload types.FalcoPayload) {
// PagerdutyPost posts alert event to Pagerduty
func (c *Client) PagerdutyPost(falcopayload types.FalcoPayload) {
c.Stats.Pagerduty.Add(Total, 1)

opts := &pagerduty.CreateIncidentOptions{
Type: "incident",
Title: falcopayload.Output,
Service: &pagerduty.APIReference{
ID: c.Config.Pagerduty.Service,
Type: "service_reference",
},
}

if len(c.Config.Pagerduty.Assignee) > 0 {
assignments := make([]pagerduty.Assignee, len(c.Config.Pagerduty.Assignee))
for i, a := range c.Config.Pagerduty.Assignee {
assignments[i] = pagerduty.Assignee{
Assignee: pagerduty.APIObject{
ID: a,
Type: "user_reference",
},
}
}
opts.Assignments = assignments
}

if policy := c.Config.Pagerduty.EscalationPolicy; policy != "" {
opts.EscalationPolicy = &pagerduty.APIReference{
ID: policy,
Type: "escalation_policy_reference",
}
}
event := createPagerdutyEvent(falcopayload, c.Config.Pagerduty)

if _, err := c.PagerdutyClient.CreateIncident("falcosidekick", opts); err != nil {
if _, err := pagerduty.ManageEvent(event); err != nil {
go c.CountMetric(Outputs, 1, []string{"output:pagerduty", "status:error"})
c.Stats.Pagerduty.Add(Error, 1)
c.PromStats.Outputs.With(map[string]string{"destination": "pagerduty", "status": Error}).Inc()
Expand All @@ -72,3 +27,18 @@ func (c *Client) PagerdutyCreateIncident(falcopayload types.FalcoPayload) {
c.PromStats.Outputs.With(map[string]string{"destination": "pagerduty", "status": OK}).Inc()
log.Printf("[INFO] : Pagerduty - Create Incident OK\n")
}

func createPagerdutyEvent(falcopayload types.FalcoPayload, config types.PagerdutyConfig) pagerduty.V2Event {
event := pagerduty.V2Event{
RoutingKey: config.RoutingKey,
Action: "trigger",
Payload: &pagerduty.V2Payload{
Source: "falco",
Summary: falcopayload.Output,
Severity: "critical",
Timestamp: falcopayload.Time.Format(time.RFC3339),
Details: falcopayload.OutputFields,
},
}
return event
}
38 changes: 38 additions & 0 deletions outputs/pagerduty_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package outputs

import (
"encoding/json"
"github.com/PagerDuty/go-pagerduty"
"github.com/falcosecurity/falcosidekick/types"
"github.com/stretchr/testify/require"
"testing"
)

func TestPagerdutyPayload(t *testing.T) {
var falcoTestInput = `{"output":"This is a test from falcosidekick","priority":"Debug","rule":"Test rule","time":"2001-01-01T01:10:00Z","output_fields": {"proc.name":"falcosidekick", "proc.tty": 1234}}`

var excpectedOutput = pagerduty.V2Event{
RoutingKey: "",
Action: "trigger",
Payload: &pagerduty.V2Payload{
Summary: "This is a test from falcosidekick",
Source: "falco",
Severity: "critical",
Timestamp: "2001-01-01T01:10:00Z",
Component: "",
Group: "",
Class: "",
Details: map[string]interface{}{
"proc.name": "falcosidekick",
"proc.tty": float64(1234),
},
},
}

var f types.FalcoPayload
json.Unmarshal([]byte(falcoTestInput), &f)

event := createPagerdutyEvent(f, types.PagerdutyConfig{})

require.Equal(t, excpectedOutput, event)
}
11 changes: 4 additions & 7 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type Configuration struct {
GCP gcpOutputConfig
Googlechat GooglechatConfig
Kafka kafkaConfig
Pagerduty pagerdutyConfig
Pagerduty PagerdutyConfig
Kubeless kubelessConfig
WebUI WebUIOutputConfig
}
Expand Down Expand Up @@ -261,12 +261,9 @@ type kafkaConfig struct {
MinimumPriority string
}

type pagerdutyConfig struct {
APIKey string
Service string
Assignee []string
EscalationPolicy string
MinimumPriority string
type PagerdutyConfig struct {
RoutingKey string
MinimumPriority string
}

type kubelessConfig struct {
Expand Down