Skip to content

Commit

Permalink
Telegram push target support
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Jogeleit <[email protected]>
  • Loading branch information
fjogeleit committed Sep 4, 2023
1 parent da46f7d commit c585a56
Show file tree
Hide file tree
Showing 7 changed files with 406 additions and 2 deletions.
12 changes: 12 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ type Webhook struct {
Channels []Webhook `mapstructure:"channels"`
}

// Telegram configuration
type Telegram struct {
TargetBaseOptions `mapstructure:",squash"`
Token string `mapstructure:"token"`
ChatID string `mapstructure:"chatID"`
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Headers map[string]string `mapstructure:"headers"`
Channels []Telegram `mapstructure:"channels"`
}

type AWSConfig struct {
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Expand Down Expand Up @@ -282,6 +293,7 @@ type Config struct {
GCS GCS `mapstructure:"gcs"`
UI UI `mapstructure:"ui"`
Webhook Webhook `mapstructure:"webhook"`
Telegram Telegram `mapstructure:"telegram"`
API API `mapstructure:"api"`
WorkerCount int `mapstructure:"worker"`
DBFile string `mapstructure:"dbfile"`
Expand Down
1 change: 1 addition & 0 deletions pkg/config/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ func (r *Resolver) TargetClients() []target.Client {
clients = append(clients, factory.SecurityHubs(r.config.SecurityHub)...)
clients = append(clients, factory.WebhookClients(r.config.Webhook)...)
clients = append(clients, factory.GCSClients(r.config.GCS)...)
clients = append(clients, factory.TelegramClients(r.config.Telegram)...)

if ui := factory.UIClient(r.config.UI); ui != nil {
clients = append(clients, ui)
Expand Down
13 changes: 11 additions & 2 deletions pkg/config/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,22 @@ var testConfig = &config.Config{
Encryption: "ssl/tls",
},
},
Telegram: config.Telegram{
Token: "XXX",
ChatID: "123456",
Channels: []config.Telegram{
{
ChatID: "1234567",
},
},
},
}

func Test_ResolveTargets(t *testing.T) {
resolver := config.NewResolver(testConfig, &rest.Config{})

if count := len(resolver.TargetClients()); count != 22 {
t.Errorf("Expected 22 Clients, got %d", count)
if count := len(resolver.TargetClients()); count != 24 {
t.Errorf("Expected 24 Clients, got %d", count)
}
}

Expand Down
86 changes: 86 additions & 0 deletions pkg/config/target_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/kyverno/policy-reporter/pkg/target/securityhub"
"github.com/kyverno/policy-reporter/pkg/target/slack"
"github.com/kyverno/policy-reporter/pkg/target/teams"
"github.com/kyverno/policy-reporter/pkg/target/telegram"
"github.com/kyverno/policy-reporter/pkg/target/ui"
"github.com/kyverno/policy-reporter/pkg/target/webhook"
)
Expand Down Expand Up @@ -284,6 +285,29 @@ func (f *TargetFactory) GCSClients(config GCS) []target.Client {
return clients
}

// TelegramClients resolver method
func (f *TargetFactory) TelegramClients(config Telegram) []target.Client {
clients := make([]target.Client, 0)
if config.Name == "" {
config.Name = "Telegram"
}

if es := f.createTelegramClient(config, Telegram{}); es != nil {
clients = append(clients, es)
}
for i, channel := range config.Channels {
if channel.Name == "" {
channel.Name = fmt.Sprintf("Webhook Channel %d", i+1)
}

if es := f.createTelegramClient(channel, config); es != nil {
clients = append(clients, es)
}
}

return clients
}

func (f *TargetFactory) createSlackClient(config Slack, parent Slack) target.Client {
if (config.SecretRef != "" && f.secretClient != nil) || config.MountedSecret != "" {
f.mapSecretValues(&config, config.SecretRef, config.MountedSecret)
Expand Down Expand Up @@ -576,6 +600,64 @@ func (f *TargetFactory) createWebhookClient(config Webhook, parent Webhook) targ
})
}

func (f *TargetFactory) createTelegramClient(config Telegram, parent Telegram) target.Client {
if (config.SecretRef != "" && f.secretClient != nil) || config.MountedSecret != "" {
f.mapSecretValues(&config, config.SecretRef, config.MountedSecret)
}

if config.Token == "" {
config.Token = parent.Token
}

if config.ChatID == "" || config.Token == "" {
return nil
}

if config.Certificate == "" {
config.Certificate = parent.Certificate
}

if !config.SkipTLS {
config.SkipTLS = parent.SkipTLS
}

if config.MinimumPriority == "" {
config.MinimumPriority = parent.MinimumPriority
}

if !config.SkipExisting {
config.SkipExisting = parent.SkipExisting
}

if len(parent.Headers) > 0 {
headers := map[string]string{}
for header, value := range parent.Headers {
headers[header] = value
}
for header, value := range config.Headers {
headers[header] = value
}

config.Headers = headers
}

zap.S().Infof("%s configured", config.Name)

return telegram.NewClient(telegram.Options{
ClientOptions: target.ClientOptions{
Name: config.Name,
SkipExistingOnStartup: config.SkipExisting,
ResultFilter: createResultFilter(config.Filter, config.MinimumPriority, config.Sources),
ReportFilter: createReportFilter(config.Filter),
},
Token: config.Token,
ChatID: config.ChatID,
Headers: config.Headers,
CustomFields: config.CustomFields,
HTTPClient: http.NewClient(config.Certificate, config.SkipTLS),
})
}

func (f *TargetFactory) createS3Client(config S3, parent S3) target.Client {
if (config.SecretRef != "" && f.secretClient != nil) || config.MountedSecret != "" {
f.mapSecretValues(&config, config.SecretRef, config.MountedSecret)
Expand Down Expand Up @@ -959,6 +1041,10 @@ func (f *TargetFactory) mapSecretValues(config any, ref, mountedSecret string) {

c.Headers["Authorization"] = values.Token
}
case *Telegram:
if values.Token != "" {
c.Token = values.Token
}
}
}

Expand Down
52 changes: 52 additions & 0 deletions pkg/config/target_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func Test_ResolveTarget(t *testing.T) {
t.Errorf("Expected 2 Client, got %d clients", len(clients))
}
})
t.Run("Telegram", func(t *testing.T) {
clients := factory.TelegramClients(testConfig.Telegram)
if len(clients) != 2 {
t.Errorf("Expected 2 Client, got %d clients", len(clients))
}
})
t.Run("S3", func(t *testing.T) {
clients := factory.S3Clients(testConfig.S3)
if len(clients) != 2 {
Expand Down Expand Up @@ -161,6 +167,11 @@ func Test_ResolveTargetWithoutHost(t *testing.T) {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Telegram", func(t *testing.T) {
if len(factory.TelegramClients(config.Telegram{})) != 0 {
t.Error("Expected Client to be nil if no chatID is configured")
}
})
t.Run("S3.Endoint", func(t *testing.T) {
if len(factory.S3Clients(config.S3{})) != 0 {
t.Error("Expected Client to be nil if no endpoint is configured")
Expand Down Expand Up @@ -358,6 +369,20 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})

t.Run("Get Telegram Token from Secret", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, ChatID: "1234"})
if len(clients) != 1 {
t.Error("Expected one client created")
}

client := reflect.ValueOf(clients[0]).Elem()

host := client.FieldByName("host").String()
if host != "https://api.telegram.org/bottoken/sendMessage" {
t.Errorf("Expected host with token from secret, got %s", host)
}
})

