Skip to content

Commit 955b927

Browse files
authored
Merge pull request #52 from prachidamle/alert_metrics
CIS v2 Alerting: Adding Prometheus Metric Exporter for CIS, also adding ClusterScanAlertRule to the scan spec
2 parents bd38b37 + 25805b4 commit 955b927

File tree

1,023 files changed

+147218
-128484
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,023 files changed

+147218
-128484
lines changed

crds/clusterscan.yaml

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,26 @@ spec:
4343
properties:
4444
spec:
4545
properties:
46-
cronSchedule:
47-
nullable: true
48-
type: string
49-
retentionCount:
50-
type: integer
5146
scanProfileName:
5247
nullable: true
5348
type: string
49+
scheduledScanConfig:
50+
nullable: true
51+
properties:
52+
cronSchedule:
53+
nullable: true
54+
type: string
55+
retentionCount:
56+
type: integer
57+
scanAlertRule:
58+
nullable: true
59+
properties:
60+
alertOnComplete:
61+
type: boolean
62+
alertOnFailure:
63+
type: boolean
64+
type: object
65+
type: object
5466
scoreWarning:
5567
enum:
5668
- pass
@@ -63,6 +75,9 @@ spec:
6375
NextScanAt:
6476
nullable: true
6577
type: string
78+
ScanAlertingRuleName:
79+
nullable: true
80+
type: string
6681
conditions:
6782
items:
6883
properties:

go.mod

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ module github.com/rancher/cis-operator
22

33
go 1.13
44

5-
replace k8s.io/client-go => k8s.io/client-go v0.18.0
5+
replace k8s.io/client-go => k8s.io/client-go v0.19.2
66

77
require (
88
github.com/blang/semver v3.5.0+incompatible
9+
github.com/prometheus-operator/prometheus-operator v0.43.2
10+
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.43.0
11+
github.com/prometheus/client_golang v1.8.0
912
github.com/rancher/kubernetes-provider-detector v0.0.0-20200807181951-690274ab1fb3
1013
github.com/rancher/lasso v0.0.0-20200820172840-0e4cc0ef5cb0
1114
github.com/rancher/security-scan v0.2.2-0.20201117171930-af478b83fbe4
1215
github.com/rancher/wrangler v0.6.2-0.20200829053106-7e1dd4260224
1316
github.com/robfig/cron v1.2.0
14-
github.com/sirupsen/logrus v1.4.2
17+
github.com/sirupsen/logrus v1.6.0
1518
github.com/urfave/cli v1.22.2
16-
k8s.io/api v0.18.8
17-
k8s.io/apiextensions-apiserver v0.18.0
18-
k8s.io/apimachinery v0.18.8
19-
k8s.io/client-go v10.0.0+incompatible
19+
golang.org/x/tools v0.0.0-20201120032337-6d151481565c // indirect
20+
k8s.io/api v0.19.2
21+
k8s.io/apiextensions-apiserver v0.19.2
22+
k8s.io/apimachinery v0.19.2
23+
k8s.io/client-go v12.0.0+incompatible
2024
)

go.sum

Lines changed: 1190 additions & 3 deletions
Large diffs are not rendered by default.

main.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import (
1616
"github.com/sirupsen/logrus"
1717
"github.com/urfave/cli"
1818

19+
"log"
20+
"net/http"
21+
22+
"github.com/prometheus/client_golang/prometheus/promhttp"
23+
1924
cisoperatorapiv1 "github.com/rancher/cis-operator/pkg/apis/cis.cattle.io/v1"
2025
cisoperator "github.com/rancher/cis-operator/pkg/securityscan"
2126
)
@@ -26,8 +31,11 @@ var (
2631
kubeConfig string
2732
threads int
2833
name string
29-
securityScanImage = "prachidamle/security-scan"
30-
securityScanImageTag = "v0.1.20"
34+
metricsPort string
35+
alertSeverity string
36+
debug bool
37+
securityScanImage = "rancher/security-scan"
38+
securityScanImageTag = "v0.2.1"
3139
sonobuoyImage = "rancher/sonobuoy-sonobuoy"
3240
sonobuoyImageTag = "v0.16.3"
3341
)
@@ -79,6 +87,23 @@ func main() {
7987
Value: "v0.16.3",
8088
Destination: &sonobuoyImageTag,
8189
},
90+
cli.StringFlag{
91+
Name: "cis_metrics_port",
92+
EnvVar: "CIS_METRICS_PORT",
93+
Value: "8080",
94+
Destination: &metricsPort,
95+
},
96+
cli.BoolFlag{
97+
Name: "debug",
98+
EnvVar: "CIS_OPERATOR_DEBUG",
99+
Destination: &debug,
100+
},
101+
cli.StringFlag{
102+
Name: "alertSeverity",
103+
EnvVar: "CIS_ALERTS_SEVERITY",
104+
Value: "warning",
105+
Destination: &alertSeverity,
106+
},
82107
}
83108
app.Action = run
84109

