Skip to content

Commit f56126b

Browse files
authored
Merge pull request #1038 from secureCodeBox/feature/auto-discovery-volume-mounts
Allow Volumes & VolumeMounts Config for AutoDiscovery Scans
2 parents 4c53d48 + e179f0b commit f56126b

11 files changed

Lines changed: 340 additions & 189 deletions

auto-discovery/kubernetes/README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,13 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
135135
| config.apiVersion | string | `"config.securecodebox.io/v1"` | |
136136
| config.cluster.name | string | `"docker-desktop"` | |
137137
| config.containerAutoDiscovery.enabled | bool | `false` | |
138-
| config.containerAutoDiscovery.scanConfig.annotations | object | `{}` | annotations to be added to the scans started by the auto-discovery |
139-
| config.containerAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery |
140-
| config.containerAutoDiscovery.scanConfig.parameters | list | `["{{ .ImageID }}"]` | parameters used for the scans created by the containerAutoDiscovery |
138+
| config.containerAutoDiscovery.scanConfig.annotations | object | `{}` | annotations to be added to the scans started by the auto-discovery, all annotation values support templating |
139+
| config.containerAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
140+
| config.containerAutoDiscovery.scanConfig.parameters | list | `["{{ .ImageID }}"]` | parameters used for the scans created by the containerAutoDiscovery, all parameters support templating |
141141
| 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. |
142142
| config.containerAutoDiscovery.scanConfig.scanType | string | `"trivy"` | |
143+
| config.containerAutoDiscovery.scanConfig.volumeMounts | list | `[]` | volumeMounts to add to the scan job, see: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes-1 the fields: `name`, `mountPath`, `subPath`, `subPathExpr` of each volumeMount support templating |
144+
| config.containerAutoDiscovery.scanConfig.volumes | list | `[]` | volumes to add to the scan job, see: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes the fields: `name`, `secret.secretName`, `configMap.name` of each volume support templating |
143145
| config.health.healthProbeBindAddress | string | `":8081"` | |
144146
| config.kind | string | `"AutoDiscoveryConfig"` | |
145147
| config.leaderElection.leaderElect | bool | `true` | |
@@ -148,11 +150,13 @@ kubectl -n juice-shop annotate service juice-shop auto-discovery.securecodebox.i
148150
| config.resourceInclusion.mode | string | `"enabled-per-namespace"` | |
149151
| config.serviceAutoDiscovery.enabled | bool | `true` | |
150152
| 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 |
151-
| 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 |
152-
| config.serviceAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery |
153-
| config.serviceAutoDiscovery.scanConfig.parameters | list | `["-t","{{ .Host.Type }}://{{ .Service.Name }}.{{ .Service.Namespace }}.svc:{{ .Host.Port }}"]` | parameters used for the scans created by the serviceAutoDiscovery |
153+
| 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 |
154+
| config.serviceAutoDiscovery.scanConfig.labels | object | `{}` | labels to be added to the scans started by the auto-discovery, all label values support templating |
155+
| 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 |
154156
| 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. |
155157
| config.serviceAutoDiscovery.scanConfig.scanType | string | `"zap-advanced-scan"` | scanType used for the scans created by the serviceAutoDiscovery |
158+
| config.serviceAutoDiscovery.scanConfig.volumeMounts | list | `[]` | volumeMounts to add to the scan job, see: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes-1 the fields: `name`, `mountPath`, `subPath`, `subPathExpr` of each volumeMount support templating |
159+
| config.serviceAutoDiscovery.scanConfig.volumes | list | `[]` | volumes to add to the scan job, see: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes the fields: `name`, `secret.secretName`, `configMap.name` of each volume support templating |
156160
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images |
157161
| image.repository | string | `"securecodebox/auto-discovery-kubernetes"` | |
158162
| image.tag | string | `nil` | |

auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package v1
66

77
import (
8+
corev1 "k8s.io/api/core/v1"
89
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
910
cfg "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
1011
)
@@ -51,11 +52,13 @@ type ResourceInclusionConfig struct {
5152
}
5253

5354
type ScanConfig struct {
54-
RepeatInterval metav1.Duration `json:"repeatInterval"`
55-
Annotations map[string]string `json:"annotations"`
56-
Labels map[string]string `json:"labels"`
57-
ScanType string `json:"scanType"`
58-
Parameters []string `json:"parameters"`
55+
RepeatInterval metav1.Duration `json:"repeatInterval"`
56+
Annotations map[string]string `json:"annotations"`
57+
Labels map[string]string `json:"labels"`
58+
ScanType string `json:"scanType"`
59+
Parameters []string `json:"parameters"`
60+
Volumes []corev1.Volume `json:"volumes"`
61+
VolumeMounts []corev1.VolumeMount `json:"volumeMounts"`
5962
}
6063

