This topic tells you how to create your own ClusterImageTemplate and customize the embedded ImageVulnerabilityScan to use the scanner of your choice.
The following prerequisite is required to author a ClusterImageTemplate for Supply Chain integration:
ytt
templates. See Carvel ytt
docs for documentation and playground samples.This section describes how to create a ClusterImageTemplate using an ImageVulnerabilityScan with Trivy. To use a different scanner, replace the embedded ImageVulnerabilityScan with your own.
ClusterImageTemplate parameters and values:
spec.params
in this sample YAML define default values for fields within the ImageVulnerabilityScan. For more information, see params
in the Cartographer documentation.
#@ data.values.params
in the ytt
template block and in the ImageVulnerabilityScan both reference spec.params
fields.data.values.workload
such as metadata
, labels
, annotations
, and spec
come from the supply chain workload.data.values.image
is the container image built from Buildpacks in the supply chain step that scans for vulnerabilities.ytt
to define a resource template written in ytt
for the ImageVulnerabilityScan Custom Resource. For more information, see ytt
in the Cartographer documentation. For example, inside the ytt
template are defined functions that you can use within the ImageVulnerabilityScan.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
is the name of your ClusterImageTemplate. Ensure that it does not conflict with the names of packaged templates. See Author your supply chains.REGISTRY-SERVER
is the registry server used for the scan results location.REGISTRY-REPOSITORY
is the registry repository used for the scan results location.TRIVY-SCANNER-IMAGE
is the location of your Trivy scanner CLI image.metadata.annotations.'app-scanning.apps.tanzu.vmware.com/scanner-name'
is the scanner image name reported in the Tanzu Developer Portal, formerly Tanzu Application Platform GUI.Create the ClusterImageTemplate:
kubectl apply -f custom-ivs-template.yaml
After you create your custom ClusterImageTemplate, you can integrate it with SCST - Scan 2.0. See Add App Scanning to default Test and Scan supply chains.