@@ -88,10 +113,11 @@ func main() {
88113
}
89114

90115
func run(c *cli.Context) {
91-
92116
logrus.Info("Starting CIS-Operator")
93117
ctx := signals.SetupSignalHandler(context.Background())
94-
118+
if debug {
119+
logrus.SetLevel(logrus.DebugLevel)
120+
}
95121
kubeConfig = c.String("kubeconfig")
96122
threads = c.Int("threads")
97123
securityScanImage = c.String("security-scan-image")
@@ -110,6 +136,7 @@ func run(c *cli.Context) {
110136
SecurityScanImageTag: securityScanImageTag,
111137
SonobuoyImage: sonobuoyImage,
112138
SonobuoyImageTag: sonobuoyImageTag,
139+
AlertSeverity: alertSeverity,
113140
}
114141

115142
if err := validateConfig(imgConfig); err != nil {
@@ -124,6 +151,9 @@ func run(c *cli.Context) {
124151
if err := ctl.Start(ctx, threads, 2*time.Hour); err != nil {
125152
logrus.Fatalf("Error starting: %v", err)
126153
}
154+
http.Handle("/metrics", promhttp.Handler())
155+
log.Fatal(http.ListenAndServe(":"+metricsPort, nil))
156+
127157
<-ctx.Done()
128158
logrus.Info("Registered CIS controller")
129159
}
@@ -132,10 +162,8 @@ func validateConfig(imgConfig *cisoperatorapiv1.ScanImageConfig) error {
132162
if imgConfig.SecurityScanImage == "" {
133163
return errors.New("No Security-Scan Image specified")
134164
}
135-
136165
if imgConfig.SonobuoyImage == "" {
137166
return errors.New("No Sonobuoy tool Image specified")
138167
}
139-
140168
return nil
141169
}

pkg/apis/cis.cattle.io/v1/types.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ type ClusterScan struct {
5353
type ClusterScanSpec struct {
5454
// scan profile to use
5555
ScanProfileName string `json:"scanProfileName,omitempty"`
56-
// Cron Expression for Schedule
57-
CronSchedule string `yaml:"cron_schedule" json:"cronSchedule,omitempty"`
58-
// Number of past scans to keep
59-
RetentionCount int `yaml:"retentionCount" json:"retentionCount,omitempty"`
56+
//config for scheduled scan
57+
ScheduledScanConfig *ScheduledScanConfig `yaml:"scheduled_scan_config" json:"scheduledScanConfig,omitempty"`
6058
// Specify if tests with "warn" output should be counted towards scan failure
6159
ScoreWarning string `yaml:"score_warning" json:"scoreWarning,omitempty"`
6260
}
@@ -69,6 +67,7 @@ type ClusterScanStatus struct {
6967
ObservedGeneration int64 `json:"observedGeneration"`
7068
Conditions []genericcondition.GenericCondition `json:"conditions,omitempty"`
7169
NextScanAt string `json:"NextScanAt"`
70+
ScanAlertingRuleName string `json:"ScanAlertingRuleName"`
7271
}
7372

7473
type ClusterScanStatusDisplay struct {
@@ -87,6 +86,20 @@ type ClusterScanSummary struct {
8786
NotApplicable int `json:"notApplicable"`
8887
}
8988

89+
type ScheduledScanConfig struct {
90+
// Cron Expression for Schedule
91+
CronSchedule string `yaml:"cron_schedule" json:"cronSchedule,omitempty"`
92+
// Number of past scans to keep
93+
RetentionCount int `yaml:"retentionCount" json:"retentionCount,omitempty"`
94+
//configure the alerts to be sent out
95+
ScanAlertRule *ClusterScanAlertRule `json:"scanAlertRule,omitempty"`
96+
}
97+
98+
type ClusterScanAlertRule struct {
99+
AlertOnComplete bool `json:"alertOnComplete,omitempty"`
100+
AlertOnFailure bool `json:"alertOnFailure,omitempty"`
101+
}
102+
90103
// +genclient
91104
// +genclient:nonNamespaced
92105
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -145,4 +158,5 @@ type ScanImageConfig struct {
145158
SecurityScanImageTag string
146159
SonobuoyImage string
147160
SonobuoyImageTag string
161+
AlertSeverity string
148162
}

pkg/apis/cis.cattle.io/v1/zz_generated_deepcopy.go

Lines changed: 43 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package alert
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"text/template"
7+
8+
meta1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
10+
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
11+
k8Yaml "k8s.io/apimachinery/pkg/util/yaml"
12+
13+
cisoperatorapiv1 "github.com/rancher/cis-operator/pkg/apis/cis.cattle.io/v1"
14+
"github.com/rancher/wrangler/pkg/name"
15+
)
16+
17+
const templateName = "prometheusrule.template"
18+
const templatePath = "./pkg/securityscan/alert/templates/prometheusrule.template"
19+
20+
func NewPrometheusRule(clusterscan *cisoperatorapiv1.ClusterScan, clusterscanprofile *cisoperatorapiv1.ClusterScanProfile, imageConfig *cisoperatorapiv1.ScanImageConfig) (*monitoringv1.PrometheusRule, error) {
21+
configdata := map[string]interface{}{
22+
"namespace": cisoperatorapiv1.ClusterScanNS,
23+
"name": name.SafeConcatName("rancher-cis-alerts", clusterscan.Name),
24+
"severity": imageConfig.AlertSeverity,
25+
"scanName": clusterscan.Name,
26+
"scanProfileName": clusterscanprofile.Name,
27+
"alertOnFailure": clusterscan.Spec.ScheduledScanConfig.ScanAlertRule.AlertOnFailure,
28+
"alertOnComplete": clusterscan.Spec.ScheduledScanConfig.ScanAlertRule.AlertOnComplete,
29+
}
30+
scanAlertRule, err := generatePrometheusRule(clusterscan, templateName, templatePath, configdata)
31+
if err != nil {
32+
return scanAlertRule, err
33+
}
34+
35+
return scanAlertRule, nil
36+
}
37+
38+
func generatePrometheusRule(clusterscan *cisoperatorapiv1.ClusterScan, templateName string, templateFile string, data map[string]interface{}) (*monitoringv1.PrometheusRule, error) {
39+
scanAlertRule := &monitoringv1.PrometheusRule{}
40+
obj, err := parseTemplate(clusterscan, templateName, templateFile, data)
41+
if err != nil {
42+
return nil, fmt.Errorf("Error parsing the template %v", err)
43+
}
44+
45+
if err := obj.Decode(&scanAlertRule); err != nil {
46+
return nil, fmt.Errorf("Error decoding to template %v", err)
47+
}
48+
49+
ownerRef := meta1.OwnerReference{
50+
APIVersion: "cis.cattle.io/v1",
51+
Kind: "ClusterScan",
52+
Name: clusterscan.Name,
53+
UID: clusterscan.GetUID(),
54+
}
55+
scanAlertRule.ObjectMeta.OwnerReferences = append(scanAlertRule.ObjectMeta.OwnerReferences, ownerRef)
56+
57+
return scanAlertRule, nil
58+
}
59+
60+
func parseTemplate(clusterscan *cisoperatorapiv1.ClusterScan, templateName string, templateFile string, data map[string]interface{}) (*k8Yaml.YAMLOrJSONDecoder, error) {
61+
cmTemplate, err := template.New(templateName).ParseFiles(templateFile)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
var b bytes.Buffer
67+
err = cmTemplate.Execute(&b, data)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
return k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(b.String())), 1000), nil
73+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apiVersion: monitoring.coreos.com/v1
2+
kind: PrometheusRule
3+
metadata:
4+
name: {{ .name }}
5+
namespace: {{ .namespace }}
6+
labels:
7+
app: rancher-monitoring
8+
spec:
9+
groups:
10+
- name: rancher-cis-scan-exporter
11+
rules:
12+
{{- if .alertOnFailure }}
13+
- alert: CISScanHasFailures
14+
annotations:
15+
description: CIS ClusterScan "{{ .scanName }}" has {{ "{{ $value }}" }} test failures
16+
summary: CIS ClusterScan has tests failures
17+
expr: cis_scan_num_tests_fail{scan_name="{{ .scanName }}"} > 0
18+
for: 1m
19+
labels:
20+
severity: {{ .severity }}
21+
job: rancher-cis-scan
22+
{{- end }}
23+
{{- if .alertOnComplete }}
24+
- alert: CISScanHasCompleted
25+
annotations:
26+
description: CIS ClusterScan "{{ .scanName }}" with Cluster Scan profile "{{ .scanProfileName }}" has completed.
27+
summary: CIS ClusterScan has completed
28+
expr: increase(cis_scan_num_scans_complete{scan_name="{{ .scanName }}"}[5m]) > 0
29+
for: 1m
30+
labels:
31+
severity: {{ .severity }}
32+
job: rancher-cis-scan
33+
{{- end }}

0 commit comments

Comments
 (0)