TAP Part 7 – Add Testing and Security Scanning to the Application

This is the last part of TAP blog post and I am sure that you have enjoyed reading the first 6 parts and learned something new. Feel free to comment on the post to provide your feedback and also let me know if you have any other question, will try helping you.

In this blog post, I will talk about adding testing and security components in the supply chain. So far, we have been looking at just the deployment part and pretty much TBS was doing the work but now we will look at Tekton, Grype packages too.

Before we start working on the practical part, Let’s understand the different supply chain profiles available with TAP as of today. “Honestly, I don’t know how to have our own custom profile, but i will work on it and let you know later.”

Out of the Box Basic Supply Chain

Diagram depicting the Source-to-URL chain: Watch Repo (Flux) to Build Image (TBS) to Apply Conventions to Deploy to Cluster (CNR).

When we installed TAP with Full profile, Basic profile was installed automatically and that you can find in tap-values.yaml file too. Look for the below section.

As you can see, In the basic supply chain, there are two steps before application is deployed. We have been dealing with those so far.

Out of the Box with Testing Supply Chain

Diagram depicting the Source-and-Test-to-URL chain: Watch Repo (Flux) to Test Code (Tekton) to Build Image (TBS) to Apply Conventions to Deploy to Cluster (CNR).

In this profile, we can bring up another hook for Test Code. By default, Tekton is used but you can have other tools too. I do not have supported list yet but will get that for you as soon i get.

Before we can use the Testing supply chain, we need to update the tap package. Let’s update the package now and include testing supply chain.

– Edit tap-values.yaml file and include the following changes.

supply_chain: testing

ootb_supply_chain_testing:
  registry:
    server: "<replace the registry server>"
    repository: "build-service"

– Now, update the tap package by running the following command.

$ tanzu package installed update tap  --package-name tap.tanzu.vmware.com  --version 0.4.0 -n tap-install  -f tap-values.yml
| Updating installed package 'tap' 
- Getting package install for 'tap' I1229 15:43:40.593130   22917 request.go:665] Waited for 1.045695997s due to client-side throttling, not priority and fairness, request: GET:https://F971DC6D64DE4B4EE58DC3CDF5C2A68A.gr7.us-east-2.eks.amazonaws.com/apis/kpack.io/v1alpha2?timeout=32s
\ Getting package install for 'tap' 
| Getting package metadata for 'tap.tanzu.vmware.com' 
| Updating secret 'tap-tap-install-values' 
| Updating package install for 'tap' 
- Waiting for 'PackageInstall' reconciliation for 'tap' 

Updated installed package 'tap' in namespace 'tap-install'

Let’s test the testing supply chain

– Create the Tekton pipeline for testing the application.

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: developer-defined-tekton-pipeline
  labels:
    apps.tanzu.vmware.com/pipeline: test     # (!) required
spec:
  params:
    - name: source-url                       # (!) required
    - name: source-revision                  # (!) required
  tasks:
    - name: test
      params:
        - name: source-url
          value: $(params.source-url)
        - name: source-revision
          value: $(params.source-revision)
      taskSpec:
        params:
          - name: source-url
          - name: source-revision
        steps:
          - name: test
            image: gradle
            script: |-
              cd `mktemp -d`

              wget -qO- $(params.source-url) | tar xvz
              ./mvnw test

– Save the content in a yaml file and apply it on a k8s cluster.

$ k apply -f pipeline.yaml 
pipeline.tekton.dev/developer-defined-tekton-pipeline created

– Deploy the application by running the following command.

tanzu apps workload create tanzu-java-web-app \
> --git-repo https://github.com/sample-accelerators/tanzu-java-web-app \
> --git-branch main \
> --type web \
> --label apps.tanzu.vmware.com/has-tests=true \
> --yes
Create workload:
      1 + |---
      2 + |apiVersion: carto.run/v1alpha1
      3 + |kind: Workload
      4 + |metadata:
      5 + |  labels:
      6 + |    apps.tanzu.vmware.com/has-tests: "true"
      7 + |    apps.tanzu.vmware.com/workload-type: web
      8 + |  name: tanzu-java-web-app
      9 + |  namespace: default
     10 + |spec:
     11 + |  source:
     12 + |    git:
     13 + |      ref:
     14 + |        branch: main
     15 + |      url: https://github.com/sample-accelerators/tanzu-java-web-app

Created workload "tanzu-java-web-app"

– Now notice the new object, Pipeline Run

kubectl get workload,gitrepository,pipelinerun,images.kpack,podintent,app,services.serving
NAME                                    AGE
workload.carto.run/tanzu-java-web-app   11s