6164
func init() {

auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

auto-discovery/kubernetes/controllers/container_scan_controller.go

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ type ContainerScanReconciler struct {
3333
Config configv1.AutoDiscoveryConfig
3434
}
3535

36+
type Cluster struct {
37+
Name string
38+
}
39+
type ContainerAutoDiscoveryTemplateArgs struct {
40+
Config configv1.AutoDiscoveryConfig
41+
ScanConfig configv1.ScanConfig
42+
Cluster configv1.ClusterConfig
43+
Target metav1.ObjectMeta
44+
Namespace corev1.Namespace
45+
ImageID string
46+
}
47+
3648
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;
3749

3850
// Reconcile compares the Pod object against the state of the cluster and updates both if needed
@@ -159,7 +171,7 @@ func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod
159171
func (r *ContainerScanReconciler) createScheduledScan(ctx context.Context, pod corev1.Pod, imageID string) {
160172
namespace := r.getNamespace(ctx, pod)
161173

162-
newScheduledScan := getScanSpec(r.Config, pod, imageID, namespace)
174+
newScheduledScan := r.generateScan(pod, imageID, namespace)
163175
err := r.Client.Create(ctx, &newScheduledScan)
164176

165177
if err != nil {
@@ -178,23 +190,26 @@ func (r *ContainerScanReconciler) getNamespace(ctx context.Context, pod corev1.P
178190
return result
179191
}
180192

181-
func getScanSpec(config configv1.AutoDiscoveryConfig, pod corev1.Pod, imageID string, namespace corev1.Namespace) executionv1.ScheduledScan {
182-
containerScanConfig := config.ContainerAutoDiscoveryConfig.ScanConfig
193+
func (r *ContainerScanReconciler) generateScan(pod corev1.Pod, imageID string, namespace corev1.Namespace) executionv1.ScheduledScan {
194+
scanConfig := r.Config.ContainerAutoDiscoveryConfig.ScanConfig
195+
templateArgs := ContainerAutoDiscoveryTemplateArgs{
196+
Config: r.Config,
197+
ScanConfig: scanConfig,
198+
Cluster: r.Config.Cluster,
199+
Target: pod.ObjectMeta,
200+
Namespace: namespace,
201+
ImageID: imageID,
202+
}
203+
scanSpec := util.GenerateScanSpec(scanConfig, templateArgs)
183204

184205
newScheduledScan := executionv1.ScheduledScan{
185206
ObjectMeta: metav1.ObjectMeta{
186207
Name: getScanName(imageID),
187208
Namespace: pod.Namespace,
188-
Annotations: getScanAnnotations(config, pod, imageID, namespace),
189-
Labels: getScanLabels(config, pod, imageID, namespace),
190-
},
191-
Spec: executionv1.ScheduledScanSpec{
192-
Interval: containerScanConfig.RepeatInterval,
193-
ScanSpec: &executionv1.ScanSpec{
194-
ScanType: containerScanConfig.ScanType,
195-
Parameters: getScanParameters(config, pod, imageID, namespace),
196-
},
209+
Annotations: getScanAnnotations(scanConfig, templateArgs),
210+
Labels: getScanLabels(scanConfig, templateArgs),
197211
},
212+
Spec: scanSpec,
198213
}
199214
return newScheduledScan
200215
}
@@ -273,23 +288,12 @@ func (r *ContainerScanReconciler) getScan(ctx context.Context, pod corev1.Pod, i
273288
return scan, err
274289
}
275290

276-
func getScanAnnotations(config configv1.AutoDiscoveryConfig, pod corev1.Pod, imageID string, namespace corev1.Namespace) map[string]string {
277-
data := util.TemplateArgs{Target: pod.ObjectMeta, Namespace: namespace.ObjectMeta, Cluster: util.Cluster(config.Cluster), ImageID: imageID}
278-
templates := config.ContainerAutoDiscoveryConfig.ScanConfig.Annotations
279-
return util.ParseMapTemplate(data, templates)
280-
}
281-
282-
func getScanParameters(config configv1.AutoDiscoveryConfig, pod corev1.Pod, imageID string, namespace corev1.Namespace) []string {
283-
data := util.TemplateArgs{Target: pod.ObjectMeta, Namespace: namespace.ObjectMeta, Cluster: util.Cluster(config.Cluster), ImageID: imageID}
284-
templates := config.ContainerAutoDiscoveryConfig.ScanConfig.Parameters
285-
return util.ParseListTemplate(data, templates)
291+
func getScanAnnotations(scanConfig configv1.ScanConfig, templateArgs ContainerAutoDiscoveryTemplateArgs) map[string]string {
292+
return util.ParseMapTemplate(templateArgs, scanConfig.Annotations)
286293
}
287294

288-
func getScanLabels(config configv1.AutoDiscoveryConfig, pod corev1.Pod, imageID string, namespace corev1.Namespace) map[string]string {
289-
data := util.TemplateArgs{Target: pod.ObjectMeta, Namespace: namespace.ObjectMeta, Cluster: util.Cluster(config.Cluster), ImageID: imageID}
290-
templates := config.ContainerAutoDiscoveryConfig.ScanConfig.Labels
291-
292-
generatedLabels := util.ParseMapTemplate(data, templates)
295+
func getScanLabels(scanConfig configv1.ScanConfig, templateArgs ContainerAutoDiscoveryTemplateArgs) map[string]string {
296+
generatedLabels := util.ParseMapTemplate(templateArgs, scanConfig.Labels)
293297
generatedLabels["app.kubernetes.io/managed-by"] = "securecodebox-autodiscovery"
294298

295299
return generatedLabels

auto-discovery/kubernetes/controllers/service_scan_controller.go

Lines changed: 32 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ type ServiceScanReconciler struct {
3636
Config configv1.AutoDiscoveryConfig
3737
}
3838

39+
type ServiceAutoDiscoveryTemplateArgs struct {
40+
Config configv1.AutoDiscoveryConfig
41+
ScanConfig configv1.ScanConfig
42+
Cluster configv1.ClusterConfig
43+
Target metav1.ObjectMeta
44+
Service corev1.Service
45+
Namespace corev1.Namespace
46+
Host HostPort
47+
}
48+
3949
const requeueInterval = 5 * time.Second
4050

4151
// +kubebuilder:rbac:groups="execution.securecodebox.io",resources=scantypes,verbs=get;list;watch
@@ -129,23 +139,35 @@ func (r *ServiceScanReconciler) Reconcile(ctx context.Context, req ctrl.Request)
129139
var previousScan executionv1.ScheduledScan
130140
err := r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-service-port-%d", service.Name, host.Port), Namespace: service.Namespace}, &previousScan)
131141

142+
// generate the scan spec for the current state of the service
143+
templateArgs := ServiceAutoDiscoveryTemplateArgs{
144+
Config: r.Config,
145+
ScanConfig: r.Config.ServiceAutoDiscoveryConfig.ScanConfig,
146+
Cluster: r.Config.Cluster,
147+
Target: service.ObjectMeta,
148+
Service: service,
149+
Namespace: namespace,
150+
Host: host,
151+
}
152+
scanSpec := util.GenerateScanSpec(r.Config.ServiceAutoDiscoveryConfig.ScanConfig, templateArgs)
153+
132154
if apierrors.IsNotFound(err) {
133155
// service was never scanned
134156
log.Info("Discovered new unscanned service, scanning it now", "service", service.Name, "namespace", service.Namespace)
135157

136158
// label is added after the initial query as it was added later and isn't garanteed to be on every auto-discovery managed scan.
137159
versionedLabels["app.kubernetes.io/managed-by"] = "securecodebox-autodiscovery"
138-
versionedLabels = addLabels(versionedLabels, r.Config, service, namespace)
160+
versionedLabels = generateScanLabels(versionedLabels, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, templateArgs)
139161

140162
// No scan for this pod digest yet. Scanning now
141163
scan := executionv1.ScheduledScan{
142164
ObjectMeta: metav1.ObjectMeta{
143165
Name: fmt.Sprintf("%s-service-port-%d", service.Name, host.Port),
144166
Namespace: service.Namespace,
145167
Labels: versionedLabels,
146-
Annotations: generateScanAnnotations(r.Config.ServiceAutoDiscoveryConfig.ScanConfig, r.Config.Cluster, service, namespace),
168+
Annotations: generateScanAnnotations(service.Annotations, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, templateArgs),
147169
},
148-
Spec: generateScanSpec(r.Config, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, host, service, namespace),
170+
Spec: scanSpec,
149171
}
150172

151173
// Ensure ScanType actually exists
@@ -181,11 +203,11 @@ func (r *ServiceScanReconciler) Reconcile(ctx context.Context, req ctrl.Request)
181203

182204
// label is added after the initial query as it was added later and isn't garanteed to be on every auto-discovery managed scan.
183205
versionedLabels["app.kubernetes.io/managed-by"] = "securecodebox-autodiscovery"
184-
versionedLabels = addLabels(versionedLabels, r.Config, service, namespace)
206+
versionedLabels = generateScanLabels(versionedLabels, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, templateArgs)
185207

186208
previousScan.ObjectMeta.Labels = versionedLabels
187-
previousScan.ObjectMeta.Annotations = generateScanAnnotations(r.Config.ServiceAutoDiscoveryConfig.ScanConfig, r.Config.Cluster, service, namespace)
188-
previousScan.Spec = generateScanSpec(r.Config, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, host, service, namespace)
209+
previousScan.ObjectMeta.Annotations = generateScanAnnotations(service.Annotations, r.Config.ServiceAutoDiscoveryConfig.ScanConfig, templateArgs)
210+
previousScan.Spec = scanSpec
189211

190212
log.V(8).Info("Updating previousScan Spec")
191213
err := r.Update(ctx, &previousScan)
@@ -363,19 +385,12 @@ podLoop:
363385
return false
364386
}
365387

366-
func generateScanAnnotations(scanConfig configv1.ScanConfig, clusterConfig configv1.ClusterConfig, service corev1.Service, namespace corev1.Namespace) map[string]string {
367-
templateArgs := util.TemplateArgs{
368-
Target: service.ObjectMeta,
369-
Namespace: namespace.ObjectMeta,
370-
Cluster: util.Cluster{
371-
Name: clusterConfig.Name,
372-
},
373-
}
388+
func generateScanAnnotations(currentAnnotations map[string]string, scanConfig configv1.ScanConfig, templateArgs ServiceAutoDiscoveryTemplateArgs) map[string]string {
374389
annotations := util.ParseMapTemplate(templateArgs, scanConfig.Annotations)
375390

376391
// Copy over securecodebox.io annotations to the created scan
377392
re := regexp.MustCompile(`.*securecodebox\.io/.*`)
378-
for key, value := range service.Annotations {
393+
for key, value := range currentAnnotations {
379394
if matches := re.MatchString(key); matches {
380395
annotations[key] = value
381396
}
@@ -384,45 +399,8 @@ func generateScanAnnotations(scanConfig configv1.ScanConfig, clusterConfig confi
384399

385400
}
386401

387-
// 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
388-
func generateScanSpec(autoDiscoveryConfig configv1.AutoDiscoveryConfig, scanConfig configv1.ScanConfig, host HostPort, service corev1.Service, namespace corev1.Namespace) executionv1.ScheduledScanSpec {
389-
type TemplateArgs struct {
390-
Config configv1.AutoDiscoveryConfig
391-
ScanConfig configv1.ScanConfig
392-
Service corev1.Service
393-
Namespace corev1.Namespace
394-
Host HostPort
395-
}
396-
parameters := scanConfig.Parameters
397-
398-
templateArgs := TemplateArgs{
399-
Config: autoDiscoveryConfig,
400-
Service: service,
401-
Namespace: namespace,
402-
Host: host,
403-
}
404-
405-
params := util.ParseListTemplate(templateArgs, parameters)
406-
407-
scheduledScanSpec := executionv1.ScheduledScanSpec{
408-
Interval: scanConfig.RepeatInterval,
409-
ScanSpec: &executionv1.ScanSpec{
410-
ScanType: scanConfig.ScanType,
411-
Parameters: params,
412-
},
413-
RetriggerOnScanTypeChange: true,
414-
}
415-
416-
return scheduledScanSpec
417-
}
418-
419-
func addLabels(currentLabels map[string]string, config configv1.AutoDiscoveryConfig, service corev1.Service, namespace corev1.Namespace) map[string]string {
420-
data := util.TemplateArgs{
421-
Target: service.ObjectMeta,
422-
Namespace: namespace.ObjectMeta,
423-
Cluster: util.Cluster(config.Cluster),
424-
}
425-
newLabels := util.ParseMapTemplate(data, config.ServiceAutoDiscoveryConfig.ScanConfig.Labels)
402+
func generateScanLabels(currentLabels map[string]string, scanConfig configv1.ScanConfig, templateArgs ServiceAutoDiscoveryTemplateArgs) map[string]string {
403+
newLabels := util.ParseMapTemplate(templateArgs, scanConfig.Labels)
426404

427405
for key, value := range newLabels {
428406
currentLabels[key] = value

0 commit comments

Comments
 (0)