Skip to content
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
2 changes: 2 additions & 0 deletions auto-discovery/kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
| config.cluster.name | string | `"docker-desktop"` | |
| config.containerAutoDiscovery.enabled | bool | `false` | |
| config.containerAutoDiscovery.scanConfig.annotations | object | `{}` | annotations to be added to the scans started by the auto-discovery, all annotation values support templating |
| config.containerAutoDiscovery.scanConfig.hookSelector | object | `{}` | hookSelector allows to specify a LabelSelector with which the hooks are selected, see: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Both matchLabels and matchExpressions are supported. All values in the matchLabels map support templating. MatchExpressions support templating in the `key` field and in every entry in the `values` list. If a value in the list renders to an empty string it is removed from the list. |
| config.containerAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
| config.containerAutoDiscovery.scanConfig.parameters | list | `["{{ .ImageID }}"]` | parameters used for the scans created by the containerAutoDiscovery, all parameters support templating |
| config.containerAutoDiscovery.scanConfig.repeatInterval | string | `"168h"` | interval in which scans are automatically repeated. If the target is updated (meaning a new image revision is deployed) the scan will repeated beforehand and the interval is reset. |
Expand All @@ -151,6 +152,7 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
| config.serviceAutoDiscovery.enabled | bool | `true` | |
| config.serviceAutoDiscovery.passiveReconcileInterval | string | `"1m"` | interval in which every service is re-checked for updated pods, if service object is updated directly this the service will get reconciled immediately |
| config.serviceAutoDiscovery.scanConfig.annotations | object | `{"defectdojo.securecodebox.io/engagement-name":"{{ .Target.Name }}","defectdojo.securecodebox.io/engagement-version":"{{if (index .Target.Labels `app.kubernetes.io/version`) }}{{ index .Target.Labels `app.kubernetes.io/version` }}{{end}}","defectdojo.securecodebox.io/product-name":"{{ .Cluster.Name }} | {{ .Namespace.Name }} | {{ .Target.Name }}","defectdojo.securecodebox.io/product-tags":"cluster/{{ .Cluster.Name }},namespace/{{ .Namespace.Name }}"}` | annotations to be added to the scans started by the auto-discovery, all annotation values support templating |
| config.serviceAutoDiscovery.scanConfig.hookSelector | object | `{}` | HookSelector allows to specify a LabelSelector with which the hooks are selected, see: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Both matchLabels and matchExpressions are supported. All values in the matchLabels map support templating. MatchExpressions support templating in the `key` field and in every entry in the `values` list. If a value in the list renders to an empty string it is removed from the list. |
| config.serviceAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
| config.serviceAutoDiscovery.scanConfig.parameters | list | `["-t","{{ .Host.Type }}://{{ .Service.Name }}.{{ .Service.Namespace }}.svc:{{ .Host.Port }}"]` | parameters used for the scans created by the serviceAutoDiscovery, all parameters support templating |
| config.serviceAutoDiscovery.scanConfig.repeatInterval | string | `"168h"` | interval in which scans are automatically repeated. If the target is updated (meaning a new image revision is deployed) the scan will repeated beforehand and the interval is reset. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type ScanConfig struct {
Parameters []string `json:"parameters"`
Volumes []corev1.Volume `json:"volumes"`
VolumeMounts []corev1.VolumeMount `json:"volumeMounts"`
HookSelector metav1.LabelSelector `json:"hookSelector"`
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion auto-discovery/kubernetes/auto-discovery-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ serviceAutoDiscovery:
repeatInterval: "168h"
# -- labels to be added to the scans started by the auto-discovery
labels: {}
# -- hookSelector allows to specify a LabelSelector with which the hooks are selected
hookSelector: {}
# -- annotations to be added to the scans started by the auto-discovery
annotations:
defectdojo.securecodebox.io/product-name: "{{ .Cluster.Name }} | {{ .Namespace.Name }} | {{ .Target.Name }}"
Expand All @@ -56,4 +58,6 @@ containerAutoDiscovery:
# -- labels to be added to the scans started by the auto-discovery
labels: {}
# -- annotations to be added to the scans started by the auto-discovery
annotations: {}
annotations: {}
# -- hookSelector allows to specify a LabelSelector with which the hooks are selected
hookSelector: {}
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,20 @@ func checkScanGoTemplate(scan executionv1.ScheduledScan, scanSpec scanGoTemplate
Expect(annotationsCorrect).Should(BeTrue())
Expect(labelsCorrect).Should(BeTrue())
Expect(parametersCorrect).Should(BeTrue())
Expect(scan.Spec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement(
metav1.LabelSelectorRequirement{
Operator: metav1.LabelSelectorOpIn,
Key: "foo",
Values: []string{"bar", "baz"},
},
))
Expect(scan.Spec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement(
metav1.LabelSelectorRequirement{

Operator: metav1.LabelSelectorOpDoesNotExist,
Key: "foo",
},
))
return annotationsCorrect && labelsCorrect && parametersCorrect
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ var _ = Describe("ServiceScan controller", func() {
Expect(scheduledScan.ObjectMeta.Name).Should(Equal("juice-shop-service-port-3000"))
Expect(scheduledScan.Spec.ScanSpec.ScanType).Should(Equal("nmap"))
Expect(scheduledScan.Spec.ScanSpec.Parameters).Should(BeEquivalentTo([]string{"-p", "3000", "juice-shop.scan-creation.svc"}))
Expect(scheduledScan.Spec.ScanSpec.HookSelector.MatchLabels).Should(BeEquivalentTo(map[string]string{
"foo": "bar",
}))
Expect(scheduledScan.Status.LastScheduleTime).Should(BeNil())

})

It("Should hold ScanCreation until all pod digests for the container backing the service match", func() {
Expand Down
18 changes: 18 additions & 0 deletions auto-discovery/kubernetes/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ var _ = BeforeSuite(func() {
Labels: map[string]string{},
Parameters: []string{"-p", "{{ .Host.Port }}", "{{ .Service.Name }}.{{ .Service.Namespace }}.svc"},
ScanType: "nmap",
HookSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
ContainerAutoDiscoveryConfig: configv1.ContainerAutoDiscoveryConfig{
Expand All @@ -94,6 +99,19 @@ var _ = BeforeSuite(func() {
Labels: map[string]string{"testLabel": "{{ .Namespace.Name }}"},
Parameters: []string{"-p", "{{ .Namespace.Name }}"},
ScanType: "nmap",
HookSelector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Operator: metav1.LabelSelectorOpIn,
Key: "foo",
Values: []string{"bar", "baz"},
},
{
Operator: metav1.LabelSelectorOpDoesNotExist,
Key: "foo",
},
},
},
},
},
ResourceInclusion: configv1.ResourceInclusionConfig{
Expand Down
2 changes: 2 additions & 0 deletions auto-discovery/kubernetes/docs/README.ArtifactHub.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
| config.cluster.name | string | `"docker-desktop"` | |
| config.containerAutoDiscovery.enabled | bool | `false` | |
| config.containerAutoDiscovery.scanConfig.annotations | object | `{}` | annotations to be added to the scans started by the auto-discovery, all annotation values support templating |
| config.containerAutoDiscovery.scanConfig.hookSelector | object | `{}` | hookSelector allows to specify a LabelSelector with which the hooks are selected, see: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Both matchLabels and matchExpressions are supported. All values in the matchLabels map support templating. MatchExpressions support templating in the `key` field and in every entry in the `values` list. If a value in the list renders to an empty string it is removed from the list. |
| config.containerAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
| config.containerAutoDiscovery.scanConfig.parameters | list | `["{{ .ImageID }}"]` | parameters used for the scans created by the containerAutoDiscovery, all parameters support templating |
| config.containerAutoDiscovery.scanConfig.repeatInterval | string | `"168h"` | interval in which scans are automatically repeated. If the target is updated (meaning a new image revision is deployed) the scan will repeated beforehand and the interval is reset. |
Expand All @@ -143,6 +144,7 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
| config.serviceAutoDiscovery.enabled | bool | `true` | |
| config.serviceAutoDiscovery.passiveReconcileInterval | string | `"1m"` | interval in which every service is re-checked for updated pods, if service object is updated directly this the service will get reconciled immediately |
| config.serviceAutoDiscovery.scanConfig.annotations | object | `{"defectdojo.securecodebox.io/engagement-name":"{{ .Target.Name }}","defectdojo.securecodebox.io/engagement-version":"{{if (index .Target.Labels `app.kubernetes.io/version`) }}{{ index .Target.Labels `app.kubernetes.io/version` }}{{end}}","defectdojo.securecodebox.io/product-name":"{{ .Cluster.Name }} | {{ .Namespace.Name }} | {{ .Target.Name }}","defectdojo.securecodebox.io/product-tags":"cluster/{{ .Cluster.Name }},namespace/{{ .Namespace.Name }}"}` | annotations to be added to the scans started by the auto-discovery, all annotation values support templating |
| config.serviceAutoDiscovery.scanConfig.hookSelector | object | `{}` | HookSelector allows to specify a LabelSelector with which the hooks are selected, see: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors Both matchLabels and matchExpressions are supported. All values in the matchLabels map support templating. MatchExpressions support templating in the `key` field and in every entry in the `values` list. If a value in the list renders to an empty string it is removed from the list. |
| config.serviceAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
| config.serviceAutoDiscovery.scanConfig.parameters | list | `["-t","{{ .Host.Type }}://{{ .Service.Name }}.{{ .Service.Namespace }}.svc:{{ .Host.Port }}"]` | parameters used for the scans created by the serviceAutoDiscovery, all parameters support templating |
| config.serviceAutoDiscovery.scanConfig.repeatInterval | string | `"168h"` | interval in which scans are automatically repeated. If the target is updated (meaning a new image revision is deployed) the scan will repeated beforehand and the interval is reset. |
Expand Down
61 changes: 55 additions & 6 deletions auto-discovery/kubernetes/pkg/util/gotemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
configv1 "github.com/secureCodeBox/secureCodeBox/auto-discovery/kubernetes/api/v1"
executionv1 "github.com/secureCodeBox/secureCodeBox/operator/apis/execution/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func templateOrPanic(templateString string, templateArgs interface{}) string {
Expand All @@ -31,6 +32,7 @@ func templateOrPanic(templateString string, templateArgs interface{}) string {
return ""
}

// ParseMapTemplate templates our all the values of the map with the given template args
func ParseMapTemplate(templateArgs interface{}, templates map[string]string) map[string]string {
result := map[string]string{}

Expand All @@ -44,6 +46,7 @@ func ParseMapTemplate(templateArgs interface{}, templates map[string]string) map
return result
}

// ParseListTemplate templates our all the values of the list with the given template args
func ParseListTemplate(templateArgs interface{}, templates []string) []string {
var result []string

Expand All @@ -57,12 +60,7 @@ func ParseListTemplate(templateArgs interface{}, templates []string) []string {
return result
}

// Takes in both autoDiscoveryConfig and scanConfig as this function might be used by other controllers in the future, which can then pass in the their relevant scanConfig into this function
func GenerateScanSpec(scanConfig configv1.ScanConfig, templateArgs interface{}) executionv1.ScheduledScanSpec {
parameters := scanConfig.Parameters

params := ParseListTemplate(templateArgs, parameters)

func generateVolumes(scanConfig configv1.ScanConfig, templateArgs interface{}) []corev1.Volume {
volumes := make([]corev1.Volume, len(scanConfig.Volumes))
for i, volume := range scanConfig.Volumes {
templatedVolume := volume.DeepCopy()
Expand All @@ -75,6 +73,10 @@ func GenerateScanSpec(scanConfig configv1.ScanConfig, templateArgs interface{})
}
volumes[i] = *templatedVolume
}
return volumes
}

func generateVolumeMounts(scanConfig configv1.ScanConfig, templateArgs interface{}) []corev1.VolumeMount {
volumeMounts := make([]corev1.VolumeMount, len(scanConfig.VolumeMounts))
for i, volumeMount := range scanConfig.VolumeMounts {
templatedVolumeMount := volumeMount.DeepCopy()
Expand All @@ -85,6 +87,52 @@ func GenerateScanSpec(scanConfig configv1.ScanConfig, templateArgs interface{})

volumeMounts[i] = *templatedVolumeMount
}
return volumeMounts
}

func generateHookSelectors(scanConfig configv1.ScanConfig, templateArgs interface{}) *metav1.LabelSelector {
var hookSelector *metav1.LabelSelector = nil
if scanConfig.HookSelector.MatchExpressions != nil {
templatedMatchExpression := make([]metav1.LabelSelectorRequirement, len(scanConfig.HookSelector.MatchExpressions))

for i, matchExpression := range scanConfig.HookSelector.MatchExpressions {

templatedExpression := matchExpression.DeepCopy()
templatedExpression.Key = templateOrPanic(matchExpression.Key, templateArgs)
if matchExpression.Values != nil {
values := []string{}
for _, value := range matchExpression.Values {
templatedValue := templateOrPanic(value, templateArgs)
if templatedValue != "" {
values = append(values, templatedValue)
}
}
templatedExpression.Values = values
}
templatedMatchExpression[i] = *templatedExpression
}

hookSelector = &metav1.LabelSelector{
MatchExpressions: templatedMatchExpression,
}
}
if scanConfig.HookSelector.MatchLabels != nil {
hookSelector = &metav1.LabelSelector{
MatchLabels: ParseMapTemplate(templateArgs, scanConfig.HookSelector.MatchLabels),
}
}
return hookSelector
}

// GenerateScanSpec takes in both autoDiscoveryConfig and scanConfig as this function might be used by other controllers in the future, which can then pass in the their relevant scanConfig into this function
func GenerateScanSpec(scanConfig configv1.ScanConfig, templateArgs interface{}) executionv1.ScheduledScanSpec {
parameters := scanConfig.Parameters

params := ParseListTemplate(templateArgs, parameters)

volumes := generateVolumes(scanConfig, templateArgs)
volumeMounts := generateVolumeMounts(scanConfig, templateArgs)
hookSelector := generateHookSelectors(scanConfig, templateArgs)

scheduledScanSpec := executionv1.ScheduledScanSpec{
Interval: scanConfig.RepeatInterval,
Expand All @@ -93,6 +141,7 @@ func GenerateScanSpec(scanConfig configv1.ScanConfig, templateArgs interface{})
Parameters: params,
Volumes: volumes,
VolumeMounts: volumeMounts,
HookSelector: hookSelector,
},
RetriggerOnScanTypeChange: true,
}
Expand Down
102 changes: 102 additions & 0 deletions auto-discovery/kubernetes/pkg/util/gotemplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,108 @@ var _ = Describe("gotemplate helper util", func() {
SubPath: "test.txt",
}))
})

Context("HookSelectors", func() {
templateArgs := TestTemplateArgs{
Target: metav1.ObjectMeta{
Name: "foobar",
Namespace: "barfoo",
},
Namespace: corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "barfoo",
},
},
Cluster: configv1.ClusterConfig{
Name: "test-cluster",
},
}

It("should work with empty list of matchExpression", func() {
scanConfig := configv1.ScanConfig{
RepeatInterval: metav1.Duration{Duration: time.Hour},
Annotations: map[string]string{},
Labels: map[string]string{},
Parameters: []string{"-p", "3000", "{{ .Target.Name }}.{{ .Namespace.Name }}.svc"},
ScanType: "nmap",
HookSelector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "{{ .Target.Name }}",
Operator: metav1.LabelSelectorOpIn,
Values: []string{
"defectdojo",
"{{ if eq .Target.Namespace `foobar` }}notification-email{{ end }}",
"{{ if eq .Target.Namespace `barfoo` }}notification-slack{{ end }}",
},
},
{
Key: "ignore-{{ `this` }}",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
}

scanSpec := GenerateScanSpec(scanConfig, templateArgs)

Expect(scanSpec.ScanSpec.ScanType).To(Equal("nmap"))
Expect(scanSpec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement(metav1.LabelSelectorRequirement{
Key: "foobar",
Operator: metav1.LabelSelectorOpIn,
Values: []string{
"defectdojo",
"notification-slack",
},
}))
Expect(scanSpec.ScanSpec.HookSelector.MatchExpressions).To(ContainElement(metav1.LabelSelectorRequirement{
Key: "ignore-this",
Operator: metav1.LabelSelectorOpDoesNotExist,
}))
})

It("should work a a list of multiple matchExpressions", func() {
scanConfig := configv1.ScanConfig{
RepeatInterval: metav1.Duration{Duration: time.Hour},
Annotations: map[string]string{},
Labels: map[string]string{},
Parameters: []string{"-p", "3000", "{{ .Target.Name }}.{{ .Namespace.Name }}.svc"},
ScanType: "nmap",
HookSelector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{},
},
}

scanSpec := GenerateScanSpec(scanConfig, templateArgs)

Expect(scanSpec.ScanSpec.ScanType).To(Equal("nmap"))
Expect(scanSpec.ScanSpec.HookSelector.MatchExpressions).To(BeEmpty())
})

It("should template with matchLabels", func() {
scanConfig := configv1.ScanConfig{
RepeatInterval: metav1.Duration{Duration: time.Hour},
Annotations: map[string]string{},
Labels: map[string]string{},
Parameters: []string{"-p", "3000", "{{ .Target.Name }}.{{ .Namespace.Name }}.svc"},
ScanType: "nmap",
HookSelector: metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
"bar": "{{ .Target.Name }}",
},
},
}

scanSpec := GenerateScanSpec(scanConfig, templateArgs)

Expect(scanSpec.ScanSpec.ScanType).To(Equal("nmap"))
Expect(scanSpec.ScanSpec.HookSelector.MatchLabels).To(BeEquivalentTo(map[string]string{
"foo": "bar",
"bar": "foobar",
}))
})
})
})
})

Expand Down
Loading