Skip to content

Commit

Permalink
feat: Allow specifying timeout for scan jobs (#53)
Browse files Browse the repository at this point in the history
Resolves: #51 

Signed-off-by: Daniel Pacak <[email protected]>
  • Loading branch information
danielpacak authored Jun 5, 2020
1 parent b10e120 commit 02c0c55
Show file tree
Hide file tree
Showing 15 changed files with 218 additions and 109 deletions.
68 changes: 68 additions & 0 deletions pkg/cmd/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"errors"
"strings"
"time"

"github.com/aquasecurity/starboard/pkg/kube"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

func SetGlobalFlags(cf *genericclioptions.ConfigFlags, cmd *cobra.Command) {
cf.AddFlags(cmd.Flags())
for _, c := range cmd.Commands() {
SetGlobalFlags(cf, c)
}
}

func WorkloadFromArgs(namespace string, args []string) (workload kube.Workload, err error) {
if len(args) < 1 {
err = errors.New("required workload kind and name not specified")
return
}

parts := strings.SplitN(args[0], "/", 2)
if len(parts) == 1 {
workload = kube.Workload{
Namespace: namespace,
Kind: kube.WorkloadKindPod,
Name: parts[0],
}
return
}
kind, err := kube.WorkloadKindFromString(parts[0])
if err != nil {
return
}
if "" == parts[1] {
err = errors.New("required workload name is blank")
return
}
workload = kube.Workload{
Namespace: namespace,
Kind: kind,
Name: parts[1],
}
return
}

const (
scanJobTimeoutFlagName = "scan-job-timeout"
)

func registerScannerOpts(cmd *cobra.Command) {
cmd.Flags().Duration(scanJobTimeoutFlagName, time.Duration(0),
"The length of time to wait before giving up on a scan job. Non-zero values should contain a"+
" corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout the scan job.")

}

func getScannerOpts(cmd *cobra.Command) (opts kube.ScannerOpts, err error) {
opts.ScanJobTimeout, err = cmd.Flags().GetDuration(scanJobTimeoutFlagName)
if err != nil {
return
}
return
}
8 changes: 7 additions & 1 deletion pkg/cmd/find_vulnerabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ NAME is the name of a particular Kubernetes workload.
if err != nil {
return err
}
reports, err := trivy.NewScanner(kubernetesClientset).Scan(ctx, workload)
opts, err := getScannerOpts(cmd)
if err != nil {
return
}
reports, err := trivy.NewScanner(opts, kubernetesClientset).Scan(ctx, workload)
if err != nil {
return
}
Expand All @@ -78,5 +82,7 @@ NAME is the name of a particular Kubernetes workload.
},
}

registerScannerOpts(cmd)

return cmd
}
9 changes: 8 additions & 1 deletion pkg/cmd/kube_bench.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ func NewKubeBenchCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
if err != nil {
return
}
report, node, err := kubebench.NewScanner(kubernetesClientset).Scan(ctx)
opts, err := getScannerOpts(cmd)
if err != nil {
return
}
report, node, err := kubebench.NewScanner(opts, kubernetesClientset).Scan(ctx)
if err != nil {
return
}
Expand All @@ -38,5 +42,8 @@ func NewKubeBenchCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
return
},
}

registerScannerOpts(cmd)

return cmd
}
9 changes: 8 additions & 1 deletion pkg/cmd/kube_hunter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func NewKubeHunterCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
if err != nil {
return
}
report, err := kubehunter.NewScanner(kubernetesClientset).Scan(ctx)
opts, err := getScannerOpts(cmd)
if err != nil {
return
}
report, err := kubehunter.NewScanner(opts, kubernetesClientset).Scan(ctx)
if err != nil {
return
}
Expand All @@ -40,5 +44,8 @@ func NewKubeHunterCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
return
},
}

registerScannerOpts(cmd)

return cmd
}
9 changes: 8 additions & 1 deletion pkg/cmd/polaris.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func NewPolarisCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
if err != nil {
return
}
reports, err := polaris.NewScanner(clientset).Scan(ctx)
opts, err := getScannerOpts(cmd)
if err != nil {
return
}
reports, err := polaris.NewScanner(opts, clientset).Scan(ctx)
if err != nil {
return
}
Expand All @@ -40,5 +44,8 @@ func NewPolarisCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
return
},
}

registerScannerOpts(cmd)

return cmd
}
45 changes: 1 addition & 44 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
package cmd