t.Run("Get S3 values from Secret", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endoint", Region: "region"}, Bucket: "bucket"})
if len(clients) != 1 {
Expand Down Expand Up @@ -458,6 +483,19 @@ func Test_GetValuesFromSecret(t *testing.T) {
t.Errorf("Expected customFields are added")
}
})
t.Run("Get CustomFields from Telegram", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Token: "XXX", ChatID: "1234"})
if len(clients) != 1 {
t.Error("Expected one client created")
}

client := reflect.ValueOf(clients[0]).Elem()

customFields := client.FieldByName("customFields").MapKeys()
if customFields[0].String() != "field" {
t.Errorf("Expected customFields are added")
}
})
t.Run("Get CustomFields from Kinesis", func(t *testing.T) {
clients := factory.KinesisClients(testConfig.Kinesis)
if len(clients) < 1 {
Expand Down Expand Up @@ -612,6 +650,20 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
}
})

t.Run("Get Telegram Token from MountedSecret", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, ChatID: "123"})
if len(clients) != 1 {
t.Error("Expected one client created")
}

client := reflect.ValueOf(clients[0]).Elem()

token := client.FieldByName("host").String()
if token != "https://api.telegram.org/bottoken/sendMessage" {
t.Errorf("Expected token from mounted secret, got %s", token)
}
})

t.Run("Get S3 values from MountedSecret", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, Bucket: "bucket"})
if len(clients) != 1 {
Expand Down
Loading

0 comments on commit c585a56

Please sign in to comment.