Author a ClusterImageTemplate for Supply Chain integration

This topic tells you how to create your own ClusterImageTemplate and customize the embedded ImageVulnerabilityScan to use the scanner of your choice.

Before you begin

Before authoring a ClusterImageTemplate for Supply Chain integration:

Create a ClusterImageTemplate

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:

  1. 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.

  2. 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.
  3. Create the ClusterImageTemplate by running:

    kubectl apply -f custom-ivs-template.yaml
    
  4. 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.

check-circle-line exclamation-circle-line close-line
Scroll to top icon