diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index a1befafeb504..7b7c91f3d88e 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 0e585260a019..8a8f43c2b8e3 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,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-0.20240408130849-81b7dc338371 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 @@ -48,8 +48,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 @@ -114,7 +114,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.28.0 ) @@ -175,7 +175,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 @@ -410,14 +410,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 lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/go.sum b/go.sum index 1f149fc1b692..aeafbace2bd7 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-0.20240408130849-81b7dc338371 h1:xkZYk3CoDkVz1iuky5iHAQG1NePsXYwbLX5SZ6t6uZQ= +github.com/aquasecurity/trivy-kubernetes v0.6.6-0.20240408130849-81b7dc338371/go.mod h1:RMtsv9cheb86EmG97CtKSrqC0/Hk33jHLLjo7KYXXFQ= 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= @@ -2477,32 +2477,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= @@ -2515,8 +2515,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 62a0bbd2d526..498f667932c4 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 41d1d2ff645d..57fe4f94f9ac 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -936,22 +936,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 { @@ -1238,7 +1237,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 7a87040ba698..f9ebc0822fe4 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: "node.collector.namespace", @@ -74,6 +54,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.exclude.kinds", + Usage: "indicate the kinds exclude from scanning (example: node)", + } + IncludeKinds = Flag[[]string]{ + Name: "include-kinds", + ConfigName: "kubernetes.include.kinds", + Usage: "indicate the kinds included in scanning (example: node)", + } + ExcludeNamespaces = Flag[[]string]{ + Name: "exclude-namespaces", + ConfigName: "kubernetes.exclude.namespaces", + Usage: "indicate the namespaces excluded from scanning (example: kube-system)", + } + IncludeNamespaces = Flag[[]string]{ + Name: "include-namespaces", + ConfigName: "kubernetes.include.namespaces", + Usage: "indicate the namespaces included in scanning (example: kube-system)", + } NodeCollectorImageRef = Flag[string]{ Name: "node-collector-imageref", ConfigName: "kubernetes.node.collector.imageref", @@ -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 bf28f26f5d7f..3d3e9b1e76d3 100644 --- a/pkg/k8s/commands/cluster.go +++ b/pkg/k8s/commands/cluster.go @@ -28,8 +28,15 @@ 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: + 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, log.Logger, trivyk8s.WithExcludeOwned(opts.ExcludeOwned)).ListArtifactAndNodeInfo(ctx, + artifacts, err = trivyk8s.New(cluster, log.Logger, k8sOpts...).ListArtifactAndNodeInfo(ctx, trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace), trivyk8s.WithIgnoreLabels(opts.ExcludeNodes), trivyk8s.WithScanJobImageRef(opts.NodeCollectorImageRef), @@ -38,7 +45,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, log.Logger).ListArtifacts(ctx) + artifacts, err = trivyk8s.New(cluster, log.Logger, 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 a748bf38dd9d..000000000000 --- a/pkg/k8s/commands/namespace.go +++ /dev/null @@ -1,41 +0,0 @@ -package commands - -import ( - "context" - - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy-kubernetes/pkg/k8s" - "github.com/aquasecurity/trivy-kubernetes/pkg/trivyk8s" - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aquasecurity/trivy/pkg/log" -) - -// namespaceRun runs scan on kubernetes cluster -func namespaceRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) error { - if err := validateReportArguments(opts); err != nil { - return err - } - var trivyk trivyk8s.TrivyK8S - if opts.AllNamespaces { - trivyk = trivyk8s.New(cluster, log.Logger).AllNamespaces() - } else { - trivyk = trivyk8s.New(cluster, log.Logger).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 len(opts.K8sOptions.Namespace) > 0 { - 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 661360d7007b..000000000000 --- 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 10557e5a62f0..000000000000 --- a/pkg/k8s/commands/resource.go +++ /dev/null @@ -1,71 +0,0 @@ -package commands - -import ( - "context" - "strings" - - "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" - "github.com/aquasecurity/trivy/pkg/log" -) - -// 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 - - trivyk = trivyk8s.New(cluster, log.Logger, 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 27ce9df5d42f..000000000000 --- 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 e9e3510f6bce..54be5362637f 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -20,19 +20,17 @@ 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 { - 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) } @@ -45,20 +43,7 @@ func Run(ctx context.Context, args []string, opts flag.Options) error { } }() 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 {