NAME                                                        URL                                                         READY   STATUS                                                            AGE
gitrepository.source.toolkit.fluxcd.io/tanzu-java-web-app   https://github.com/sample-accelerators/tanzu-java-web-app   True    Fetched revision: main/ee4bcbc5bb3c0cb8b9b7db06d0a83b50ee7ed231   8s

NAME                                              SUCCEEDED   REASON    STARTTIME   COMPLETIONTIME
pipelinerun.tekton.dev/tanzu-java-web-app-n2lmg   Unknown     Running   1s        

– Wait for few mins to complete the pipeline run.

– Once testing is completed, you will see the build part will start and finally application is deployed. Run the below command to get the other objects.

$ kubectl get workload,gitrepository,pipelinerun,images.kpack,podintent,app,services.serving
NAME                                    AGE
workload.carto.run/tanzu-java-web-app   17s

NAME                                                        URL                                                         READY   STATUS                                                            AGE
gitrepository.source.toolkit.fluxcd.io/tanzu-java-web-app   https://github.com/sample-accelerators/tanzu-java-web-app   True    Fetched revision: main/ee4bcbc5bb3c0cb8b9b7db06d0a83b50ee7ed231   14s

NAME                                              SUCCEEDED   REASON    STARTTIME   COMPLETIONTIME
pipelinerun.tekton.dev/tanzu-java-web-app-n2lmg   Unknown     Running   7s          

NAME                                      DESCRIPTION           SINCE-DEPLOY   AGE
app.kappctrl.k14s.io/tanzu-java-web-app   Reconcile succeeded   4s             4s

NAME                                             URL                                             LATESTCREATED              LATESTREADY   READY     REASON
service.serving.knative.dev/tanzu-java-web-app   http://tanzu-java-web-app.default.example.com   tanzu-java-web-app-00001                 Unknown   RevisionMissing

– If you are interested in seeing the ran test, view the pod logs. e.g. below is the pod log

$ k logs tanzu-java-web-app-crxhl-test-pod

------truncated output-----
viewResolver
welcomePageHandlerMapping
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.185 s - in com.example.springboot.HelloControllerTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  26.457 s
[INFO] Finished at: 2021-12-28T06:13:05Z
[INFO] ------------------------------------------------------------------------

So now we understood that how to bring Testing supply chain and leverage Tekton to test the code.

Out of the Box with Testing and Scanning Supply Chain

Diagram depicting the Source-and-Test-to-URL chain: Watch Repo (Flux) to Test Code (Tekton) to Build Image (TBS) to Apply Conventions to Deploy to Cluster (CNR).
Source -> Above images are copied from TAP documentation.

In the Testing supply chain profile, we understood that how to include Testing using Tekton. In this profile, there is scanning using Grype included too. As you notice in the above picture, there is Source code scanning and then container image scanning where grype is used. Since I have TAP Full profile installation is done, So I have all the packages running and we will update the tap package to include the new profile.

– Edit the tap-values.yaml file and include the below detail.

supply_chain: testing_scanning

ootb_supply_chain_testing_scanning:
  registry:
    server: "<replace the registry server>"
    repository: "build-service"

– Save the details and run the update command for tanzu package update.

$ tanzu package installed update tap  --package-name tap.tanzu.vmware.com  --version 0.4.0 -n tap-install  -f tap-values.yml

– During installation of the Grype Scanner as part of TAP, sample ScanTemplates are installed into the default namespace. If the workload is deployed into another namespace, these sample ScanTemplates also need to be present in the other namespace. View the default scan templates.

$ k get scantemplate
NAME                           AGE
blob-source-scan-template      25h
private-image-scan-template    25h
private-source-scan-template   25h
public-image-scan-template     25h
public-source-scan-template    25h

– Now, we need to create a Scan Policy for source code and container image.

