Skip to content

Commit

Permalink
feat: add support for k8s non-intrusive flag
Browse files Browse the repository at this point in the history
Signed-off-by: chenk <[email protected]>
  • Loading branch information
chen-keinan committed Mar 31, 2024
1 parent 71da44f commit fa55824
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 240 deletions.
2 changes: 1 addition & 1 deletion docs/docs/references/configuration/cli/trivy_kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg:
--cache-ttl duration cache TTL when using redis as cache backend
--clear-cache clear image caches without scanning
--compliance string compliance report to generate (k8s-nsa,k8s-cis,k8s-pss-baseline,k8s-pss-restricted)
--components strings specify which components to scan (workload,infra) (default [workload,infra])
--config-data strings specify paths from which data for the Rego policies will be recursively loaded
--config-policy strings specify the paths to the Rego policy files or to the directories containing them, applying config files
--context string specify a context to scan
Expand Down Expand Up @@ -91,6 +90,7 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg:
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip
--skip-intrusive When the flag is activated, the node-collector job will not be executed, thus skipping misconfiguration findings on the node.
--skip-java-db-update skip updating Java index database
--skip-policy-update skip fetching rego policy updates
-t, --template string output template
Expand Down
4 changes: 0 additions & 4 deletions integration/k8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ func TestK8s(t *testing.T) {
"5m0s",
"--format",
"json",
"--components",
"workload",
"--context",
"kind-kind-test",
"--output",
Expand Down Expand Up @@ -128,8 +126,6 @@ func TestK8s(t *testing.T) {
"5m0s",
"--format",
"json",
"--components",
"workload",
"--context",
"kind-kind-test",
"--output",
Expand Down
28 changes: 10 additions & 18 deletions pkg/flag/kubernetes_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ var (
ConfigName: "kubernetes.kubeconfig",
Usage: "specify the kubeconfig file path to use",
}
ComponentsFlag = Flag[[]string]{
Name: "components",
ConfigName: "kubernetes.components",
Default: []string{
"workload",
"infra",
},
Values: []string{
"workload",
"infra",
},
Usage: "specify which components to scan",
}
K8sVersionFlag = Flag[string]{
Name: "k8s-version",
ConfigName: "kubernetes.k8s.version",
Expand All @@ -58,6 +45,11 @@ var (
Shorthand: "A",
Usage: "fetch resources from all cluster namespaces",
}
SkipIntrusive = Flag[bool]{
Name: "skip-intrusive",
ConfigName: "kubernetes.non.intrusive",
Usage: "When the flag is activated, the node-collector job will not be executed, thus skipping misconfiguration findings on the node.",
}
NodeCollectorNamespace = Flag[string]{
Name: "node-collector-namespace",
ConfigName: "node.collector.namespace",
Expand Down Expand Up @@ -98,9 +90,9 @@ type K8sFlagGroup struct {
ClusterContext *Flag[string]
Namespace *Flag[string]
KubeConfig *Flag[string]
Components *Flag[[]string]
K8sVersion *Flag[string]
Tolerations *Flag[[]string]
SkipIntrusive *Flag[bool]
NodeCollectorImageRef *Flag[string]
AllNamespaces *Flag[bool]
NodeCollectorNamespace *Flag[string]
Expand All @@ -114,13 +106,13 @@ type K8sOptions struct {
ClusterContext string
Namespace string
KubeConfig string
Components []string
K8sVersion string
Tolerations []corev1.Toleration
NodeCollectorImageRef string
AllNamespaces bool
NodeCollectorNamespace string
ExcludeOwned bool
SkipIntrusive bool
ExcludeNodes map[string]string
QPS float32
Burst int
Expand All @@ -131,9 +123,9 @@ func NewK8sFlagGroup() *K8sFlagGroup {
ClusterContext: ClusterContextFlag.Clone(),
Namespace: K8sNamespaceFlag.Clone(),
KubeConfig: KubeConfigFlag.Clone(),
Components: ComponentsFlag.Clone(),
K8sVersion: K8sVersionFlag.Clone(),
Tolerations: TolerationsFlag.Clone(),
SkipIntrusive: SkipIntrusive.Clone(),
AllNamespaces: AllNamespaces.Clone(),
NodeCollectorNamespace: NodeCollectorNamespace.Clone(),
ExcludeOwned: ExcludeOwned.Clone(),
Expand All @@ -153,8 +145,8 @@ func (f *K8sFlagGroup) Flags() []Flagger {
f.ClusterContext,
f.Namespace,
f.KubeConfig,
f.Components,
f.K8sVersion,
f.SkipIntrusive,
f.Tolerations,
f.AllNamespaces,
f.NodeCollectorNamespace,
Expand Down Expand Up @@ -190,9 +182,9 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) {
ClusterContext: f.ClusterContext.Value(),
Namespace: f.Namespace.Value(),
KubeConfig: f.KubeConfig.Value(),
Components: f.Components.Value(),
K8sVersion: f.K8sVersion.Value(),
Tolerations: tolerations,
SkipIntrusive: f.SkipIntrusive.Value(),
AllNamespaces: f.AllNamespaces.Value(),
NodeCollectorNamespace: f.NodeCollectorNamespace.Value(),
ExcludeOwned: f.ExcludeOwned.Value(),
Expand Down
4 changes: 2 additions & 2 deletions pkg/flag/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,12 @@ func (o *Options) Align() {
}

// Vulnerability scanning is disabled by default for CycloneDX.
if o.Format == types.FormatCycloneDX && !viper.IsSet(ScannersFlag.ConfigName) && len(o.K8sOptions.Components) == 0 { // remove K8sOptions.Components validation check when vuln scan is supported for k8s report with cycloneDX
if o.Format == types.FormatCycloneDX && !viper.IsSet(ScannersFlag.ConfigName) { // remove K8sOptions.Components validation check when vuln scan is supported for k8s report with cycloneDX
log.Logger.Info(`"--format cyclonedx" disables security scanning. Specify "--scanners vuln" explicitly if you want to include vulnerabilities in the CycloneDX report.`)
o.Scanners = nil
}

if o.Format == types.FormatCycloneDX && len(o.K8sOptions.Components) > 0 {
if o.Format == types.FormatCycloneDX {
log.Logger.Info(`"k8s with --format cyclonedx" disable security scanning`)
o.Scanners = nil
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/k8s/commands/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package commands
import (
"context"

"golang.org/x/exp/slices"
"golang.org/x/xerrors"

k8sArtifacts "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
Expand All @@ -28,7 +27,7 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err
return xerrors.Errorf("get k8s artifacts with node info error: %w", err)
}
case types.FormatJSON, types.FormatTable:
if opts.Scanners.AnyEnabled(types.MisconfigScanner) && slices.Contains(opts.Components, "infra") {
if opts.Scanners.AnyEnabled(types.MisconfigScanner) && !opts.SkipIntrusive {
artifacts, err = trivyk8s.New(cluster, log.Logger, trivyk8s.WithExcludeOwned(opts.ExcludeOwned)).ListArtifactAndNodeInfo(ctx,
trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace),
trivyk8s.WithIgnoreLabels(opts.ExcludeNodes),
Expand Down
1 change: 0 additions & 1 deletion pkg/k8s/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er
Report: r.flagOpts.ReportFormat,
Output: output,
Severities: r.flagOpts.Severities,
Components: r.flagOpts.Components,
Scanners: r.flagOpts.ScanOptions.Scanners,
APIVersion: r.flagOpts.AppVersion,
}); err != nil {
Expand Down
28 changes: 12 additions & 16 deletions pkg/k8s/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ type Option struct {
Severities []dbTypes.Severity
ColumnHeading []string
Scanners types.Scanners
Components []string
APIVersion string
}

Expand Down Expand Up @@ -133,12 +132,12 @@ type reports struct {
// - misconfiguration report
// - rbac report
// - infra checks report
func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, components []string) []reports {
func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners) []reports {

var workloadMisconfig, infraMisconfig, rbacAssessment, workloadVulnerabilities, infraVulnerabilities, workloadResource []Resource
for _, resource := range k8sReport.Resources {
switch {
case vulnerabilitiesOrSecretResource(resource):
case vulnerabilitiesOrSecretResource(resource) && !infraResource(resource):
if resource.Namespace == infraNamespace || nodeInfoResource(resource) {
infraVulnerabilities = append(infraVulnerabilities, nodeKind(resource))
} else {
Expand All @@ -149,31 +148,29 @@ func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
case infraResource(resource):
infraMisconfig = append(infraMisconfig, nodeKind(resource))
case scanners.Enabled(types.MisconfigScanner) &&
!rbacResource(resource) &&
slices.Contains(components, workloadComponent):
!rbacResource(resource):
workloadMisconfig = append(workloadMisconfig, resource)
}
}

var r []reports
workloadResource = append(workloadResource, workloadVulnerabilities...)
workloadResource = append(workloadResource, workloadMisconfig...)
if shouldAddToReport(scanners, components, workloadComponent) {
if shouldAddToReport(scanners) {
workloadReport := Report{
SchemaVersion: 0,
ClusterName: k8sReport.ClusterName,
Resources: workloadResource,
name: "Workload Assessment",
}
if slices.Contains(components, workloadComponent) {
r = append(r, reports{
Report: workloadReport,
Columns: WorkloadColumns(),
})
}
r = append(r, reports{
Report: workloadReport,
Columns: WorkloadColumns(),
})

}
infraMisconfig = append(infraMisconfig, infraVulnerabilities...)
if shouldAddToReport(scanners, components, infraComponent) {
if shouldAddToReport(scanners) {
r = append(r, reports{
Report: Report{
SchemaVersion: 0,
Expand Down Expand Up @@ -265,12 +262,11 @@ func (r Report) PrintErrors() {
}
}

func shouldAddToReport(scanners types.Scanners, components []string, componentType string) bool {
func shouldAddToReport(scanners types.Scanners) bool {
return scanners.AnyEnabled(
types.MisconfigScanner,
types.VulnerabilityScanner,
types.SecretScanner) &&
slices.Contains(components, componentType)
types.SecretScanner)
}

func vulnerabilitiesOrSecretResource(resource Resource) bool {
Expand Down
32 changes: 13 additions & 19 deletions pkg/k8s/report/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,20 +515,15 @@ func Test_separateMisconfigReports(t *testing.T) {
name string
k8sReport Report
scanners types.Scanners
components []string
expectedReports []Report
}{
{
/*{
name: "Config, Rbac, and Infra Reports",
k8sReport: k8sReport,
scanners: types.Scanners{
types.MisconfigScanner,
types.RBACScanner,
},
components: []string{
workloadComponent,
infraComponent,
},
expectedReports: []Report{
// the order matter for the test
{
Expand All @@ -545,10 +540,6 @@ func Test_separateMisconfigReports(t *testing.T) {
name: "Config and Infra for the same resource",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{
workloadComponent,
infraComponent,
},
expectedReports: []Report{
// the order matter for the test
{
Expand All @@ -567,37 +558,40 @@ func Test_separateMisconfigReports(t *testing.T) {
expectedReports: []Report{
{Resources: []Resource{{Kind: "Role"}}},
},
},
},*/
{
name: "Config Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{workloadComponent},
name: "Config Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
expectedReports: []Report{
{
Resources: []Resource{
{Kind: "Deployment"},
{Kind: "StatefulSet"},
},
},
{
Resources: []Resource{
{Kind: "Pod"},
},
},
},
},
{
/* {
name: "Infra Report Only",
k8sReport: k8sReport,
scanners: types.Scanners{types.MisconfigScanner},
components: []string{infraComponent},
expectedReports: []Report{
{Resources: []Resource{{Kind: "Pod"}}},
},
},
},*/

// TODO: add vuln only
// TODO: add secret only
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reports := SeparateMisconfigReports(tt.k8sReport, tt.scanners, tt.components)
reports := SeparateMisconfigReports(tt.k8sReport, tt.scanners)
assert.Equal(t, len(tt.expectedReports), len(reports))

for i := range reports {
Expand Down
9 changes: 2 additions & 7 deletions pkg/k8s/report/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewSummaryWriter(output io.Writer, requiredSevs []dbTypes.Severity, columnH
}
}

func ColumnHeading(scanners types.Scanners, components, availableColumns []string) []string {
func ColumnHeading(scanners types.Scanners, availableColumns []string) []string {
columns := []string{
NamespaceColumn,
ResourceColumn,
Expand All @@ -47,12 +47,7 @@ func ColumnHeading(scanners types.Scanners, components, availableColumns []strin
case types.VulnerabilityScanner:
securityOptions[VulnerabilitiesColumn] = nil
case types.MisconfigScanner:
if slices.Contains(components, workloadComponent) {
securityOptions[MisconfigurationsColumn] = nil
}
if slices.Contains(components, infraComponent) {
securityOptions[MisconfigurationsColumn] = nil
}
securityOptions[MisconfigurationsColumn] = nil
case types.SecretScanner:
securityOptions[SecretsColumn] = nil
case types.RBACScanner:
Expand Down
Loading

0 comments on commit fa55824

Please sign in to comment.