import (
"errors"
"strings"

"github.com/aquasecurity/starboard/pkg/kube"

"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
Expand All @@ -32,45 +27,7 @@ func NewRootCmd(version VersionInfo) *cobra.Command {
rootCmd.AddCommand(NewGetCmd(cf))
rootCmd.AddCommand(NewCleanupCmd(cf))

SetFlags(cf, rootCmd)
SetGlobalFlags(cf, rootCmd)

return rootCmd
}

func SetFlags(cf *genericclioptions.ConfigFlags, cmd *cobra.Command) {
cf.AddFlags(cmd.Flags())
for _, c := range cmd.Commands() {
SetFlags(cf, c)
}
}

func WorkloadFromArgs(namespace string, args []string) (workload kube.Workload, err error) {
if len(args) < 1 {
err = errors.New("required workload kind and name not specified")
return
}

parts := strings.SplitN(args[0], "/", 2)
if len(parts) == 1 {
workload = kube.Workload{
Namespace: namespace,
Kind: kube.WorkloadKindPod,
Name: parts[0],
}
return
}
kind, err := kube.WorkloadKindFromString(parts[0])
if err != nil {
return
}
if "" == parts[1] {
err = errors.New("required workload name is blank")
return
}
workload = kube.Workload{
Namespace: namespace,
Kind: kind,
Name: parts[1],
}
return
}
14 changes: 11 additions & 3 deletions pkg/find/vulnerabilities/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ package vulnerabilities

import (
"context"
sec "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"

starboard "github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
"github.com/aquasecurity/starboard/pkg/kube"
core "k8s.io/api/core/v1"
)

// Scanner defines methods for vulnerability scanner.
//
// Scan scans all container images of the specified Kubernetes workload.
// Returns a map of container names to VulnerabilityReports.
//
// ScanByPodSpec scans all container images of the specified Kubernetes workload with the given PodSpec.
// Returns a map of container names to VulnerabilityReports.
type Scanner interface {
Scan(ctx context.Context, workload kube.Workload) (reports map[string]sec.VulnerabilityReport, err error)
ScanByPodSpec(ctx context.Context, workload kube.Workload, spec core.PodSpec) (reports map[string]sec.VulnerabilityReport, err error)
Scan(ctx context.Context, workload kube.Workload) (reports map[string]starboard.VulnerabilityReport, err error)
ScanByPodSpec(ctx context.Context, workload kube.Workload, spec core.PodSpec) (reports map[string]starboard.VulnerabilityReport, err error)
}
37 changes: 18 additions & 19 deletions pkg/find/vulnerabilities/trivy/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import (
"context"
"fmt"
"io"
"time"

"github.com/aquasecurity/starboard/pkg/ext"

"github.com/aquasecurity/starboard/pkg/scanners"
"k8s.io/klog"

"github.com/aquasecurity/starboard/pkg/kube"
Expand All @@ -33,26 +32,26 @@ const (
trivyImageRef = "docker.io/aquasec/trivy:0.8.0"
)

var (
scanJobRunnerTimeout = 60 * time.Second
)

type scanner struct {
clientset kubernetes.Interface
pods *pod.Manager
secrets *secret.Manager
converter Converter
}

func NewScanner(clientset kubernetes.Interface) vulnerabilities.Scanner {
// NewScanner constructs a new vulnerability Scanner with the specified options and Kubernetes client Interface.
func NewScanner(opts kube.ScannerOpts, clientset kubernetes.Interface) vulnerabilities.Scanner {
return &scanner{
opts: opts,
clientset: clientset,
pods: pod.NewPodManager(clientset),
secrets: secret.NewSecretManager(clientset),
converter: DefaultConverter,
}
}

type scanner struct {
opts kube.ScannerOpts
clientset kubernetes.Interface
pods *pod.Manager
secrets *secret.Manager
converter Converter
scanners.Base
}

func (s *scanner) Scan(ctx context.Context, workload kube.Workload) (reports map[string]sec.VulnerabilityReport, err error) {
klog.V(3).Infof("Getting Pod template for workload: %v", workload)
podSpec, err := s.pods.GetPodSpecByWorkload(ctx, workload)
Expand All @@ -69,26 +68,26 @@ func (s *scanner) Scan(ctx context.Context, workload kube.Workload) (reports map
}

func (s *scanner) ScanByPodSpec(ctx context.Context, workload kube.Workload, spec core.PodSpec) (map[string]sec.VulnerabilityReport, error) {
klog.V(3).Infof("Scanning with options: %+v", s.opts)
job, err := s.prepareJob(ctx, workload, spec)
if err != nil {
return nil, fmt.Errorf("preparing scan job: %w", err)
}

err = runner.New(scanJobRunnerTimeout).
Run(ctx, kube.NewRunnableJob(s.clientset, job))
err = runner.New().Run(ctx, kube.NewRunnableJob(s.clientset, job))
if err != nil {
return nil, fmt.Errorf("running scan job: %w", err)
}

defer func() {
klog.V(3).Infof("Deleting job: %s/%s", job.Namespace, job.Name)
klog.V(3).Infof("Deleting Job: %s/%s", job.Namespace, job.Name)
background := meta.DeletePropagationBackground
_ = s.clientset.BatchV1().Jobs(job.Namespace).Delete(ctx, job.Name, meta.DeleteOptions{
PropagationPolicy: &background,
})
}()

klog.V(3).Infof("Scan job completed: %s/%s", job.Namespace, job.Name)
klog.V(3).Infof("Scan Job completed: %s/%s", job.Namespace, job.Name)

job, err = s.clientset.BatchV1().Jobs(job.Namespace).Get(ctx, job.Name, meta.GetOptions{})
if err != nil {
Expand Down Expand Up @@ -183,7 +182,7 @@ func (s *scanner) prepareJob(ctx context.Context, workload kube.Workload, spec c
Spec: batch.JobSpec{
BackoffLimit: pointer.Int32Ptr(1),
Completions: pointer.Int32Ptr(1),
ActiveDeadlineSeconds: pointer.Int64Ptr(int64(scanJobRunnerTimeout.Seconds())),
ActiveDeadlineSeconds: s.GetActiveDeadlineSeconds(s.opts.ScanJobTimeout),
Template: core.PodTemplateSpec{
ObjectMeta: meta.ObjectMeta{
Labels: map[string]string{
Expand Down
6 changes: 6 additions & 0 deletions pkg/kube/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kube

import (
"fmt"
"time"
)

const (
Expand Down Expand Up @@ -91,3 +92,8 @@ func WorkloadKindFromString(s string) (WorkloadKind, error) {
}
return WorkloadKindUnknown, fmt.Errorf("unrecognized workload: %s", s)
}

// ScannerOpts holds configuration of the vulnerability Scanner.
type ScannerOpts struct {
ScanJobTimeout time.Duration
}
Loading

0 comments on commit 02c0c55

Please sign in to comment.