$ kubectl apply -f - -o yaml << EOF
> ---
> apiVersion: scst-scan.apps.tanzu.vmware.com/v1alpha1
> kind: ScanPolicy
> metadata:
>   name: scan-policy
> spec:
>   regoFile: |
>     package policies
> 
>     default isCompliant = false
> 
>     # Accepted Values: "Critical", "High", "Medium", "Low", "Negligible", "UnknownSeverity"
>     violatingSeverities := ["Critical","High","UnknownSeverity"]
>     ignoreCVEs := []
> 
>     contains(array, elem) = true {
>       array[_] = elem
>     } else = false { true }
> 
>     isSafe(match) {
>       fails := contains(violatingSeverities, match.Ratings.Rating[_].Severity)
>       not fails
>     }
> 
>     isSafe(match) {
>       ignore := contains(ignoreCVEs, match.Id)
>       ignore
>     }
> 
>     isCompliant = isSafe(input.currentVulnerability)
> EOF
apiVersion: scst-scan.apps.tanzu.vmware.com/v1alpha1
kind: ScanPolicy
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"scst-scan.apps.tanzu.vmware.com/v1alpha1","kind":"ScanPolicy","metadata":{"annotations":{},"name":"scan-policy","namespace":"default"},"spec":{"regoFile":"package policies\n\ndefault isCompliant = false\n\n# Accepted Values: \"Critical\", \"High\", \"Medium\", \"Low\", \"Negligible\", \"UnknownSeverity\"\nviolatingSeverities := [\"Critical\",\"High\",\"UnknownSeverity\"]\nignoreCVEs := []\n\ncontains(array, elem) = true {\n  array[_] = elem\n} else = false { true }\n\nisSafe(match) {\n  fails := contains(violatingSeverities, match.Ratings.Rating[_].Severity)\n  not fails\n}\n\nisSafe(match) {\n  ignore := contains(ignoreCVEs, match.Id)\n  ignore\n}\n\nisCompliant = isSafe(input.currentVulnerability)\n"}}
  creationTimestamp: "2021-12-28T06:07:22Z"
  generation: 1
  managedFields:
  - apiVersion: scst-scan.apps.tanzu.vmware.com/v1alpha1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        .: {}
        f:regoFile: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2021-12-28T06:07:22Z"
  name: scan-policy
  namespace: default
  resourceVersion: "5223136"
  uid: 78b2adf9-aca7-4ddd-81c4-259f900bb3c6
spec:
  regoFile: |
    package policies

    default isCompliant = false

    # Accepted Values: "Critical", "High", "Medium", "Low", "Negligible", "UnknownSeverity"
    violatingSeverities := ["Critical","High","UnknownSeverity"]
    ignoreCVEs := []

    contains(array, elem) = true {
      array[_] = elem
    } else = false { true }

    isSafe(match) {
      fails := contains(violatingSeverities, match.Ratings.Rating[_].Severity)
      not fails
    }

    isSafe(match) {
      ignore := contains(ignoreCVEs, match.Id)
      ignore
    }

    isCompliant = isSafe(input.currentVulnerability)
    Events:  <none>

– Now, Deploy the application.

$ tanzu apps workload create tanzu-java-web-app \
> --git-repo https://github.com/sample-accelerators/tanzu-java-web-app \
> --git-branch main \
> --type web \
> --label apps.tanzu.vmware.com/has-tests=true \
> --yes
Create workload:
      1 + |---
      2 + |apiVersion: carto.run/v1alpha1
      3 + |kind: Workload
      4 + |metadata:
      5 + |  labels:
      6 + |    apps.tanzu.vmware.com/has-tests: "true"
      7 + |    apps.tanzu.vmware.com/workload-type: web
      8 + |  name: tanzu-java-web-app
      9 + |  namespace: default
     10 + |spec:
     11 + |  source:
     12 + |    git:
     13 + |      ref:
     14 + |        branch: main
     15 + |      url: https://github.com/sample-accelerators/tanzu-java-web-app

Created workload "tanzu-java-web-app"

– Validate the scan results.

# Image Scan
$ k get imagescan
NAME                 PHASE       SCANNEDIMAGE   AGE    CRITICAL   HIGH   MEDIUM   LOW   UNKNOWN   CVETOTAL
tanzu-java-web-app   Completed                  2m7s    

# Source Scan
$ k get sourcescan
NAME                 PHASE       SCANNEDREVISION                            SCANNEDREPOSITORY                                                                                                                                  AGE   CRITICAL   HIGH   MEDIUM   LOW   UNKNOWN   CVETOTAL
tanzu-java-web-app   Completed   ee4bcbc5bb3c0cb8b9b7db06d0a83b50ee7ed231   http://source-controller.flux-system.svc.cluster.local./gitrepository/default/tanzu-java-web-app/ee4bcbc5bb3c0cb8b9b7db06d0a83b50ee7ed231.tar.gz   16m      

Scan results are stored in a Metadata Store and I will write a separate blog on that.

That’s all from this post, hopefully you have enjoyed the TAP blogs and been helpful for you.

For more reference, Refer VMware TAP documentation.

https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/0.4/tap/GUID-getting-started.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s