From 060d0bb6411b6ed5c004af80b29754c9313e20dc Mon Sep 17 00:00:00 2001 From: chenk Date: Sat, 27 Apr 2024 17:30:17 +0300 Subject: [PATCH] BREAKING: support exclude `kinds/namespaces` and include `kinds/namespaces` (#6323) Signed-off-by: chenk --- .../configuration/cli/trivy_kubernetes.md | 29 ++++--- go.mod | 22 ++--- go.sum | 40 ++++----- integration/k8s_test.go | 56 +------------ pkg/commands/app.go | 21 +++-- pkg/flag/kubernetes_flags.go | 82 +++++++++++-------- pkg/k8s/commands/cluster.go | 17 ++-- pkg/k8s/commands/namespace.go | 44 ---------- pkg/k8s/commands/namespace_test.go | 47 ----------- pkg/k8s/commands/resource.go | 73 ----------------- pkg/k8s/commands/resource_test.go | 61 -------------- pkg/k8s/commands/run.go | 44 +++------- 12 files changed, 129 insertions(+), 407 deletions(-) delete mode 100644 pkg/k8s/commands/namespace.go delete mode 100644 pkg/k8s/commands/namespace_test.go delete mode 100644 pkg/k8s/commands/resource.go delete mode 100644 pkg/k8s/commands/resource_test.go diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index a1befafeb50..7b7c91f3d88 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -2,32 +2,33 @@ [EXPERIMENTAL] Scan kubernetes cluster +### Synopsis + +Default context in kube configuration will be used unless specified + ``` -trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg: pods, pod/NAME } +trivy kubernetes [flags] [CONTEXT] ``` ### Examples ``` # cluster scanning - $ trivy k8s --report summary cluster - - # namespace scanning: - $ trivy k8s -n kube-system --report summary all + $ trivy k8s --report summary - # resources scanning: - $ trivy k8s --report=summary deploy - $ trivy k8s --namespace=kube-system --report=summary deploy,configmaps + # cluster scanning with specific namespace: + $ trivy k8s --include-namespaces kube-system --report summary - # resource scanning: - $ trivy k8s deployment/orion + # cluster with specific context: + $ trivy k8s kind-kind --report summary + + ``` ### Options ``` - -A, --all-namespaces fetch resources from all cluster namespaces --burst int specify the maximum burst for throttle (default 10) --cache-backend string cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend @@ -36,11 +37,12 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg: --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 --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --download-db-only download/update vulnerability database but don't run a scan --download-java-db-only download/update Java index database but don't run a scan + --exclude-kinds strings indicate the kinds exclude from scanning (example: node) + --exclude-namespaces strings indicate the namespaces excluded from scanning (example: kube-system) --exclude-nodes strings indicate the node labels that the node-collector job should exclude from scanning (example: kubernetes.io/arch:arm64,team:dev) --exclude-owned exclude resources that have an owner reference --exit-code int specify exit code when any security issues are found @@ -58,13 +60,14 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg: --ignore-unfixed display only fixed vulnerabilities --ignorefile string specify .trivyignore file (default ".trivyignore") --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) + --include-kinds strings indicate the kinds included in scanning (example: node) + --include-namespaces strings indicate the namespaces included in scanning (example: kube-system) --include-non-failures include successes and exceptions, available with '--scanners misconfig' --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --kubeconfig string specify the kubeconfig file path to use --list-all-pkgs enabling the option will output all packages regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) - -n, --namespace string specify a namespace to scan --no-progress suppress progress bar --node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.0.9") --node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp") diff --git a/go.mod b/go.mod index 463dccf9801..03b9790a1af 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/aquasecurity/trivy-aws v0.8.0 github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 - github.com/aquasecurity/trivy-kubernetes v0.6.3 + github.com/aquasecurity/trivy-kubernetes v0.6.6 github.com/aquasecurity/trivy-policies v0.10.0 github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/config v1.27.10 @@ -50,8 +50,8 @@ require ( github.com/go-openapi/strfmt v0.23.0 github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt v3.2.2+incompatible - github.com/golang/protobuf v1.5.3 - github.com/google/go-containerregistry v0.19.0 + github.com/golang/protobuf v1.5.4 + github.com/google/go-containerregistry v0.19.1 github.com/google/licenseclassifier/v2 v2.0.0 github.com/google/uuid v1.6.0 github.com/google/wire v0.6.0 @@ -105,7 +105,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/xlab/treeprint v1.2.0 go.etcd.io/bbolt v1.3.9 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/mod v0.16.0 golang.org/x/net v0.23.0 @@ -115,7 +115,7 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.29.1 + k8s.io/api v0.29.3 k8s.io/utils v0.0.0-20231127182322-b307cd553661 modernc.org/sqlite v1.29.7 ) @@ -177,7 +177,7 @@ require ( github.com/antchfx/xpath v1.2.3 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.49.21 // indirect + github.com/aws/aws-sdk-go v1.51.16 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect @@ -411,14 +411,14 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.29.0 // indirect - k8s.io/apimachinery v0.29.1 // indirect + k8s.io/apimachinery v0.29.3 // indirect k8s.io/apiserver v0.29.0 // indirect - k8s.io/cli-runtime v0.29.0 // indirect - k8s.io/client-go v0.29.0 // indirect - k8s.io/component-base v0.29.0 // indirect + k8s.io/cli-runtime v0.29.3 // indirect + k8s.io/client-go v0.29.3 // indirect + k8s.io/component-base v0.29.3 // indirect k8s.io/klog/v2 v2.120.0 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/kubectl v0.29.0 // indirect + k8s.io/kubectl v0.29.3 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.49.3 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index aa2ebaa4619..59e9518afb1 100644 --- a/go.sum +++ b/go.sum @@ -349,8 +349,8 @@ github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d h1:fjI9mkoTU github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d/go.mod h1:cj9/QmD9N3OZnKQMp+/DvdV+ym3HyIkd4e+F0ZM3ZGs= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= -github.com/aquasecurity/trivy-kubernetes v0.6.3 h1:Hmo0pefXRsyVYsii62WUQyt3xMHjm37ipPESeWM/LNA= -github.com/aquasecurity/trivy-kubernetes v0.6.3/go.mod h1:v6B8SO2ep718ccGbbjhpzMn6p27IijS+dMb+MeYz3jQ= +github.com/aquasecurity/trivy-kubernetes v0.6.6 h1:90Y3FH7Mrh+M06+RyLhl26HA06kWbhvTWwKWpt9jE0M= +github.com/aquasecurity/trivy-kubernetes v0.6.6/go.mod h1:+NJBTgQErUmq21Ag71q/EuXZKIP+/OJvBAR0G+YUkKo= github.com/aquasecurity/trivy-policies v0.10.0 h1:QONOsIFi6+WyB+7NGMBQeCgMFcRg6RV9dTBBpeOFDxU= github.com/aquasecurity/trivy-policies v0.10.0/go.mod h1:7WU0GTUqtQxqQ+FV3JAy7lskQQZU6lp7Mz1i8GEapFw= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -366,8 +366,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.49.21 h1:Rl8KW6HqkwzhATwvXhyr7vD4JFUMi7oXGAw9SrxxIFY= -github.com/aws/aws-sdk-go v1.49.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.51.16 h1:vnWKK8KjbftEkuPX8bRj3WHsLy1uhotn0eXptpvrxJI= +github.com/aws/aws-sdk-go v1.51.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= @@ -948,8 +948,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -981,8 +981,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw= -github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic= -github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= +github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= +github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -2476,32 +2476,32 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= -k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= -k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o= k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= -k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4= -k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk= +k8s.io/cli-runtime v0.29.3 h1:r68rephmmytoywkw2MyJ+CxjpasJDQY7AGc3XY2iv1k= +k8s.io/cli-runtime v0.29.3/go.mod h1:aqVUsk86/RhaGJwDhHXH0jcdqBrgdF3bZWk4Z9D4mkM= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= -k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s= -k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= +k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= +k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= @@ -2514,8 +2514,8 @@ k8s.io/klog/v2 v2.120.0/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/kubectl v0.29.0 h1:Oqi48gXjikDhrBF67AYuZRTcJV4lg2l42GmvsP7FmYI= -k8s.io/kubectl v0.29.0/go.mod h1:0jMjGWIcMIQzmUaMgAzhSELv5WtHo2a8pq67DtviAJs= +k8s.io/kubectl v0.29.3 h1:RuwyyIU42MAISRIePaa8Q7A3U74Q9P4MoJbDFz9o3us= +k8s.io/kubectl v0.29.3/go.mod h1:yCxfY1dbwgVdEt2zkJ6d5NNLOhhWgTyrqACIoFhpdd4= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= diff --git a/integration/k8s_test.go b/integration/k8s_test.go index 62a0bbd2d52..498f667932c 100644 --- a/integration/k8s_test.go +++ b/integration/k8s_test.go @@ -31,7 +31,7 @@ func TestK8s(t *testing.T) { "--cache-dir", cacheDir, "k8s", - "cluster", + "kind-kind-test", "--report", "summary", "-q", @@ -39,10 +39,6 @@ func TestK8s(t *testing.T) { "5m0s", "--format", "json", - "--components", - "workload", - "--context", - "kind-kind-test", "--output", outputFile, } @@ -79,12 +75,10 @@ func TestK8s(t *testing.T) { outputFile := filepath.Join(t.TempDir(), "output.json") osArgs := []string{ "k8s", - "cluster", + "kind-kind-test", "--format", "cyclonedx", "-q", - "--context", - "kind-kind-test", "--output", outputFile, } @@ -111,51 +105,5 @@ func TestK8s(t *testing.T) { assert.True(t, lo.SomeBy(*got.Dependencies, func(r cdx.Dependency) bool { return len(*r.Dependencies) > 0 })) - - }) - - t.Run("specific resource scan", func(t *testing.T) { - // Set up the output file - outputFile := filepath.Join(t.TempDir(), "output.json") - - osArgs := []string{ - "k8s", - "-n", - "default", - "deployments/nginx-deployment", - "-q", - "--timeout", - "5m0s", - "--format", - "json", - "--components", - "workload", - "--context", - "kind-kind-test", - "--output", - outputFile, - } - - // Run Trivy - err := execute(osArgs) - require.NoError(t, err) - - var got report.Report - f, err := os.Open(outputFile) - require.NoError(t, err) - defer f.Close() - - err = json.NewDecoder(f).Decode(&got) - require.NoError(t, err) - - // Flatten findings - results := lo.FlatMap(got.Resources, func(resource report.Resource, _ int) []types.Result { - return resource.Results - }) - - // Has vulnerabilities - assert.True(t, lo.SomeBy(results, func(r types.Result) bool { - return len(r.Vulnerabilities) > 0 - })) }) } diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 902b92b7808..05e14ea87f8 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -934,22 +934,21 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), } cmd := &cobra.Command{ - Use: "kubernetes [flags] { cluster | all | specific resources like kubectl. eg: pods, pod/NAME }", + Use: "kubernetes [flags] [CONTEXT]", Aliases: []string{"k8s"}, GroupID: groupScanning, Short: "[EXPERIMENTAL] Scan kubernetes cluster", + Long: `Default context in kube configuration will be used unless specified`, Example: ` # cluster scanning - $ trivy k8s --report summary cluster + $ trivy k8s --report summary - # namespace scanning: - $ trivy k8s -n kube-system --report summary all + # cluster scanning with specific namespace: + $ trivy k8s --include-namespaces kube-system --report summary - # resources scanning: - $ trivy k8s --report=summary deploy - $ trivy k8s --namespace=kube-system --report=summary deploy,configmaps - - # resource scanning: - $ trivy k8s deployment/orion + # cluster with specific context: + $ trivy k8s kind-kind --report summary + + `, PreRunE: func(cmd *cobra.Command, args []string) error { if err := k8sFlags.Bind(cmd); err != nil { @@ -1236,7 +1235,7 @@ func validateArgs(cmd *cobra.Command, args []string) error { return nil } - if len(args) == 0 && viper.GetString(flag.InputFlag.ConfigName) == "" { + if len(args) == 0 && viper.GetString(flag.InputFlag.ConfigName) == "" && cmd.Name() != "kubernetes" { if err := cmd.Help(); err != nil { return err } diff --git a/pkg/flag/kubernetes_flags.go b/pkg/flag/kubernetes_flags.go index a2f47ace08d..e325eb86104 100644 --- a/pkg/flag/kubernetes_flags.go +++ b/pkg/flag/kubernetes_flags.go @@ -10,20 +10,6 @@ import ( ) var ( - ClusterContextFlag = Flag[string]{ - Name: "context", - ConfigName: "kubernetes.context", - Usage: "specify a context to scan", - Aliases: []Alias{ - {Name: "ctx"}, - }, - } - K8sNamespaceFlag = Flag[string]{ - Name: "namespace", - ConfigName: "kubernetes.namespace", - Shorthand: "n", - Usage: "specify a namespace to scan", - } KubeConfigFlag = Flag[string]{ Name: "kubeconfig", ConfigName: "kubernetes.kubeconfig", @@ -52,12 +38,6 @@ var ( ConfigName: "kubernetes.tolerations", Usage: "specify node-collector job tolerations (example: key1=value1:NoExecute,key2=value2:NoSchedule)", } - AllNamespaces = Flag[bool]{ - Name: "all-namespaces", - ConfigName: "kubernetes.all-namespaces", - Shorthand: "A", - Usage: "fetch resources from all cluster namespaces", - } NodeCollectorNamespace = Flag[string]{ Name: "node-collector-namespace", ConfigName: "kubernetes.node-collector.namespace", @@ -80,6 +60,27 @@ var ( ConfigName: "kubernetes.exclude.nodes", Usage: "indicate the node labels that the node-collector job should exclude from scanning (example: kubernetes.io/arch:arm64,team:dev)", } + + ExcludeKinds = Flag[[]string]{ + Name: "exclude-kinds", + ConfigName: "kubernetes.excludeKinds", + Usage: "indicate the kinds exclude from scanning (example: node)", + } + IncludeKinds = Flag[[]string]{ + Name: "include-kinds", + ConfigName: "kubernetes.includeKinds", + Usage: "indicate the kinds included in scanning (example: node)", + } + ExcludeNamespaces = Flag[[]string]{ + Name: "exclude-namespaces", + ConfigName: "kubernetes.excludeNamespaces", + Usage: "indicate the namespaces excluded from scanning (example: kube-system)", + } + IncludeNamespaces = Flag[[]string]{ + Name: "include-namespaces", + ConfigName: "kubernetes.includeNamespaces", + Usage: "indicate the namespaces included in scanning (example: kube-system)", + } QPS = Flag[float64]{ Name: "qps", ConfigName: "kubernetes.qps", @@ -95,49 +96,52 @@ var ( ) type K8sFlagGroup struct { - ClusterContext *Flag[string] - Namespace *Flag[string] KubeConfig *Flag[string] Components *Flag[[]string] K8sVersion *Flag[string] Tolerations *Flag[[]string] NodeCollectorImageRef *Flag[string] - AllNamespaces *Flag[bool] NodeCollectorNamespace *Flag[string] ExcludeOwned *Flag[bool] ExcludeNodes *Flag[[]string] + ExcludeKinds *Flag[[]string] + IncludeKinds *Flag[[]string] + ExcludeNamespaces *Flag[[]string] + IncludeNamespaces *Flag[[]string] QPS *Flag[float64] Burst *Flag[int] } type K8sOptions struct { - ClusterContext string - Namespace string KubeConfig string Components []string K8sVersion string Tolerations []corev1.Toleration NodeCollectorImageRef string - AllNamespaces bool NodeCollectorNamespace string ExcludeOwned bool ExcludeNodes map[string]string + ExcludeKinds []string + IncludeKinds []string + ExcludeNamespaces []string + IncludeNamespaces []string QPS float32 Burst int } func NewK8sFlagGroup() *K8sFlagGroup { return &K8sFlagGroup{ - ClusterContext: ClusterContextFlag.Clone(), - Namespace: K8sNamespaceFlag.Clone(), KubeConfig: KubeConfigFlag.Clone(), Components: ComponentsFlag.Clone(), K8sVersion: K8sVersionFlag.Clone(), Tolerations: TolerationsFlag.Clone(), - AllNamespaces: AllNamespaces.Clone(), NodeCollectorNamespace: NodeCollectorNamespace.Clone(), ExcludeOwned: ExcludeOwned.Clone(), ExcludeNodes: ExcludeNodes.Clone(), + ExcludeKinds: ExcludeKinds.Clone(), + IncludeKinds: IncludeKinds.Clone(), + ExcludeNamespaces: ExcludeNamespaces.Clone(), + IncludeNamespaces: IncludeNamespaces.Clone(), NodeCollectorImageRef: NodeCollectorImageRef.Clone(), QPS: QPS.Clone(), Burst: Burst.Clone(), @@ -150,17 +154,18 @@ func (f *K8sFlagGroup) Name() string { func (f *K8sFlagGroup) Flags() []Flagger { return []Flagger{ - f.ClusterContext, - f.Namespace, f.KubeConfig, f.Components, f.K8sVersion, f.Tolerations, - f.AllNamespaces, f.NodeCollectorNamespace, f.ExcludeOwned, f.ExcludeNodes, f.NodeCollectorImageRef, + f.ExcludeKinds, + f.IncludeKinds, + f.ExcludeNamespaces, + f.IncludeNamespaces, f.QPS, f.Burst, } @@ -185,20 +190,27 @@ func (f *K8sFlagGroup) ToOptions() (K8sOptions, error) { } exludeNodeLabels[excludeNodeParts[0]] = excludeNodeParts[1] } + if len(f.ExcludeNamespaces.Value()) > 0 && len(f.IncludeNamespaces.Value()) > 0 { + return K8sOptions{}, fmt.Errorf("include-namespaces and exclude-namespaces flags cannot be used together") + } + if len(f.ExcludeKinds.Value()) > 0 && len(f.IncludeKinds.Value()) > 0 { + return K8sOptions{}, fmt.Errorf("include-kinds and exclude-kinds flags cannot be used together") + } return K8sOptions{ - ClusterContext: f.ClusterContext.Value(), - Namespace: f.Namespace.Value(), KubeConfig: f.KubeConfig.Value(), Components: f.Components.Value(), K8sVersion: f.K8sVersion.Value(), Tolerations: tolerations, - AllNamespaces: f.AllNamespaces.Value(), NodeCollectorNamespace: f.NodeCollectorNamespace.Value(), ExcludeOwned: f.ExcludeOwned.Value(), ExcludeNodes: exludeNodeLabels, NodeCollectorImageRef: f.NodeCollectorImageRef.Value(), QPS: float32(f.QPS.Value()), + ExcludeKinds: f.ExcludeKinds.Value(), + IncludeKinds: f.IncludeKinds.Value(), + ExcludeNamespaces: f.ExcludeNamespaces.Value(), + IncludeNamespaces: f.IncludeNamespaces.Value(), Burst: f.Burst.Value(), }, nil } diff --git a/pkg/k8s/commands/cluster.go b/pkg/k8s/commands/cluster.go index b4ad9be7834..bc985bff739 100644 --- a/pkg/k8s/commands/cluster.go +++ b/pkg/k8s/commands/cluster.go @@ -3,7 +3,6 @@ package commands import ( "context" - "go.uber.org/zap" "golang.org/x/exp/slices" "golang.org/x/xerrors" @@ -16,9 +15,6 @@ import ( // clusterRun runs scan on kubernetes cluster func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) error { - // TODO: replace with log.Logger - logger, _ := zap.NewProduction() - if err := validateReportArguments(opts); err != nil { return err } @@ -26,13 +22,20 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err var err error switch opts.Format { case types.FormatCycloneDX: - artifacts, err = trivyk8s.New(cluster, logger.Sugar()).ListClusterBomInfo(ctx) + artifacts, err = trivyk8s.New(cluster).ListClusterBomInfo(ctx) if err != nil { return xerrors.Errorf("get k8s artifacts with node info error: %w", err) } case types.FormatJSON, types.FormatTable: + k8sOpts := []trivyk8s.K8sOption{ + trivyk8s.WithExcludeNamespaces(opts.ExcludeNamespaces), + trivyk8s.WithIncludeNamespaces(opts.IncludeNamespaces), + trivyk8s.WithExcludeKinds(opts.ExcludeKinds), + trivyk8s.WithIncludeKinds(opts.IncludeKinds), + trivyk8s.WithExcludeOwned(opts.ExcludeOwned), + } if opts.Scanners.AnyEnabled(types.MisconfigScanner) && slices.Contains(opts.Components, "infra") { - artifacts, err = trivyk8s.New(cluster, logger.Sugar(), trivyk8s.WithExcludeOwned(opts.ExcludeOwned)).ListArtifactAndNodeInfo(ctx, + artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifactAndNodeInfo(ctx, trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace), trivyk8s.WithIgnoreLabels(opts.ExcludeNodes), trivyk8s.WithScanJobImageRef(opts.NodeCollectorImageRef), @@ -41,7 +44,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) } } else { - artifacts, err = trivyk8s.New(cluster, logger.Sugar()).ListArtifacts(ctx) + artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifacts(ctx) if err != nil { return xerrors.Errorf("get k8s artifacts error: %w", err) } diff --git a/pkg/k8s/commands/namespace.go b/pkg/k8s/commands/namespace.go deleted file mode 100644 index b6c85e8dae0..00000000000 --- a/pkg/k8s/commands/namespace.go +++ /dev/null @@ -1,44 +0,0 @@ -package commands - -import ( - "context" - - "go.uber.org/zap" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy-kubernetes/pkg/k8s" - "github.com/aquasecurity/trivy-kubernetes/pkg/trivyk8s" - "github.com/aquasecurity/trivy/pkg/flag" -) - -// namespaceRun runs scan on kubernetes cluster -func namespaceRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) error { - // TODO: replace with slog.Logger - logger, _ := zap.NewProduction() - - if err := validateReportArguments(opts); err != nil { - return err - } - var trivyk trivyk8s.TrivyK8S - if opts.AllNamespaces { - trivyk = trivyk8s.New(cluster, logger.Sugar()).AllNamespaces() - } else { - trivyk = trivyk8s.New(cluster, logger.Sugar()).Namespace(getNamespace(opts, cluster.GetCurrentNamespace())) - } - - artifacts, err := trivyk.ListArtifacts(ctx) - if err != nil { - return xerrors.Errorf("get k8s artifacts error: %w", err) - } - - runner := newRunner(opts, cluster.GetCurrentContext()) - return runner.run(ctx, artifacts) -} - -func getNamespace(opts flag.Options, currentNamespace string) string { - if opts.K8sOptions.Namespace != "" { - return opts.K8sOptions.Namespace - } - - return currentNamespace -} diff --git a/pkg/k8s/commands/namespace_test.go b/pkg/k8s/commands/namespace_test.go deleted file mode 100644 index 661360d7007..00000000000 --- a/pkg/k8s/commands/namespace_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package commands - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/aquasecurity/trivy/pkg/flag" -) - -func Test_getNamespace(t *testing.T) { - - tests := []struct { - name string - currentNamespace string - opts flag.Options - want string - }{ - { - name: "--namespace=custom", - currentNamespace: "default", - opts: flag.Options{ - K8sOptions: flag.K8sOptions{ - Namespace: "custom", - }, - }, - want: "custom", - }, - { - name: "no namespaces passed", - currentNamespace: "default", - opts: flag.Options{ - K8sOptions: flag.K8sOptions{ - Namespace: "", - }, - }, - want: "default", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := getNamespace(test.opts, test.currentNamespace) - assert.Equal(t, test.want, got) - }) - } -} diff --git a/pkg/k8s/commands/resource.go b/pkg/k8s/commands/resource.go deleted file mode 100644 index 1662fe25d4d..00000000000 --- a/pkg/k8s/commands/resource.go +++ /dev/null @@ -1,73 +0,0 @@ -package commands - -import ( - "context" - "strings" - - "go.uber.org/zap" - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" - "github.com/aquasecurity/trivy-kubernetes/pkg/k8s" - "github.com/aquasecurity/trivy-kubernetes/pkg/trivyk8s" - "github.com/aquasecurity/trivy/pkg/flag" -) - -// resourceRun runs scan on kubernetes cluster -func resourceRun(ctx context.Context, args []string, opts flag.Options, cluster k8s.Cluster) error { - kind, name, err := extractKindAndName(args) - if err != nil { - return err - } - - runner := newRunner(opts, cluster.GetCurrentContext()) - - var trivyk trivyk8s.TrivyK8S - - // TODO: replace with slog.Logger - logger, _ := zap.NewProduction() - trivyk = trivyk8s.New(cluster, logger.Sugar(), trivyk8s.WithExcludeOwned(opts.ExcludeOwned)) - - if opts.AllNamespaces { - trivyk = trivyk.AllNamespaces() - } else { - trivyk = trivyk.Namespace(getNamespace(opts, cluster.GetCurrentNamespace())) - } - - if name == "" { // pods or configmaps etc - if err = validateReportArguments(opts); err != nil { - return err - } - - targets, err := trivyk.Resources(kind).ListArtifacts(ctx) - if err != nil { - return err - } - - return runner.run(ctx, targets) - } - - // pod/NAME or pod NAME etc - artifact, err := trivyk.GetArtifact(ctx, kind, name) - if err != nil { - return err - } - - return runner.run(ctx, []*artifacts.Artifact{artifact}) -} - -func extractKindAndName(args []string) (string, string, error) { - switch len(args) { - case 1: - s := strings.Split(args[0], "/") - if len(s) != 2 { - return args[0], "", nil - } - - return s[0], s[1], nil - case 2: - return args[0], args[1], nil - } - - return "", "", xerrors.Errorf("can't parse arguments %v. Please run `trivy k8s` for usage.", args) -} diff --git a/pkg/k8s/commands/resource_test.go b/pkg/k8s/commands/resource_test.go deleted file mode 100644 index 27ce9df5d42..00000000000 --- a/pkg/k8s/commands/resource_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package commands - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_extractKindAndName(t *testing.T) { - tests := []struct { - name string - args []string - expectedKind string - expectedName string - expectedError string - }{ - { - name: "one argument only", - args: []string{"deploy"}, - expectedKind: "deploy", - expectedName: "", - }, - { - name: "one argument only, multiple targets", - args: []string{"deploy,configmaps"}, - expectedKind: "deploy,configmaps", - expectedName: "", - }, - { - name: "bar separated", - args: []string{"deploy/orion"}, - expectedKind: "deploy", - expectedName: "orion", - }, - { - name: "space separated", - args: []string{"deploy", "lua"}, - expectedKind: "deploy", - expectedName: "lua", - }, - { - name: "multiple arguments separated", - args: []string{"test", "test", "test"}, - expectedError: "can't parse arguments [test test test]. Please run `trivy k8s` for usage.", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - kind, name, err := extractKindAndName(test.args) - - if len(test.expectedError) > 0 { - assert.Error(t, err, test.expectedError) - return - } - - assert.Equal(t, test.expectedKind, kind) - assert.Equal(t, test.expectedName, name) - }) - } -} diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go index 567c63fa461..179853f9912 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -20,48 +20,30 @@ import ( "github.com/aquasecurity/trivy/pkg/types" ) -const ( - clusterArtifact = "cluster" - allArtifact = "all" -) - // Run runs a k8s scan func Run(ctx context.Context, args []string, opts flag.Options) error { - ctx, cancel := context.WithTimeout(ctx, opts.Timeout) - defer cancel() - - ctx = log.WithContextPrefix(ctx, "k8s") - - cluster, err := k8s.GetCluster( - k8s.WithContext(opts.K8sOptions.ClusterContext), + clusterOptions := []k8s.ClusterOption{ k8s.WithKubeConfig(opts.K8sOptions.KubeConfig), k8s.WithBurst(opts.K8sOptions.Burst), k8s.WithQPS(opts.K8sOptions.QPS), - ) + } + if len(args) > 0 { + clusterOptions = append(clusterOptions, k8s.WithContext(args[0])) + } + cluster, err := k8s.GetCluster(clusterOptions...) if err != nil { return xerrors.Errorf("failed getting k8s cluster: %w", err) } + ctx, cancel := context.WithTimeout(ctx, opts.Timeout) defer func() { + cancel() if errors.Is(err, context.DeadlineExceeded) { - log.Warn("Increase --timeout value") + log.WarnContext(ctx, "Increase --timeout value") } }() opts.K8sVersion = cluster.GetClusterVersion() - switch args[0] { - case clusterArtifact: - return clusterRun(ctx, opts, cluster) - case allArtifact: - if opts.Format == types.FormatCycloneDX { - return xerrors.Errorf("KBOM with CycloneDX format is not supported for all namespace scans") - } - return namespaceRun(ctx, opts, cluster) - default: // resourceArtifact - if opts.Format == types.FormatCycloneDX { - return xerrors.Errorf("KBOM with CycloneDX format is not supported for resource scans") - } - return resourceRun(ctx, args, opts, cluster) - } + return clusterRun(ctx, opts, cluster) } type runner struct { @@ -71,8 +53,8 @@ type runner struct { func newRunner(flagOpts flag.Options, cluster string) *runner { return &runner{ - flagOpts: flagOpts, - cluster: cluster, + flagOpts, + cluster, } } @@ -86,7 +68,7 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er } defer func() { if err := runner.Close(ctx); err != nil { - log.ErrorContext(ctx, "Failed to close runner", log.Err(err)) + log.ErrorContext(ctx, "failed to close runner: %s", err) } }()