This topic tells you how to create your own ClusterImageTemplate
and customize the embedded ImageVulnerabilityScan
to use the scanner of your choice.
Before authoring a ClusterImageTemplate
for Supply Chain integration:
ImageVulnerabilityScan
or configure one of the samples provided in Configure your custom ImageVulnerabilityScan. For more information, see ClusterImageTemplate
in the Cartographer documentation.ytt
templates. For more information and playground samples, see the Carvel ytt
documentation.This section describes how to create a ClusterImageTemplate
by using an ImageVulnerabilityScan
with Trivy. To use a different scanner, replace the embedded ImageVulnerabilityScan
with your own.
ClusterImageTemplate
parameters and values are described in this table:
Parameter or value | Description |
---|---|
spec.params |
spec.params in this sample YAML defines default values for fields within ImageVulnerabilityScan . |
#@ data.values.params |
#@ data.values.params in the ytt template block and in the ImageVulnerabilityScan both reference spec.params fields. |
data.values.workload |
The values in the data.values.workload , such as metadata , labels , annotations , and spec , come from the supply chain workload. |
data.values.image |
The data.values.image is the container image built from Buildpacks in the supply-chain step that scans for vulnerabilities. |
ytt |
The example YAML in this topic uses ytt to define a resource template written in ytt for the ImageVulnerabilityScan Custom Resource. The ytt template has defined functions that you can use within ImageVulnerabilityScan . |
For more information, see params
and ytt
in the in the Cartographer documentation.
To create a ClusterImageTemplate
:
Create a YAML file with the following content and name it custom-ivs-template.yaml
:
apiVersion: carto.run/v1alpha1
kind: ClusterImageTemplate
metadata:
name: image-vulnerability-scan-custom # input name of your ClusterImageTemplate
spec:
imagePath: .status.scannedImage
retentionPolicy:
maxFailedRuns: 10
maxSuccessfulRuns: 10
lifecycle: immutable
healthRule:
multiMatch:
healthy:
matchConditions:
- status: "True"
type: ScanCompleted
- status: "True"
type: Succeeded
unhealthy:
matchConditions:
- status: "False"
type: ScanCompleted
- status: "False"
type: Succeeded
params:
- name: image_scanning_workspace_size
default: 4Gi
- name: image_scanning_service_account_scanner
default: scanner
- name: image_scanning_service_account_publisher
default: publisher
- name: image_scanning_active_keychains
default: []
- name: image_scanning_workspace_bindings
default: []
- name: image_scanning_steps_env_vars
default: []
- name: trivy_db_repository
default: ghcr.io/aquasecurity/trivy-db
- name: trivy_java_db_repository
default: ghcr.io/aquasecurity/trivy-java-db
- name: registry
default:
server: REGISTRY-SERVER # input your registry server
repository: REGISTRY-REPOSITORY # input your registry repository
ytt: |
#@ load("@ytt:data", "data")
#@ load("@ytt:template", "template")
#@ def merge_labels(fixed_values):
#@ labels = {}
#@ if hasattr(data.values.workload.metadata, "labels"):
#@ exclusions = ["kapp.k14s.io/app", "kapp.k14s.io/association"]
#@ for k,v in dict(data.values.workload.metadata.labels).items():
#@ if k not in exclusions:
#@ labels[k] = v
#@ end
#@ end
#@ end
#@ labels.update(fixed_values)
#@ return labels
#@ end
#@ def scanResultsLocation():
#@ return "/".join([
#@ data.values.params.registry.server,
#@ data.values.params.registry.repository,
#@ "-".join([
#@ data.values.workload.metadata.name,
#@ data.values.workload.metadata.namespace,
#@ "scan-results",
#@ ])
#@ ]) + ":" + data.values.workload.metadata.uid
#@ end
#@ def param(key):
#@ if not key in data.values.params:
#@ return None
#@ end
#@ return data.values.params[key]
#@ end
#@ def maven_param(key):
#@ if not key in data.values.params["maven"]:
#@ return None
#@ end
#@ return data.values.params["maven"][key]
#@ end
#@ def maven_repository_url():
#@ if maven_param("repository") and "url" in maven_param("repository"):
#@ return maven_param("repository")["url"]
#@ elif param("maven_repository_url"):
#@ return param("maven_repository_url")
#@ else:
#@ return None
#@ end
#@ end
#@ def correlationId():
#@ if hasattr(data.values.workload, "annotations") and hasattr(data.values.workload.annotations, "apps.tanzu.vmware.com/correlationid"):
#@ return data.values.workload.annotations["apps.tanzu.vmware.com/correlationid"]
#@ end
#@ url = ""
#@ if hasattr(data.values.workload.spec, "source"):
#@ if hasattr(data.values.workload.spec.source, "git"):
#@ url = data.values.workload.spec.source.git.url
#@ elif hasattr(data.values.workload.spec.source, "image"):
#@ url = data.values.workload.spec.source.image.split("@")[0]
#@ end
#@ url = url + "?sub_path=" + getattr(data.values.workload.spec.source, "subPath", "/")
#@ end
#@ if param("maven"):
#@ url = maven_repository_url() + "/" + maven_param("groupId").replace(".", "/") + "/" + maven_param("artifactId")
#@ end
#@ if hasattr(data.values.workload.spec, "image"):
#@ url = data.values.workload.spec.image.split("@",1)[0]
#@ url = url.split(":",1)[0]
#@ end
#@ return url
#@ end
---
apiVersion: app-scanning.apps.tanzu.vmware.com/v1alpha1
kind: ImageVulnerabilityScan
metadata:
labels: #@ merge_labels({ "app.kubernetes.io/component": "image-scan" })
annotations:
apps.tanzu.vmware.com/correlationid: #@ correlationId()
app-scanning.apps.tanzu.vmware.com/scanner-name: Trivy
generateName: #@ data.values.workload.metadata.name + "-trivy-scan-"
spec:
image: #@ data.values.image
activeKeychains: #@ data.values.params.image_scanning_active_keychains
scanResults:
location: #@ scanResultsLocation()
workspace:
size: #@ data.values.params.image_scanning_workspace_size
#@ if/end data.values.params.image_scanning_workspace_bindings:
bindings: #@ data.values.params.image_scanning_workspace_bindings
serviceAccountNames:
scanner: #@ data.values.params.image_scanning_service_account_scanner
publisher: #@ data.values.params.image_scanning_service_account_publisher
steps:
- name: trivy-generate-report
image: TRIVY-SCANNER-IMAGE
env:
- name: TRIVY_DB_REPOSITORY
value: #@ data.values.params.trivy_db_repository
- name: TRIVY_JAVA_DB_REPOSITORY
value: #@ data.values.params.trivy_java_db_repository
- name: TRIVY_CACHE_DIR
value: /workspace/trivy-cache
- name: XDG_CACHE_HOME
value: /workspace/.cache
- name: TMPDIR
value: /workspace
- #@ template.replace(data.values.params.image_scanning_steps_env_vars)
args:
- image
- $(params.image)
- --exit-code=0
- --no-progress
- --scanners=vuln
- --format=cyclonedx
- --output=$(params.scan-results-path)/scan.cdx.json
Note
apps.tanzu.vmware.com/correlationid
contains the metadata of the mapping to the source of the scanned resource.
Edit the following in your custom-ivs-template.yaml
file:
.metadata.name
, which is the name of your ClusterImageTemplate
. Ensure that it does not conflict with the names of packaged templates. For more informatio, see Author your supply chains.REGISTRY-SERVER
, which is the registry server used for the scan results location.REGISTRY-REPOSITORY
, which is the registry repository used for the scan results location.TRIVY-SCANNER-IMAGE
, which is the location of your Trivy scanner CLI image..metadata.annotations.'app-scanning.apps.tanzu.vmware.com/scanner-name'
, which is the scanner image name reported in Tanzu Developer Portal.Create the ClusterImageTemplate
by running:
kubectl apply -f custom-ivs-template.yaml
Integrate your custom ClusterImageTemplate
with SCST - Scan 2.0. For more information, see Enable SCST - Scan 2.0 for default Test and Scan supply chains.