<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Supply Chain on TechBlog about OpenShift/Ansible/Satellite and much more</title><link>https://blog.stderr.at/categories/supply-chain/</link><description>TechBlog about OpenShift/Ansible/Satellite and much more</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Toni Schmidbauer &amp; Thomas Jungbauer</copyright><lastBuildDate>Wed, 28 Jun 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.stderr.at/categories/supply-chain/index.xml" rel="self" type="application/rss+xml"/><item><title>Introduction to a Secure Supply Chain</title><link>https://blog.stderr.at/securesupplychain/2023-06-15-securesupplychain-intro/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-15-securesupplychain-intro/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;The goal of the following (&amp;#34;short&amp;#34;) series is to build a secure CI/CD pipeline step by step using OpenShift Pipelines (based on Tekton).
The whole build process shall pull and build an image, upload it to a development environment and subsequently update the production environment.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The main focus here is security. Several steps and tools shall help to build and deploy a &lt;strong&gt;Secure Supply Chain&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The whole process is part of a Red Hat workshop which can present to your organization. I did some tweaks and created a step-by-step plan in order
to remember it …​ since I am getting old :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_the_journey_to_secure_supply_chain"&gt;The Journey to Secure Supply Chain&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This series includes the following articles:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step1/"&gt;Listen to Events&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step2/"&gt;Pipelines&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step3/"&gt;SonarQube&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step4/"&gt;Verify Git Commit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step5/"&gt;Build and Sign Image&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step6/"&gt;Scanning with ACS&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step7/"&gt;Generating a SBOM&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step8/"&gt;Updating Kubernetes Manifests&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step9/"&gt;Linting Kubernetes Manifests&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step10/"&gt;The Example Application&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step11/"&gt;ACS Deployment Check&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step12/"&gt;Verify TLOG Signature&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.stderr.at/openshift/securesupplychain/step13/"&gt;Bring it to Production&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In order to develop our Secure Supply Chain, we need an &lt;strong&gt;OpenShift 4 Cluster&lt;/strong&gt;. I am currently using OpenShift 4.13.
Moreover, the &lt;strong&gt;OpenShift Pipelines&lt;/strong&gt; operator must be deployed. It is based on &lt;a href="https://tekton.dev/" target="_blank" rel="noopener"&gt;Tekton&lt;/a&gt; and provides a Kubernetes-native way to create CI/CD pipelines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The operator is deployed using the Operator Hub inside your cluster. Simply search for OpenShift Pipelines and install the operator using the default settings.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/intro-install_Tekton_Operator.png?width=220px" alt="OpenShift Pipelines"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Install OpenShift Pipelines&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, you will need a GitHub account to be able to fork some repositories.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Some steps in the pipeline are working tightly with GitHub, especially the very last one that is talking GitHub’s API. However, any Git-system should work, and probably just minor changes will be required.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Everything else will be installed during the different steps described in the upcoming articles, while we build and tweak our pipeline.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Remember, the big goal of our pipeline is NOT to simply pull, build and push our code, but to integrate certain security tools like code scanning, image scanning and linting.
Otherwise, it would be boring.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_used_tools"&gt;Used Tools&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The following list of tools (or specifications) are used for our pipeline. They will be deployed when the appropriate step requires it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.openshift.com/acs/4.1/welcome/index.html" target="_blank" rel="noopener"&gt;Advanced Cluster Security&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.openshift.com/acs/4.1/cli/getting-started-cli.html" target="_blank" rel="noopener"&gt;roxctl&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.sonarsource.com/products/sonarqube/" target="_blank" rel="noopener"&gt;SonarQube&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.redhat.com/en/technologies/cloud-computing/quay" target="_blank" rel="noopener"&gt;Quay - quay.io as public registry&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.sigstore.dev/cosign/overview/" target="_blank" rel="noopener"&gt;CoSign (sigstore)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.sigstore.dev/rekor/overview/" target="_blank" rel="noopener"&gt;Rekor (sigstore)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://cyclonedx.org/capabilities/sbom/" target="_blank" rel="noopener"&gt;Software Bill of Material (SBOM)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.kubelinter.io/#/" target="_blank" rel="noopener"&gt;KubeLinter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kube-score.com/" target="_blank" rel="noopener"&gt;KubeScore&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/adrienverge/yamllint" target="_blank" rel="noopener"&gt;YamlLint&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/looztra/kubesplit" target="_blank" rel="noopener"&gt;kubesplit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 1 - Listen to Events</title><link>https://blog.stderr.at/securesupplychain/2023-06-16-securesupplychain-step1/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-16-securesupplychain-step1/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;In this first step, we will simply prepare our environment to be able to retrieve calls from Git. In Git we will fork a prepared source code into a repository and any time a developer pushes a new code into our repository a webhook will notify OpenShift Pipelines to start the pipeline. Like most pipelines, the first task to be executed is to fetch the source code so it can be used for the next steps.
The application I am going to use is called &lt;a href="https://github.com/tjungbauer/globex-ui" target="_blank" rel="noopener"&gt;globex-ui&lt;/a&gt; and is an example webUI build with Angular.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create the &lt;strong&gt;EventListener&lt;/strong&gt; and &lt;strong&gt;Trigger-Settings&lt;/strong&gt; that will take care of notifications by GitHub.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a secret for GitHub authentication.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fork a prepared source code and create a webhook inside Git.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_create_the_eventlistener"&gt;Create the EventListener&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;The first thing we need to create is a Namespace that will be responsible for all our Pipeline-objects. In this example, it is called &lt;strong&gt;ci&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-bash hljs" data-lang="bash"&gt;oc new-project ci&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we need to create the so-called &lt;strong&gt;EventListener&lt;/strong&gt;. This requires the creation of several objects:&lt;/p&gt;
&lt;div class="olist loweralpha"&gt;
&lt;ol class="loweralpha" type="a"&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;TriggerBinding&lt;/strong&gt;: A TriggerBinding captures fields from an event and provides them as named parameters to the TriggerTemplate and subsequently to the PipelineRun.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We will create two TriggerBindings:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;globex-ui&lt;/strong&gt; - For the required settings of our example application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;github-push&lt;/strong&gt; - For the relevant parameters to push into git. These parameters will be provided by Git whenever Git is using the Webhook to inform OpenShift that a new push event happened.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Copy the following examples into your cluster.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock warning"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-warning" title="Warning"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
The list of parameters in these manifests will be extended throughout this series.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: globex-ui
namespace: ci
spec:
params:
- name: tlsVerify &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
value: &amp;#34;false&amp;#34;
- name: gitRepoHost &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
value: github.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Default values for verifying SSL is &amp;#34;false&amp;#34; (Since I do not have certificates in place)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The default value for the Git URL is github.com&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: github-push
namespace: ci
spec:
params:
- name: gitrepositoryurl &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
value: $(body.repository.clone_url)
- name: fullname
value: $(body.repository.full_name)
- name: io.openshift.build.commit.ref
value: $(extensions.ref)
- name: io.openshift.build.commit.id
value: $(body.head_commit.id)
- name: io.openshift.build.commit.date
value: $(body.head_commit.timestamp)
- name: io.openshift.build.commit.message
value: $(body.head_commit.message)
- name: io.openshift.build.commit.author
value: $(body.head_commit.author.name)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Several parameters, coming from Git via the Webhook, for example the exact URL to the repository, the ID of the commit, the date of the commit etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;strong&gt;TriggerTemplate&lt;/strong&gt;: A TriggerTemplate acts as a blueprint for PipelineRuns (or TaskRuns). The resources and parameters here will be used when our Pipeline is executed. It also defines the workspaces, that will be used by the pipeline. For now, we are using the space &lt;em&gt;shared-data&lt;/em&gt; where we will pull the source code for further checks.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: app-globex-ui-template
namespace: ci
spec:
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- description: The git repository URL.
name: gitrepositoryurl
- description: The repository name for this PullRequest.
name: fullname
- description: The git branch for this PR.
name: io.openshift.build.commit.ref
- description: the specific commit SHA.
name: io.openshift.build.commit.id
- description: The date at which the commit was made
name: io.openshift.build.commit.date
- description: The commit message
name: io.openshift.build.commit.message
- description: The name of the github user handle that made the commit
name: io.openshift.build.commit.author
- description: The host name of the git repo
name: gitRepoHost
- description: Enable image repository TLS certification verification.
name: tlsVerify
- description: Extra parameters passed for the push command when pushing images.
name: build_extra_args
- description: Target image repository name
name: imageRepo
resourcetemplates: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: secure-supply-chain- &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
spec:
params: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
- name: REPO_HOST
value: $(tt.params.gitRepoHost)
- name: GIT_REPO
value: $(tt.params.gitrepositoryurl)
- name: TLSVERIFY
value: $(tt.params.tlsVerify)
- name: BUILD_EXTRA_ARGS
value: $(tt.params.build_extra_args)
- name: IMAGE_REPO
value: $(tt.params.imageRepo)
- name: IMAGE_TAG
value: &amp;gt;-
$(tt.params.io.openshift.build.commit.ref)-$(tt.params.io.openshift.build.commit.id)
- name: COMMIT_SHA
value: $(tt.params.io.openshift.build.commit.id)
- name: GIT_REF
value: $(tt.params.io.openshift.build.commit.ref)
- name: COMMIT_DATE
value: $(tt.params.io.openshift.build.commit.date)
- name: COMMIT_AUTHOR
value: $(tt.params.io.openshift.build.commit.author)
- name: COMMIT_MESSAGE
value: $(tt.params.io.openshift.build.commit.message)
pipelineRef: &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
name: secure-supply-chain
serviceAccountName: pipeline &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
workspaces: &lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;(7)&lt;/b&gt;
- name: shared-data
volumeClaimTemplate:
metadata:
creationTimestamp: null
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
status: {}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;List of parameters for this TriggerTemplate, that should be used further for the pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The resources we are going to use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The name prefix of the generated PipelineRun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;List of parameters that shall be provided to the pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The reference to the pipeline that shall be executed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Name of the ServiceAccount that will execute the Pipeline. Per default, this is &lt;strong&gt;pipeline&lt;/strong&gt; which is managed by the Operator.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;7&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The workspaces that will be used by the PipelineRun. Currently &lt;strong&gt;shared-data&lt;/strong&gt; only.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an &lt;strong&gt;EventListener&lt;/strong&gt; that sets up a Service and listens for specific events and exposes a sink that receives incoming events, for example from a GitHub Webhook. It connects TriggerTemplate to a TriggerBinding. In this example, we create a Listener with 1 replica (that’s enough for testing) and connect our two TriggerBindings.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We also refer to the secret &lt;strong&gt;webhook-secret-globex-ui&lt;/strong&gt; which will hold the password for GitHub to authenticate.
We filter any push event coming from my Git repository &lt;strong&gt;tjungbauer/globex-ui&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: globex-ui-event-listener
namespace: ci
spec:
namespaceSelector: {}
resources:
kubernetesResource:
replicas: 1
spec:
template:
metadata:
creationTimestamp: null
spec:
containers: null
serviceAccountName: pipeline
triggers: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- bindings:
- kind: TriggerBinding
ref: globex-ui
- kind: TriggerBinding
ref: github-push
interceptors:
- params:
- name: secretRef
value:
secretKey: webhook-secret-key
secretName: webhook-secret-globex-ui &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
ref:
kind: ClusterInterceptor
name: github
- params:
- name: filter &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
value: &amp;gt;-
(header.match(&amp;#39;X-GitHub-Event&amp;#39;, &amp;#39;push&amp;#39;) &amp;amp;&amp;amp;
body.repository.full_name == &amp;#39;tjungbauer/globex-ui&amp;#39;)
- name: overlays
value:
- expression: &amp;#39;body.ref.split(&amp;#39;&amp;#39;/&amp;#39;&amp;#39;)[2]&amp;#39;
key: ref
ref:
kind: ClusterInterceptor
name: cel
name: build-from-push-globex-ui
template: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
ref: app-globex-ui-template&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;TriggerBindings that are used.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Reference to the secret.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;A filter for push events and our repository name.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The TriggerTemplate that will be used.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now let us create a &lt;strong&gt;Route&lt;/strong&gt; object to allow external traffic (from Git) to the EventListener.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: el-event-listener
namespace: ci
spec:
port:
targetPort: http-listener
to:
kind: Service
name: el-globex-ui-event-listener &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
weight: 100
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
wildcardPolicy: None&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Service that will be automatically created when the EventListener has been created.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And finally, we create a Secret to allow GitHub to authenticate. The name of the Secret is referenced inside the EventListener object.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: webhook-secret-globex-ui &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: ci
stringData:
webhook-secret-key: yoursecret &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Name as referenced in the EventListener&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Your super secure password&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prepare_github"&gt;Prepare GitHub&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now we have everything in place to prepare our source code in Git. All we need to do is to create a repository that holds our source code and a Webhook.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Fork the Source Code: &lt;a href="https://github.com/redhat-gpte-devopsautomation/globex-ui" class="bare"&gt;https://github.com/redhat-gpte-devopsautomation/globex-ui&lt;/a&gt;&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Why fork? I want to be able to update the files and trigger the Pipeline whenever I want to. My forked repository can be found at: &lt;a href="https://github.com/tjungbauer/globex-ui" class="bare"&gt;https://github.com/tjungbauer/globex-ui&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Webhook in GitHub. Go to &lt;em&gt;Settings &amp;gt; Webhooks&lt;/em&gt; and add a new Webhook using:&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step1-create_github_webhook.png?width=500px" alt="Create a new Webhook."/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Create a new Webhook.&lt;/div&gt;
&lt;/div&gt;
&lt;div class="olist loweralpha"&gt;
&lt;ol class="loweralpha" type="a"&gt;
&lt;li&gt;
&lt;p&gt;The Route URL that was created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Content type: application/json.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your Password as used in the secret above.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable or disable SSL verification, since I was too lazy to create a certificate at my demo cluster, I disabled it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And select which events, shall be sent to the Listener. In our case, push events are just fine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After a few seconds GitHub should have validated the Webhook (reload the page eventually)&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step1-active_webhook.png" alt="Verify Webhook"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Verify Webhook&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;That’s it, we now have a Git repository, that will send any push-event to the EventListener, which uses the Triggers to fill out any required parameters and starts the pipeline named: &lt;strong&gt;secure-supply-chain&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This pipeline does not exist yet and will be created in the next step together with its first task to pull from the Git repository.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 2 - Pipelines</title><link>https://blog.stderr.at/securesupplychain/2023-06-17-securesupplychain-step2/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-17-securesupplychain-step2/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;We will now create the Pipeline and try to trigger it for the first time to verify if our Webhook works as intended.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create the &lt;strong&gt;Pipeline&lt;/strong&gt; with a first task&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the Github repository, to verify if the Webhook works&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify if the PipelineRun is successful&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_create_the_pipeline"&gt;Create the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;The Pipeline object is responsible to define the Tasks (steps) that should be executed. Whenever a Pipeline is started a PipelineRun is created that performs each defined Task in the defined order and logs the output. Tasks can run subsequently or in parallel.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Currently, the Pipeline has one task &lt;strong&gt;pull-source-code&lt;/strong&gt; which is defined as a ClusterTask &amp;#34;git-clone&amp;#34;. The purpose is to simply pull the source code to the workspace &amp;#34;shared-data&amp;#34;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: secure-supply-chain &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: ci
spec:
params: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- name: REPO_HOST
type: string
- name: COMMIT_SHA
type: string
- name: TLSVERIFY
type: string
- name: BUILD_EXTRA_ARGS
type: string
- name: IMAGE_REPO
type: string
- name: IMAGE_TAG
type: string
- name: GIT_REF
type: string
- name: COMMIT_DATE
type: string
- name: COMMIT_AUTHOR
type: string
- name: COMMIT_MESSAGE
type: string
- name: GIT_REPO
type: string
tasks: &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
- name: pull-source-code &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
params:
- name: url &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
value: $(params.GIT_REPO)
- name: revision
value: $(params.GIT_REF)
- name: deleteExisting
value: &amp;#39;true&amp;#39;
taskRef: &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
kind: ClusterTask
name: git-clone
workspaces: &lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;(7)&lt;/b&gt;
- name: output
workspace: shared-data
workspaces: &lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;(8)&lt;/b&gt;
- name: shared-data&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Name of the Pipeline as referenced in the TriggerTemplate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;List of Parameters, hopefully, injected by the EventListener.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;List of Tasks that will be executed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Name of the Task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameters used in this Task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The Reference to the task. Here a ClusterTask named &amp;#34;git-clone&amp;#34; is used.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;7&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspace that shall be used in this Task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;8&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspaces available in this Pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The initial Pipeline will now look like the following (Go to: Pipelines &amp;gt; Pipelines &amp;gt; secure-supply-chain)&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step2-initial_Pipeline.png?width=320px" alt="Initial Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Initial Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_our_first_run"&gt;Our first Run&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now it is time to update something in our Git Repository and verify if everything can be executed successfully.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To update, it is enough to simply add a space in the &lt;strong&gt;README.md&lt;/strong&gt; file and push it to Git.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;If the Webhook works as expected, Git will notify our EventListener, which will then trigger the Pipeline.
A PipelineRun is created, that executes all Tasks that are defined in the Pipeline (currently just 1)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;You can monitor the progress of the PipelineRun:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step2-PipelineRun_overview.png" alt="PipelineRun"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. PipelineRun Overview&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;On the Details-page you can see which step is currently executed:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step2-PipelineRun_exec.png?width=320px" alt="PipelineRun Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. PipelineRun Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Eventually, the PipelineRun finishes successfully.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step2-PipelineRun_done.png?width=320px" alt="PipelineRun Finished"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 4. PipelineRun Finished&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;You can analyze the Logs in case of an Error or to get more details of a certain Task:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step2-PipelineRun_logs.png" alt="Task Logs"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 5. Task Logs&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We have now created our first Pipeline and tested the GitHub Webhook. Whenever we push changes to the code, Git will notify the EventListener which will trigger the Pipeline with all required Parameters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;A PipelineRun is generated and is executing the defined Tasks. Currently, not much is done, expect cloning the Git repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In the next steps, we will evolve our Pipeline to perform security checks and sign our image.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 3 - SonarQube</title><link>https://blog.stderr.at/securesupplychain/2023-06-18-securesupplychain-step3/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-18-securesupplychain-step3/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;After the Pipeline has been created and tested we will add another Task to verify the source code and check for possible security issues, leveraging the tool &lt;a href="https://www.sonarsource.com/products/sonarqube/" target="_blank" rel="noopener"&gt;SonarQube&lt;/a&gt; by Sonar.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install and configure SonarQube&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a Task to scan our source code for vulnerabilities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify the results.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_sonarqube"&gt;SonarQube&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;a href="https://www.sonarsource.com/products/sonarqube/" target="_blank" rel="noopener"&gt;SonarQube&lt;/a&gt; by Sonar helps developers to deliver clean code. With the integration into our CICD pipeline it will detect issues and reports them back to the developers. The results will be shown in a dashboard. We will install the Community version of SonarQube, which is enough for our showcase.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_sonarqube_installation"&gt;SonarQube Installation&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To install SonarQube I have prepared a &lt;a href="https://github.com/tjungbauer/helm-charts/tree/main/charts/sonarqube" target="_blank" rel="noopener"&gt;Helm Chart&lt;/a&gt; that I use with GitOps when I deploy a new lab environment. Feel free to use it. It simply calls the Chart that is provided by Sonar. In addition, it creates a Job that changes the default administrator password to a different one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The values file is good to go, the only item you must change is the route in the very first line.
Also, if you prefer not to deploy any plugins (for example, the German language pack), you can remove the appropriate line.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Before you run the Helm, you need to provide a Sealed Secret (or manually create a Secret) like the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: credentials
namespace: sonarqube
data:
adminpass: &amp;lt;your base64 password string&amp;gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This password will be used by the Job &amp;#34;change-admin-password&amp;#34; and will configure a new password for the user &amp;#34;admin&amp;#34;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Once everything is installed (this will take several minutes), you can access SonarQube using the URL you defined in the values file.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step3-SonarQube-entrypage.png" alt="SonarQube"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. SonarQube&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_sonarqube_create_token"&gt;SonarQube Create Token&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Our Pipeline will talk to SonarQube and request a scan of the source code. To be able to do this, we need to create a Token in SonarQube and store it as a Secret in our &lt;strong&gt;ci&lt;/strong&gt; namespace.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Click on &lt;strong&gt;&amp;#34;My Account&amp;#34; (Upper right corner) &amp;gt; Security&lt;/strong&gt; and create a new token:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step3-sonarqube-token.png" alt="SonarQube Token"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. SonarQube Token&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Copy the token and create the following Secret:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: globex-ui-sonarqube-secret
namespace: ci
stringData:
token: &amp;lt;your SonarQube Token&amp;gt; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The generated Token NOT base64 encrypted (I am using &lt;strong&gt;stringData&lt;/strong&gt; in this case)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_modify_the_pipeline"&gt;Modify the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now it is time to bring the SonarQube scan task into our Pipeline. This requires some modifications to existing objects and the creation of some new ones.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Modify the TriggerBinding.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Add the following lines to &lt;strong&gt;globex-ui&lt;/strong&gt; TriggerBinding&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: sonarqubeHostUrl
value: http://sonarqube.apps.ocp.aws.ispworld.at/ &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The URL of SonarQube&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;So, it will look like this:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: globex-ui
namespace: ci
spec:
params:
- name: tlsVerify
value: &amp;#39;false&amp;#39;
- name: gitRepoHost
value: github.com
- name: sonarqubeHostUrl
value: https://sonarqube.apps.ocp.aws.ispworld.at/ &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The URL of SonarQube&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify the TriggerTemplate&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Add the following lines:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- description: Sonarqube host url
name: sonarqubeHostUrl
...
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
spec:
params: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- name: SONARQUBE_HOST_URL
value: $(tt.params.sonarqubeHostUrl)
- name: SONARQUBE_PROJECT_KEY
value: globex-ui &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
- name: SONARQUBE_PROJECT_SECRET
value: globex-ui-sonarqube-secret &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameters provided by the TriggerBinding.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameters provided to the Pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Project that will be created in SonarQube.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Secret of the SonarQube token.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The result should look like the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: app-globex-ui-template
namespace: ci
spec:
params:
- description: The git repository URL.
name: gitrepositoryurl
- description: The repository name for this PullRequest.
name: fullname
- description: The git branch for this PR.
name: io.openshift.build.commit.ref
- description: the specific commit SHA.
name: io.openshift.build.commit.id
- description: The date at which the commit was made
name: io.openshift.build.commit.date
- description: The commit message
name: io.openshift.build.commit.message
- description: The name of the github user handle that made the commit
name: io.openshift.build.commit.author
- description: The host name of the git repo
name: gitRepoHost
- description: Enable image repository TLS certification verification.
name: tlsVerify
- description: Extra parameters passed for the push command when pushing images.
name: build_extra_args
- description: Target image repository name
name: imageRepo
- description: Sonarqube host url
name: sonarqubeHostUrl
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: secure-supply-chain-
spec:
params:
- name: REPO_HOST
value: $(tt.params.gitRepoHost)
- name: GIT_REPO
value: $(tt.params.gitrepositoryurl)
- name: TLSVERIFY
value: $(tt.params.tlsVerify)
- name: BUILD_EXTRA_ARGS
value: $(tt.params.build_extra_args)
- name: IMAGE_REPO
value: $(tt.params.imageRepo)
- name: IMAGE_TAG
value: &amp;gt;-
$(tt.params.io.openshift.build.commit.ref)-$(tt.params.io.openshift.build.commit.id)
- name: COMMIT_SHA
value: $(tt.params.io.openshift.build.commit.id)
- name: GIT_REF
value: $(tt.params.io.openshift.build.commit.ref)
- name: COMMIT_DATE
value: $(tt.params.io.openshift.build.commit.date)
- name: COMMIT_AUTHOR
value: $(tt.params.io.openshift.build.commit.author)
- name: COMMIT_MESSAGE
value: $(tt.params.io.openshift.build.commit.message)
- name: SONARQUBE_HOST_URL
value: $(tt.params.sonarqubeHostUrl)
- name: SONARQUBE_PROJECT_KEY
value: globex-ui
- name: SONARQUBE_PROJECT_SECRET
value: globex-ui-sonarqube-secret
pipelineRef:
name: secure-supply-chain
serviceAccountName: pipeline
workspaces:
- name: shared-data
volumeClaimTemplate:
metadata:
creationTimestamp: null
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
status: {}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the Task &lt;strong&gt;scan-source&lt;/strong&gt;. This task will use the pulled source code and uses SonarQube to let it scan our code.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: scan-code
namespace: ci
spec:
description: &amp;gt;-
Source code scan using sonar-scanner and SonarQube.
params:
- default: &amp;#39;docker.io/sonarsource/sonar-scanner-cli:latest&amp;#39; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
name: scanImage
type: string
- default: &amp;#39;https://sonarqube-sonarqube.myplaceholder.com/&amp;#39; &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
name: sonarqubeHostUrl
type: string
- default: object-detection-rest
name: sonarqubeProjectKey
type: string
- default: object-detection-rest-sonarqube-secret
name: sonarqubeProjectSecret
type: string
- default: &amp;#39;true&amp;#39;
name: verbose
type: string
steps:
- env:
- name: SONAR_TOKEN_WEB_UI &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
valueFrom:
secretKeyRef:
key: token
name: $(params.sonarqubeProjectSecret) &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
image: $(params.scanImage)
name: scan-code
resources: {}
script: &amp;gt; &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
set -x
echo $(ls -a)
sonar-scanner -X -Dsonar.projectKey=$(params.sonarqubeProjectKey)
-Dsonar.sources=./ -Dsonar.host.url=$(params.sonarqubeHostUrl)
-Dsonar.login=$SONAR_TOKEN_WEB_UI
workingDir: /workspace/repository
workspaces: &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Image containing SonarQube command line tool. The cluster must be able to connect to docker.io.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Default parameters for this Task that might be overwritten.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The Secret with the token.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameter as set by the PipelineRun which gets the value from the TriggerTemplate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Script that is executed to scan the source code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The workspace where we can find the source code.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update your Pipeline and add the following task:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
...
- name: SONARQUBE_HOST_URL
type: string
- name: SONARQUBE_PROJECT_KEY
type: string
- name: SONARQUBE_PROJECT_SECRET
type: string
tasks:
...
- name: scan-source
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: sonarqubeHostUrl
value: $(params.SONARQUBE_HOST_URL)
- name: sonarqubeProjectKey
value: $(params.SONARQUBE_PROJECT_KEY)
- name: sonarqubeProjectSecret
value: $(params.SONARQUBE_PROJECT_SECRET)
runAfter: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- pull-source-code
taskRef: &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
kind: Task
name: scan-code
workspaces: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
- name: repository
workspace: shared-data&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameters that shall be provided for the Task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The task should run AFTER the source has been pulled …​ which makes sense.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Reference to the Task we created above.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspace &lt;strong&gt;shared-data&lt;/strong&gt; where the source code was pulled from the previous Task.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The full pipeline objects now look like the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;div class="expand"&gt;
&lt;div class="expand-label" style="cursor: pointer;" onclick="$h = $(this);$h.next('div').slideToggle(100,function () {$h.children('i').attr('class',function () {return $h.next('div').is(':visible') ? 'fas fa-chevron-down' : 'fas fa-chevron-right';});});"&gt;
&lt;i style="font-size:x-small;" class="fas fa-chevron-right"&gt;&lt;/i&gt;
&lt;span&gt;
&lt;a&gt;Expand me...&lt;/a&gt;
&lt;/span&gt;
&lt;/div&gt;
&lt;div class="expand-content" style="display: none;"&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: secure-supply-chain
namespace: ci
spec:
params:
- name: REPO_HOST
type: string
- name: COMMIT_SHA
type: string
- name: TLSVERIFY
type: string
- name: BUILD_EXTRA_ARGS
type: string
- name: IMAGE_REPO
type: string
- name: IMAGE_TAG
type: string
- name: GIT_REF
type: string
- name: COMMIT_DATE
type: string
- name: COMMIT_AUTHOR
type: string
- name: COMMIT_MESSAGE
type: string
- name: GIT_REPO
type: string
- name: SONARQUBE_HOST_URL
type: string
- name: SONARQUBE_PROJECT_KEY
type: string
- name: SONARQUBE_PROJECT_SECRET
type: string
tasks:
- name: pull-source-code
params:
- name: url
value: $(params.GIT_REPO)
- name: revision
value: $(params.GIT_REF)
- name: deleteExisting
value: &amp;#39;true&amp;#39;
taskRef:
kind: ClusterTask
name: git-clone
workspaces:
- name: output
workspace: shared-data
- name: scan-source
params:
- name: sonarqubeHostUrl
value: $(params.SONARQUBE_HOST_URL)
- name: sonarqubeProjectKey
value: $(params.SONARQUBE_PROJECT_KEY)
- name: sonarqubeProjectSecret
value: $(params.SONARQUBE_PROJECT_SECRET)
runAfter:
- pull-source-code
taskRef:
kind: Task
name: scan-code
workspaces:
- name: repository
workspace: shared-data
workspaces:
- name: shared-data&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Pipeline now has a second task:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step3-pipeline.png?width=420px" alt="Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s update the &lt;strong&gt;README.md&lt;/strong&gt; of our source code again to trigger another PipelineRun. After the code has been pulled it should now perform the second task and scan the quality of the source code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;You can monitor the progress of the PipelineRun again:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step3-PipelineRun_exec.png?width=600px" alt="PipelineRun Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 4. PipelineRun Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Once the PipelineRun executed both tasks successfully, we can check SonarQube.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The project &lt;strong&gt;globex-ui&lt;/strong&gt; has been created which shows the results of our scan:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step3-sonarqube_result.png" alt="SonarQube Results"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 5. SonarQube Results&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We have now added a Task to our Pipeline that performs a code analysis of our source code. The results are shown in SonarQube and the developers can react accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 4 - Verify Git Commit</title><link>https://blog.stderr.at/securesupplychain/2023-06-19-securesupplychain-step4/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-19-securesupplychain-step4/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;Besides checking the source code quality, we should also verify if the commit into Git was done by someone/something we trust. It is a good practice to sign all commits to Git. You need to prepare your Git account and create trusted certificates.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock caution"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-caution" title="Caution"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
I will not describe how exactly you need to configure Git to sign your commit. Verify the following link to learn more about &lt;a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits" target="_blank" rel="noopener"&gt;Signing Commits&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Verify if the last commit has been signed&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Signing public key&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configured Git to verify your gpg signature&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When your commit is signed, Git will show that:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step4-signed-commit.png?width=420px" alt="Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_steps"&gt;Steps&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create the following Secret that contains your &lt;strong&gt;PUBLIC&lt;/strong&gt; key.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: gpg-public-key
namespace: ci
data:
public.key: &amp;gt;-
&amp;lt;Base64 PUBLIC GPG KEY&amp;gt; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Public key, containing BEGIN/END lines base64 encoded.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the following Task:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1
kind: Task
metadata:
name: verify-source-code-commit-signature
namespace: ci
spec:
description: This task verifies the latest commit and signature against the gpg
public key
params:
- default: &amp;#39;registry.redhat.io/openshift-pipelines/pipelines-git-init-rhel8:v1.10.4-4&amp;#39;
name: gitInit
type: string
steps:
- computeResources: {}
image: $(params.gitInit)
name: git-verify
script: |
set -x &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
gpg --import /workspace/secrets/public.key
git config --global --add safe.directory /workspace/repository
git verify-commit HEAD || (echo &amp;#34;Unable to verify commit at HEAD!&amp;#34; &amp;amp;&amp;amp; exit 1)
workingDir: /workspace/repository
workspaces:
- name: repository
- name: secrets &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The script to verify the signature of the commit,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The workspace that mounts the Secret containing the gpg key,&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify the TriggerTemplate and add the following 3 lines&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; workspaces:
...
- name: secrets
secret:
secretName: gpg-public-key &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The name of the Secret where the public key can be found.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the pipeline to execute the task &lt;strong&gt;verify-commit-signature&lt;/strong&gt;, which is running in parallel to the SonarQube scan.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: verify-commit-signature
runAfter:
- pull-source-code &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
taskRef:
kind: Task
name: verify-source-code-commit-signature &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
workspaces: &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
- name: repository
workspace: shared-data
- name: secrets
workspace: secrets
workspaces:
...
- name: secrets &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This task runs after &lt;strong&gt;pull-source-code&lt;/strong&gt; but in parallels with the SonarQube task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspaces that are used in this Task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Additional workspace for the Pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The full pipeline objects now look like the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;div class="expand"&gt;
&lt;div class="expand-label" style="cursor: pointer;" onclick="$h = $(this);$h.next('div').slideToggle(100,function () {$h.children('i').attr('class',function () {return $h.next('div').is(':visible') ? 'fas fa-chevron-down' : 'fas fa-chevron-right';});});"&gt;
&lt;i style="font-size:x-small;" class="fas fa-chevron-right"&gt;&lt;/i&gt;
&lt;span&gt;
&lt;a&gt;Expand me...&lt;/a&gt;
&lt;/span&gt;
&lt;/div&gt;
&lt;div class="expand-content" style="display: none;"&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: secure-supply-chain
namespace: ci
spec:
params:
- name: REPO_HOST
type: string
- name: COMMIT_SHA
type: string
- name: TLSVERIFY
type: string
- name: BUILD_EXTRA_ARGS
type: string
- name: IMAGE_REPO
type: string
- name: IMAGE_TAG
type: string
- name: GIT_REF
type: string
- name: COMMIT_DATE
type: string
- name: COMMIT_AUTHOR
type: string
- name: COMMIT_MESSAGE
type: string
- name: GIT_REPO
type: string
- name: SONARQUBE_HOST_URL
type: string
- name: SONARQUBE_PROJECT_KEY
type: string
- name: SONARQUBE_PROJECT_SECRET
type: string
tasks:
- name: pull-source-code
params:
- name: url
value: $(params.GIT_REPO)
- name: revision
value: $(params.GIT_REF)
- name: deleteExisting
value: &amp;#39;true&amp;#39;
taskRef:
kind: ClusterTask
name: git-clone
workspaces:
- name: output
workspace: shared-data
- name: scan-source
params:
- name: sonarqubeHostUrl
value: $(params.SONARQUBE_HOST_URL)
- name: sonarqubeProjectKey
value: $(params.SONARQUBE_PROJECT_KEY)
- name: sonarqubeProjectSecret
value: $(params.SONARQUBE_PROJECT_SECRET)
runAfter:
- pull-source-code
taskRef:
kind: Task
name: scan-code
workspaces:
- name: repository
workspace: shared-data
- name: verify-commit-signature
runAfter:
- pull-source-code
taskRef:
kind: Task
name: verify-source-code-commit-signature
workspaces:
- name: repository
workspace: shared-data
- name: secrets
workspace: secrets
workspaces:
- name: shared-data
- name: secrets&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The status of the Pipeline now is:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step4-pipeline.png?width=600px" alt="Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s update the &lt;strong&gt;README.md&lt;/strong&gt; of our source code again to trigger another PipelineRun.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now the 3rd task will verify if the commit was signed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step4-PipelineRun_exec.png?width=600px" alt="PipelineRun Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. PipelineRun Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In the logs of the Task, we can see that the commit was signed and could be verified.
See:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-none hljs"&gt;...
gpg: Good signature from &amp;#34;Thomas Jungbauer &amp;lt;tjungbau@redhat.com&amp;gt;&amp;#34;
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step4-verify-signature.png?width=600px" alt="Signature Verification"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 4. Signature Verification&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;At this stage we have a Pipeline, that pulls our code, does a code analysis, and verifies if the commit has been signed.
The very next step is to build the image and push it into an Image Registry.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 5 - Build and Sign Image</title><link>https://blog.stderr.at/securesupplychain/2023-06-20-securesupplychain-step5/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-20-securesupplychain-step5/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, after pulling and checking the code, we are going to create the image. During this process the image will be signed and uploaded to the public registry &lt;a href="https://quay.io" target="_blank" rel="noopener"&gt;Quay.io&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Build the image&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sign the image&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upload it Quay.io&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
You can use any registry you like.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You need to have an account in Quay.io and create a repository there.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I have created the repository &lt;a href="https://quay.io/repository/tjungbau/secure-supply-chain-demo" class="bare"&gt;https://quay.io/repository/tjungbau/secure-supply-chain-demo&lt;/a&gt; which will contain the images and the signatures.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_steps"&gt;Steps&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create a Robot account in Quay.io and get the authentication json. Go to &amp;#34;Account Settings &amp;gt; Robot Accounts&amp;#34; and create a new account.&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step5-quay_robot_account.png?width=600px" alt="Quay Robot Account"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Quay Robot Account&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow this account WRITE permissions to the repository&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step5-quay_robot_account_perms.png?width=600px" alt="Quay Robot Account Permissions"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Quay Robot Account Permissions&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the just-created account and go to Kubernetes Secret.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Here you can view or download the Secret. It should look like the following:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: v1
kind: Secret
metadata:
name: tjungbau-secure-supply-chain-demo-pull-secret
data:
.dockerconfigjson: &amp;lt;SECURE&amp;gt;
type: kubernetes.io/dockerconfigjson&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Copy the content and store it in your &lt;strong&gt;ci&lt;/strong&gt; Namespace.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
The name will be probably different in other examples.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To be able for the ServiceAccount, which is executing the Pipelines, to use this Secret, we need to modify the ServiceAccount object.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In our case the ServiceAccount is called &lt;strong&gt;pipeline&lt;/strong&gt;. Modify it and add the following lines:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-bash hljs" data-lang="bash"&gt;secrets:
...
- name: tjungbau-secure-supply-chain-demo-pull-secret &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
imagePullSecrets:
- name: tjungbau-secure-supply-chain-demo-pull-secret &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Use your appropriate name for the secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Use your appropriate name for the secret, required to be able to sign the image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the Pipeline object and add the following block&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This time we do not need to add a Task object, because we are using a ClusterTask &amp;#34;buildah&amp;#34; that takes care of the building process. As workspace &amp;#34;shared-data&amp;#34; is used again, which has all the source code stored:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: build-sign-image
params:
- name: TLSVERIFY
value: $(params.TLSVERIFY)
- name: BUILD_EXTRA_ARGS
value: &amp;gt;-
--label=io.openshift.build.commit.author=&amp;#39;$(params.COMMIT_AUTHOR)&amp;#39;
--label=io.openshift.build.commit.date=&amp;#39;$(params.COMMIT_DATE)&amp;#39;
--label=io.openshift.build.commit.id=&amp;#39;$(params.COMMIT_SHA)&amp;#39;
--label=io.openshift.build.commit.message=&amp;#39;$(params.COMMIT_MESSAGE)&amp;#39;
--label=io.openshift.build.commit.ref=&amp;#39;$(params.GIT_REF)&amp;#39;
--ulimit=nofile=4096:4096
- name: IMAGE
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
retries: 1
runAfter:
- scan-source
- verify-commit-signature
taskRef:
kind: ClusterTask
name: buildah
workspaces:
- name: source
workspace: shared-data&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerBinding &lt;strong&gt;globex-ui&lt;/strong&gt; and define the URL for your registry respectively for your image repository&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
parameters:
- name: imageRepo
value: quay.io/tjungbau/secure-supply-chain-demo&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
With this configuration, we could already build the image and push it into the registry. However, we also would like to sign it.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prepare_tekton_for_image_signing"&gt;Prepare Tekton for Image Signing&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To automatically sign images, we use &lt;a href="https://github.com/tektoncd/chains" target="_blank" rel="noopener"&gt;TektonChains&lt;/a&gt; which is a controller that allows you to manage your supply chain security by signing TaskRuns and/or OCI images. Once the build-Task has pushed the images to Quay, Tekton will detect this event (by monitoring specific values of Task results) and then creates and pushes the signature for that image.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;By default, Tekton Chains observes all task run executions. When the task runs complete, Tekton Chains signs and stores all artefacts.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Tekton Chain is part of OpenShift Pipelines and has the following main features:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You can sign task runs, task run results, and OCI registry images with cryptographic keys that are generated by tools such as cosign and skopeo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use attestation formats such as &lt;a href="https://github.com/in-toto/attestation/tree/v0.1.0/spec" target="_blank" rel="noopener"&gt;in-toto&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can securely store signatures and signed artefacts using OCI repository as a storage backend.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In our case, we will use &lt;a href="https://docs.sigstore.dev/cosign/overview/" target="_blank" rel="noopener"&gt;CoSign&lt;/a&gt; for signing the images and &lt;a href="https://docs.sigstore.dev/rekor/overview/" target="_blank" rel="noopener"&gt;Rekor&lt;/a&gt; for the attestation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To activate image signing add the following to the TektonConfig object by removing the &amp;#34;chain{}&amp;#34; entry.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; chain:
artifacts.oci.storage: oci &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
artifacts.taskrun.format: in-toto &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
artifacts.taskrun.storage: oci &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
transparency.enabled: true &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The storage backend for storing OCI signatures.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The format for storing TaskRun payloads. Can be in-toto or slsa/v1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The storage backend for TaskRun signatures. Multiple can be defined.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Enable or disable automatic binary transparency uploads. The URL for uploading binary transparency attestations is &lt;a href="https://rekor.sigstore.dev" class="bare"&gt;https://rekor.sigstore.dev&lt;/a&gt; by default.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
More details can be found at &lt;a href="https://docs.openshift.com/container-platform/4.13/cicd/pipelines/using-tekton-chains-for-openshift-pipelines-supply-chain-security.html" class="bare"&gt;https://docs.openshift.com/container-platform/4.13/cicd/pipelines/using-tekton-chains-for-openshift-pipelines-supply-chain-security.html&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_cosign_signing_the_image"&gt;CoSign - Signing the Image&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We will use &lt;a href="https://github.com/sigstore/cosign" target="_blank" rel="noopener"&gt;CoSign&lt;/a&gt; to sign our image. To do so we need to download the cosign binary and generate a key pair:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Be sure that you are logged into the OpenShift cluster.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-bash hljs" data-lang="bash"&gt;cosign generate-key-pair k8s://openshift-pipelines/signing-secrets&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-bash hljs" data-lang="bash"&gt;Enter password for private key:
Enter password for private key again:
Successfully created secret signing-secrets in namespace openshift-pipelines
oc get secrets signing-secrets -n openshift-pipelines
NAME TYPE DATA AGE
signing-secrets Opaque 3 46h&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Cosign will ask you to enter a password and will then create a Kubernetes secret in the Namespace openshift-pipelines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When OpenShift Pipelines now execute a Task that is pushing an image, this Secret will be used to sign the image.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s update our source code. The image will be built and pushed into Quay. The small black shield indicates that this image has been signed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step5-signed_image.png?width=600px" alt="Quay Signed Image"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. Quay Signed Image&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Besides the actual image the files: *.sig and *.att can be seen. The first one is the signature, the second one shows metadata about the attestation retrieved from Rekor which can be compared with the Rekor URL.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_rekor"&gt;Rekor&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;em&gt;Rekor’s goals are to provide an immutable tamper resistant ledger of metadata generated within a software projects supply chain. Rekor will enable software maintainers and build systems to record signed metadata to an immutable record. Other parties can then query said metadata to enable them to make informed decisions on trust and non-repudiation of an object’s lifecycle. For more details visit the sigstore website.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;em&gt;The Rekor project provides a restful API based server for validation and a transparency log for storage. A CLI application is available to make and verify entries, query the transparency log for inclusion proof, integrity verification of the transparency log or retrieval of entries by either public key or artifact.&lt;/em&gt; &lt;a href="https://github.com/sigstore/rekor" target="_blank" rel="noopener"&gt;Rekor Git&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;An important feature of Tekton Chains is that it integrates seamlessly with an application called &lt;strong&gt;Rekor&lt;/strong&gt;. The configuration &lt;code&gt;transparency.enabled: true&lt;/code&gt; enables the call to the Rekor API that can be found by default at: &lt;a href="https://rekor.sigstore.dev" class="bare"&gt;https://rekor.sigstore.dev&lt;/a&gt;. This will create a transparency log which can be used for verification purposes later.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Of course, it would be possible to install your own server. Learn in the official &lt;a href="https://docs.sigstore.dev/rekor/installation/" target="_blank" rel="noopener"&gt;Docs&lt;/a&gt; how this can be achieved.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This means every time our build-Tasks successfully completes a URL to the transparency logs will be attached to the Task.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;After our PipelineRuns were successful, we can verify if the image has an entry in the Rekor transparency log. This is important to ensure that the image went through a valid signing process. At a later step (before we create the pull request for the production update) we will verify if the log exists.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The following annotations should have been added to the TaskRun or PipelineRun secure-supply-chain-XXX-build-sign-image automatically:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;metadata:
annotations:
chains.tekton.dev/signed: &amp;#39;true&amp;#39;
chains.tekton.dev/transparency: &amp;#39;https://rekor.sigstore.dev/api/v1/log/entries?logIndex=25593495&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This created the following transparency logs:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-json hljs" data-lang="json"&gt;{
&amp;#34;24296fb24b8ad77a7ae84f4b950336d1872a066aeb9463dfbc231a7d3b73dd040dd5faefb3c013a7&amp;#34;: {
&amp;#34;body&amp;#34;: &amp;#34;eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJmYzE2ODM3Mzc1ODUyNjc0MTQ4MjIyMDJhMWU2Y2RkZDkxNWI4NWUzYzhhNDVhZmI0YzEyYmE2YmNhMGNmNDQyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNRMnlnU2ZGTllHS2pzbXRIYjVielAwdlRNMFgyNHVlNXlKbjJxdWFWSyt3SWhBSmhoSWpweEFybDJERjFsVmpMT0ZzVnRhMzJmbXJXRmxXWkdRQ015b2xrayIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGVEc0clduaENNMHNyS3pWSk1pOWlNWFJqVFVKcVZXODRjWGx6YVFwNlQzUlpVbGRQUTBwTVRWQlNWVGR4WTFJeVMyWlZZazAzYlVwUlNtbHZjbXR6VW1KUmNHMTBTekV4WjJNM1pIVkZObGg0WmxOdlpWUkJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=&amp;#34;,
&amp;#34;integratedTime&amp;#34;: 1688060588,
&amp;#34;logID&amp;#34;: &amp;#34;c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d&amp;#34;,
&amp;#34;logIndex&amp;#34;: 25593495,
&amp;#34;verification&amp;#34;: {
&amp;#34;inclusionProof&amp;#34;: {
&amp;#34;checkpoint&amp;#34;: &amp;#34;rekor.sigstore.dev - 2605736670972794746\n21430853\n10sx5mwekx2c8jltWbt0WeJu+tNM9FOBNQoyPiC3CPQ=\nTimestamp: 1688061275289390813\n\n— rekor.sigstore.dev wNI9ajBFAiEAyzFYKKghB+RQsbMIIzjywdA9vFjsusQoMWMUJPRjw3ECIDVeYSGKn7zwXWxGLhQlIGAI1Ca0Gu7ZuLJ64xR3SrY0\n&amp;#34;,
&amp;#34;hashes&amp;#34;: [
&amp;#34;5cc5dba1f5f420acc9ed049c77b04c4fdd66cf6ef1ebd46b66e26039bb9ba06c&amp;#34;,
&amp;#34;3041520c9fec6177225063053503f6d20b09e86b72968b737e3211a233dab6ce&amp;#34;,
&amp;#34;ddb57132bba122df920d4816af7ac02bef03aec533cc7b45f8552ce19c023d10&amp;#34;,
&amp;#34;8a510da356706a0a822daf3c3a935176d3634c6e0cce6830fe90a8771a3bc83d&amp;#34;,
&amp;#34;7d254234192620827306cc5024ffb8ae699bfe2725c8e6aa13757f5471d416d4&amp;#34;,
&amp;#34;648a4e45da960f4ad286dfa22ae93721c9ba82ee05edd5a7134d9b19e35735e0&amp;#34;,
&amp;#34;9dcf0f21690ec420bffbc1d10c383b7d3dc568d26bd8fd9e8771abb01f5976dc&amp;#34;,
&amp;#34;86575ffec011d78ad393e7af267693c0c59360e2299b308369951d8362032733&amp;#34;,
&amp;#34;0088ef1f6ec1e992fa6c91837287f2bd7eea238e5af7467e21e6df0dc8efe516&amp;#34;,
&amp;#34;b149d95903a4e554ac1c381f1afff31bb62b6e12b6b2fc95f36b8e198e1a42cd&amp;#34;,
&amp;#34;de73a694862cebd660ef1cecdd1bb66e273ab0651ca939bf7fa6f5f567bafe93&amp;#34;,
&amp;#34;a3c84734ddae3102952584443dea70e3dec2cc085af7cf0ba3530751966861ec&amp;#34;,
&amp;#34;0be5c7bbcf481d1efcfc63a27fce447cf6345f7bb3155cf5de39de592c16d52d&amp;#34;,
&amp;#34;f597f4bae8df3d6fc6eebfe3eabd7d393e08781f6f16b42398eca8512398fff1&amp;#34;,
&amp;#34;4e35fcb3c0a59e7f329994002b38db35f5d511f499ba009e10b31f5d27563607&amp;#34;,
&amp;#34;47044b7ac3aab820e44f0010538d7de71e17a11f4140cbbe9eeb37f78b77cc7d&amp;#34;,
&amp;#34;eee63677e2591eefe06ab537d6dd1b4060770682744c8287879b5dcd3365a5b2&amp;#34;,
&amp;#34;ff41aa21106dbe03996b4335dd158c7ffafd144e45022193de19b2b9136c3e42&amp;#34;,
&amp;#34;e6ebdeef2e23335d8d7049ba5a0049a90593efdfe9c1b4548946b44a19d7214f&amp;#34;,
&amp;#34;dd51e840e892d70093ad7e1db1e2dea3d50334c7345d360e444d22fc49ed9f5e&amp;#34;,
&amp;#34;ad712c98424de0f1284d4f144b8a95b5d22c181d4c0a246518e7a9a220bdf643&amp;#34;
],
&amp;#34;logIndex&amp;#34;: 21430064,
&amp;#34;rootHash&amp;#34;: &amp;#34;d74b31e66c1e931d9cf2396d59bb7459e26efad34cf45381350a323e20b708f4&amp;#34;,
&amp;#34;treeSize&amp;#34;: 21430853
},
&amp;#34;signedEntryTimestamp&amp;#34;: &amp;#34;MEYCIQDqmqb4k95FjiBNogOAmjTskkIPaGslgvrSND4pQdUALwIhAMt85yLsa5Ei+3NsmJ906/T9Hx1YZDDHQfHlTEYqqcaE&amp;#34;
}
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, we have a signed image uploaded to Quay. In addition, a transparency log has been created. Now let us add some security checks in the next steps.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 6 - Scanning with ACS</title><link>https://blog.stderr.at/securesupplychain/2023-06-21-securesupplychain-step6/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-21-securesupplychain-step6/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;In this step we will install &lt;strong&gt;Advanced Cluster Security&lt;/strong&gt; (ACS) and create 2 new steps in our Pipeline to scan the image for vulnerabilities and security policy. A custom security policy, configured in ACS, will verify if the image is signed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install ACS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a custom security policy into ACS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure CoSign integration in ACS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a step in the Pipeline to perform an image scan (vulnerabilities).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a step in the Pipeline to perform an image check (security policies).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_install_advanced_cluster_security_acs"&gt;Install Advanced Cluster Security (ACS)&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We will install ACS on our cluster which will be called &lt;strong&gt;local-cluster&lt;/strong&gt; inside ACS.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The deployment will happen via Argo CD (OpenShift GitOps) that will use the following Helm Chart: &lt;a href="https://github.com/tjungbauer/helm-charts/tree/main/charts/rhacs-full-stack" target="_blank" rel="noopener"&gt;RHACS&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Argo CD Application will trigger the deployment which will take several minutes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock warning"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-warning" title="Warning"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
The deployment will install a minimal version of ACS and tries to limit the required resources and replicas. For production environments, you probably want to increase the settings.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: in-cluster-init-rhacs
namespace: openshift-gitops
spec:
destination:
namespace: default
server: &amp;#39;https://kubernetes.default.svc&amp;#39; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
info:
- name: Description
value: &amp;gt;-
Initialize Red Hat Advanced Cluster Security and deploy Central and
SecuredCluster
project: in-cluster
source:
path: charts/rhacs-full-stack &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
repoURL: &amp;#39;https://github.com/tjungbauer/helm-charts&amp;#39; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
targetRevision: main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Installing on the local cluster.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Path to the Helm chart.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;URL to the Helm chart repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-sync_acs.png?width=400px" alt="Syncing ACS Deployment Application"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Syncing ACS Deployment Application&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This GitOps Application will start a full-stack ACS deployment. It does (in this exact order):&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Installs the ACS Operator into rhacs-operator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verifies if the Operator is ready&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates the Namespace stackrox&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adds a ConsoleLink in the upper right action menu of OpenShift&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_consolelink.png?width=220px" alt="ACS ConsoleLine"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. ACS ConsoleLink&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates the Central custom resource with minimum resources&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates an init-bundle to add the first (local) cluster&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adds the custom resource SecureCluster to install the rest of the ACS components&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And finally, runs a basic configuration that enables authentication via OpenShift and provides the &lt;strong&gt;kubeadmin&lt;/strong&gt; user admin privileges. This can be disabled in the values file if you prefer not to configure this, or if kubeadmin does not exist anymore in your cluster.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The whole process will take a while and uses syncwaves and hooks.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_configure_acs_integration"&gt;Configure ACS Integration&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_generate_authentication_token"&gt;Generate Authentication Token&lt;/h3&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create an authentication Token in ACS&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Go to &lt;code&gt;&amp;#34;Platform Configuration &amp;gt; Integrations &amp;gt; API Token&amp;#34;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_token.png?width=440px" alt="ACS Generate Token"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. ACS Generate Token&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Generate a new Token with at least &lt;strong&gt;Continuous Integration&lt;/strong&gt; privileges and save the created Token.&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back in OpenShift, a Secret must be created with the key &lt;strong&gt;rox_api_token&lt;/strong&gt;&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: stackrox-secret
namespace: ci
data:
rox_api_token: &amp;lt;base 64 Token&amp;gt; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Base64 decoded ACS Token.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a 2nd Secret that contains the ACS endpoint and its port:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: stackrox-endpoint
namespace: ci
stringData:
rox_central_endpoint: central-stackrox.apps.ocp.aws.ispworld.at:443 &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The endpoint URL of my example cluster. Note: the port MUST be added here.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_integrate_cosign_with_acs"&gt;Integrate CoSign with ACS&lt;/h3&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Configure CoSign Integration&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;During &lt;a href="https://blog.stderr.at/openshift/securesupplychain/step5/" target="_blank" rel="noopener"&gt;Step 5&lt;/a&gt; we have created a CoSign key pair to sign our images. To integrate with ACS, we need to retrieve the &lt;strong&gt;public key&lt;/strong&gt;. In OpenShift, open the Secret signing-secrets in the Namespace openshift-pipelines and extract the key cosign.pub.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It will look something like tjis:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-none hljs"&gt;-----BEGIN PUBLIC KEY-----
key...
-----END PUBLIC KEY-----&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In ACS, go to &lt;code&gt;&amp;#34;Platform Configuration &amp;gt; Integrations &amp;gt; Signature&amp;#34;&lt;/code&gt; and create a new CoSign integration.&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_cosign.png?width=440px" alt="ACS CoSign Integration"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 4. ACS CoSign Integration&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Enter a name and the public key and activate this integration&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_cosign_int.png?width=600px" alt="ACS Create CoSign Integration"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 5. ACS Create CoSign Integration&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This will enable ACS to verify the CoSign signature of the image.&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_create_a_custom_policy_that_verifies_the_signature"&gt;Create a Custom Policy that verifies the Signature&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Create a custom security policy, that verifies if our image has been signed or not.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To be sure that every image is correctly signed, we create a custom security policy that verifies this signature using our CoSign integration. This policy can be configured as &lt;strong&gt;inform&lt;/strong&gt; or &lt;strong&gt;enforce&lt;/strong&gt;. Enforce means that the pipeline will fail (during the Task acs-image-check) if the image signature cannot be checked.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s create our policy by hand for this time. However, it is also possible to export/import rules. The important part here is that link to the CoSign integration is valid, otherwise ACS cannot verify the signature.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;code&gt;&amp;#34;Platform Configuration &amp;gt; Policy &amp;gt; and click the button Create Policy&amp;#34;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name: &lt;strong&gt;Trusted Signature Policy&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Severity: &lt;strong&gt;High&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Categories: &lt;strong&gt;Security Best Practices&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lifecycle stage: check &lt;strong&gt;Build&lt;/strong&gt; and &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Response method: &lt;strong&gt;Inform and enforce&lt;/strong&gt; &amp;gt; This will make the Task in the Pipeline fail when the signature cannot be verified.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure enforcement behavior:&lt;/p&gt;
&lt;div class="olist loweralpha"&gt;
&lt;ol class="loweralpha" type="a"&gt;
&lt;li&gt;
&lt;p&gt;Activate: &lt;strong&gt;Enforce on Build&lt;/strong&gt; &amp;gt; Only fail for build lifecycles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leave Deployment disabled for now. A good practice would be to enable it, but for other steps in this series, it will be required to keep it disabled for now.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leave Runtime disabled.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As policy criteria find &amp;#34;Image Registry &amp;gt; Image signature&amp;#34; and drag and drop it to the policy section&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_policy_criteria.png?width=600px" alt="ACS Policy Criteria"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 6. ACS Policy Criteria&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on &lt;strong&gt;Select&lt;/strong&gt; and select the CoSign integration&lt;/p&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_assign_cosign.png?width=600px" alt="ACS Assign CoSign integration to policy"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 7. ACS Assign CoSign integration to policy&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leave the Policy scope for now and click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Review the Policy and &lt;strong&gt;Save&lt;/strong&gt; it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prepare_the_pipeline_tasks"&gt;Prepare the Pipeline Tasks&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now, finally, after all these preparations we are going to integrate ACS into our Pipeline. For this, we will create 2 tasks:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;acs-image-scan&lt;/strong&gt;: Will scan the image for vulnerabilities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;acs-image-check&lt;/strong&gt;: Will verify if the image would violate any security policy, especially the custom policy we have created.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Both checks will use the &lt;strong&gt;roxctl&lt;/strong&gt; command line tool.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
It is recommended to add these two Tasks to any Pipeline system you are using when you have ACS installed to leverage the full potential of ACS. It does not matter if you are using Tekton, Jenkins or something else. You can find examples of integrations at the Git repository: &lt;a href="https://github.com/stackrox/contributions/" class="bare"&gt;https://github.com/stackrox/contributions/&lt;/a&gt;.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_task_acs_image_scan"&gt;Task acs-image-scan&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To scan for vulnerabilities, create the following Task. It leverages the command line tool &lt;strong&gt;roxctl&lt;/strong&gt; which can also be used on your local machine to perform such scans manually.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: acs-image-scan
namespace: ci
spec:
description: &amp;gt;-
Scan an image with StackRox/RHACS. This tasks allows you to check an
image against vulnerabilities.
params:
- description: |
Secret containing the address:port tuple for StackRox Central)
(example - rox.stackrox.io:443)
name: rox_central_endpoint
type: string
- description: Secret containing the StackRox API token with CI permissions
name: rox_api_token
type: string
- description: |
Full name of image to scan (example -- gcr.io/rox/sample:5.0-rc1)
name: image
type: string
- default: &amp;#39;false&amp;#39;
description: |
When set to `&amp;#34;true&amp;#34;`, skip verifying the TLS certs of the Central
endpoint. Defaults to `&amp;#34;false&amp;#34;`.
name: insecure-skip-tls-verify
type: string
- default: &amp;#39;registry.access.redhat.com/ubi9@sha256:089bd3b82a78ac45c0eed231bb58bfb43bfcd0560d9bba240fc6355502c92976&amp;#39;
name: ubi9
type: string
results:
- description: Output of `roxctl image check`
name: check_output
type: string
steps:
- env: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: ROX_API_TOKEN
valueFrom:
secretKeyRef:
key: rox_api_token
name: $(params.rox_api_token)
- name: ROX_CENTRAL_ENDPOINT
valueFrom:
secretKeyRef:
key: rox_central_endpoint
name: $(params.rox_central_endpoint)
image: $(params.ubi9)
name: rox-image-scan
resources: {}
script: | &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
#!/usr/bin/env bash
set +x
curl -s -k -L -H &amp;#34;Authorization: Bearer $ROX_API_TOKEN&amp;#34; \
&amp;#34;https://$ROX_CENTRAL_ENDPOINT/api/cli/download/roxctl-linux&amp;#34; \
--output ./roxctl \
&amp;gt; /dev/null
chmod +x ./roxctl &amp;gt; /dev/null
if [ &amp;#34;$(params.insecure-skip-tls-verify)&amp;#34; = &amp;#34;true&amp;#34; ]; then
export ROX_INSECURE_CLIENT_SKIP_TLS_VERIFY=true
fi
./roxctl image scan -e &amp;#34;$ROX_CENTRAL_ENDPOINT&amp;#34; --image &amp;#34;$(params.image)&amp;#34; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Token end endpoint which we defined as Secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Script that downloads and executes the CLI &lt;strong&gt;roxctl&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Calling &amp;#34;&lt;em&gt;roxctl image scan&lt;/em&gt;&amp;#34;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_task_image_check"&gt;Task Image Check&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To verify the policies that are configured in ACS we create the Task &lt;strong&gt;acs-image-check&lt;/strong&gt;.
We will use the command line tool roxctl again.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
ACS comes with several (80+) build-in policies. Some of them are activated, but none of them configured to enforce a policy. Except, the policy &lt;strong&gt;Trusted Signature Policy&lt;/strong&gt; we have created above.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: acs-image-check
namespace: ci
spec:
description: &amp;gt;-
Policy check an image with StackRox/RHACS This tasks allows you to check an
image against build-time policies and apply enforcement to fail builds. It&amp;#39;s
a companion to the stackrox-image-scan task, which returns full
vulnerability scan results for an image.
params:
- description: |
Secret containing the address:port tuple for StackRox Central)
(example - rox.stackrox.io:443)
name: rox_central_endpoint
type: string
- description: Secret containing the StackRox API token with CI permissions
name: rox_api_token
type: string
- description: |
Full name of image to scan (example -- gcr.io/rox/sample:5.0-rc1)
name: image
type: string
- default: &amp;#39;false&amp;#39;
description: |
When set to `&amp;#34;true&amp;#34;`, skip verifying the TLS certs of the Central
endpoint. Defaults to `&amp;#34;false&amp;#34;`.
name: insecure-skip-tls-verify
type: string
- default: &amp;#39;registry.access.redhat.com/ubi9@sha256:089bd3b82a78ac45c0eed231bb58bfb43bfcd0560d9bba240fc6355502c92976&amp;#39;
name: ubi9
type: string
results:
- description: Output of `roxctl image check`
name: check_output
type: string
steps:
- env: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: ROX_API_TOKEN
valueFrom:
secretKeyRef:
key: rox_api_token
name: $(params.rox_api_token)
- name: ROX_CENTRAL_ENDPOINT
valueFrom:
secretKeyRef:
key: rox_central_endpoint
name: $(params.rox_central_endpoint)
image: $(params.ubi9)
name: rox-image-check
resources: {}
script: |
#!/usr/bin/env bash &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
set +x
curl -s -k -L -H &amp;#34;Authorization: Bearer $ROX_API_TOKEN&amp;#34; \
&amp;#34;https://$ROX_CENTRAL_ENDPOINT/api/cli/download/roxctl-linux&amp;#34; \
--output ./roxctl \
&amp;gt; /dev/null
chmod +x ./roxctl &amp;gt; /dev/null
if [ &amp;#34;$(params.insecure-skip-tls-verify)&amp;#34; = &amp;#34;true&amp;#34; ]; then
export ROX_INSECURE_CLIENT_SKIP_TLS_VERIFY=true
fi
./roxctl image check -e &amp;#34;$ROX_CENTRAL_ENDPOINT&amp;#34; --image &amp;#34;$(params.image)&amp;#34; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Token end endpoint which we defined as Secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Script that downloads and executes the CLI &lt;strong&gt;roxctl&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Calling &amp;#34;&lt;em&gt;roxctl image check&lt;/em&gt;&amp;#34;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_extend_the_pipeline"&gt;Extend the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now it is time to extend our Pipeline with the two tasks. Add the following blocks to the Pipeline &lt;strong&gt;secure-supply-chain&lt;/strong&gt;:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yml hljs" data-lang="yml"&gt; - name: acs-image-scan &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
params: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- name: rox_central_endpoint
value: stackrox-endpoint
- name: rox_api_token
value: stackrox-secret
- name: image
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
- name: insecure-skip-tls-verify &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
value: &amp;#39;true&amp;#39;
runAfter: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
- build-sign-image
taskRef:
kind: Task
name: acs-image-scan
- name: acs-image-check &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
params: &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
- name: rox_central_endpoint
value: stackrox-endpoint
- name: rox_api_token
value: stackrox-secret
- name: image
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
- name: insecure-skip-tls-verify &lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;(7)&lt;/b&gt;
value: &amp;#39;true&amp;#39;
runAfter:
- build-sign-image &lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;(8)&lt;/b&gt;
taskRef:
kind: Task
name: acs-image-check&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Scanning for vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Provide parameters to the Task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Set to &amp;#39;false&amp;#39; if you have valid certificates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Run after the Task &amp;#34;build-sign-image&amp;#34;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Scanning security policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Provide parameters to the Task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;7&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Set to &amp;#39;false&amp;#39; if you have valid certificates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;8&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Run after the Task &amp;#34;build-sign-image&amp;#34;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Pipeline will now look like this:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-pipeline.png" alt="Pipeline Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 8. Pipeline Details&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s trigger another PipelineRun by updating the &lt;strong&gt;README.md&lt;/strong&gt; of our source code.
While the previous Tasks should run successfully, let’s monitor our two ACS tasks. However, now the Pipeline is running quite long already. This means it is time for a coffee.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Still here? Good, eventually our two ACS tasks have finished successfully.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Task &lt;strong&gt;image-scan&lt;/strong&gt; is looking for vulnerabilities and did not find any high-severity issues.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_scan.png?width=600px" alt="ACS Image Scan Result"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 9. ACS Image Scan Result&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Task &lt;strong&gt;image-check&lt;/strong&gt; verified the security policies. You can see in the Logs that the &lt;strong&gt;*Trusted Signature Policy&lt;/strong&gt; was not violated. Another might be shown, but since all other policies are configured to notify only (instead of &amp;#34;enforce&amp;#34;), the whole Task will end successfully. This is good enough for our first tests. The important bit is that the image has been signed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step6-acs_check.png?width=600px" alt="ACS Image Check Result"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 10. ACS Image Check Result&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now we have integrated ACS and are using its powers to scan images for vulnerabilities and check them against security policies. Two Tasks have been created that are executed after the image has been built and are leveraging ACS’s command line tool &lt;strong&gt;roxctl&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Whenever vulnerabilities are found, or security policies are violated (if the policy is set to &lt;strong&gt;enforce&lt;/strong&gt;) the Pipeline will fail.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Such tasks should be built in any pipeline you are creating, independently of using ACS or any other security verification tool.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In the next step, we will create a Task that creates a Software Bill of Material (SBOM) for us.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 7 - Generating a SBOM</title><link>https://blog.stderr.at/securesupplychain/2023-06-22-securesupplychain-step7/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-22-securesupplychain-step7/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;A &lt;strong&gt;software bill of materials&lt;/strong&gt; (SBOM) offers an insight into the makeup of an application developed using third-party commercial tools and open-source software. It is a machine-readable, formally structured list of all components. This list is important to gain visibility into what risks might exist in a package and their potential impact. It allows to prioritize the risks and define a remediation path.
The teams can use the SBOM to monitor all components that are used across an application.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We will create a Task that will generate a SBOM and uploads it into an SBOM repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install a SBOM repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate a SBOM and upload it to the repository.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_toolstandard_cyclonedx"&gt;Tool/Standard CycloneDX&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;OWASP CycloneDX is a full-stack Bill of Materials (BOM) standard that provides advanced supply chain capabilities for cyber risk reduction. The specification supports:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Software Bill of Materials (SBOM)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Software-as-a-Service Bill of Materials (SaaSBOM)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hardware Bill of Materials (HBOM)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Operations Bill of Materials (OBOM)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vulnerability Disclosure Reports (VDR)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vulnerability Exploitability eXchange (VEX)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Strategic direction of the specification is managed by the CycloneDX Core Working Group, is backed by the OWASP Foundation, and is supported by the global information security community.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Source: &lt;a href="https://cyclonedx.org/" class="bare"&gt;https://cyclonedx.org/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The CycloneDX module for Node.js creates a valid CycloneDX Software Bill-of-Materials (SBOM) containing an aggregate of all project dependencies. CycloneDX is a lightweight SBOM specification that is easily created, human and machine-readable, and simple to parse.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
&lt;a href="https://cyclonedx.org/capabilities/sbom/" class="bare"&gt;https://cyclonedx.org/capabilities/sbom/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We will use this standard and will deploy a small Pod in our cluster to be able to upload our SBOM locally.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_install_cyclonedx_repository_server"&gt;Install CycloneDX Repository Server&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We can install a CycloneDX Repository Server, which is a simple Pod running in our cluster. You can use the following Argo CD application, which will create the Namespace (cyclonedx),Deployment, Route, and Service.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: in-cluster-install-cyclonedx
namespace: openshift-gitops
spec:
destination:
namespace: cyclonedx
server: &amp;#39;https://kubernetes.default.svc&amp;#39;
info:
- name: Description
value: Install CycloneDX SBOM Repository
project: in-cluster
source:
path: clusters/management-cluster/cyclonedx &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
repoURL: &amp;#39;https://github.com/tjungbauer/openshift-clusterconfig-gitops&amp;#39;
targetRevision: main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Path and URL to the Git repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In Argo CD the following Application will be created:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step7-install_cyclonedx.png?width=600px" alt="CycloneDX Installation"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. CycloneDX Installation&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_update_the_tekton_pipeline"&gt;Update the Tekton Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerBinding &lt;strong&gt;globex-ui&lt;/strong&gt; and add the following:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: cyclonedxHostUrl
value: &amp;gt;-
https://cyclonedx-bom-repo-server-cyclonedx.apps.ocp.aws.ispworld.at &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DO NOT add a trailing slash here.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerTemplate and add the following to the appropriate blocks:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
parames:
...
- description: Cyclonedx host url
name: cyclonedxHostUrl &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
resourcetemplates:
...
spec:
params:
...
- name: CYCLONEDX_HOST_URL
value: $(tt.params.cyclonedxHostUrl) &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The parameter defined in the TriggerBinding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The parameter as it will be provided to the Pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the following Task&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: generate-sbom
namespace: ci
spec:
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- default: &amp;gt;-
https://cyclonedx-bom-repo-server-cyclonedx.apps.placeholder.com/
name: cyclonedxHostUrl
type: string
- default: &amp;#39;cyclonedx/cyclonedx-node&amp;#39;
name: cycloneDXnodeImage
type: string
results:
- description: The url location of the generate SBOM
name: sbomUrl
type: string
steps:
- image: $(params.cycloneDXnodeImage)
name: generate-sbom
resources:
requests:
memory: 1Gi
script: &amp;gt; &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
apk add git curl
npm install .
/usr/src/cyclonedx-bom/bin/make-bom.js -o bom.xml
curl -v -k $(params.cyclonedxHostUrl)/v1/bom -H &amp;#34;Content-Type:
application/vnd.cyclonedx+xml; version=1.4&amp;#34; -H &amp;#34;Accept: */*&amp;#34; -d @bom.xml
-D /tmp/header.txt
LOCATION=$(cat /tmp/header.txt | grep location: | awk &amp;#39;{print $2}&amp;#39; | sed
&amp;#39;s|http:|https:|g&amp;#39;)
printf &amp;#34;%s&amp;#34; &amp;#34;$LOCATION&amp;#34; &amp;gt; &amp;#34;$(results.sbomUrl.path)&amp;#34;
echo &amp;#34;SBOM URL accessible on Results of TaskRun $(context.taskRun.name)&amp;#34;
workingDir: /workspace/repository
workspaces:
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Default parameters for this task. Might be overwritten, by the EventListener&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Script to be executed. It will download a script and upload the SBOM to our CycloneDX server.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the Pipeline object&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
...
- name: CYCLONEDX_HOST_URL &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: string
tasks:
...
- name: generate-sbom &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
params:
- name: cyclonedxHostUrl
value: $(params.CYCLONEDX_HOST_URL)
runAfter:
- build-sign-image &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
taskRef:
kind: Task
name: generate-sbom
workspaces:
- name: repository
workspace: shared-data&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Add this to the parameter section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The new Task to generate a SBOM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Run after build-sign-image (and in parallel to the ACS tasks)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step7-pipeline.png?width=800px" alt="Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Let’s trigger the pipeline again. If everything works well, the URL to the uploaded SBOM will be visible in the TaskRun log.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step7-sbom_url.png" alt="Pipeline"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. Pipeline&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;When you open this URL, you will get a huge list of components end dependencies.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For example, the following snippet shows that the angular component &lt;strong&gt;animations&lt;/strong&gt; (version 13.2.6) is used.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-xml hljs" data-lang="xml"&gt;&amp;lt;components&amp;gt;
&amp;lt;component type=&amp;#34;library&amp;#34; bom-ref=&amp;#34;pkg:npm/%40angular/animations@13.2.6&amp;#34;&amp;gt;
&amp;lt;author&amp;gt;angular&amp;lt;/author&amp;gt;
&amp;lt;group&amp;gt;@angular&amp;lt;/group&amp;gt;
&amp;lt;name&amp;gt;animations&amp;lt;/name&amp;gt;
&amp;lt;version&amp;gt;13.2.6&amp;lt;/version&amp;gt;
&amp;lt;description&amp;gt;Angular - animations integration with web-animations&amp;lt;/description&amp;gt;
&amp;lt;licenses&amp;gt;
&amp;lt;license&amp;gt;
&amp;lt;id&amp;gt;MIT&amp;lt;/id&amp;gt;
&amp;lt;/license&amp;gt;
&amp;lt;/licenses&amp;gt;
&amp;lt;purl&amp;gt;pkg:npm/%40angular/animations@13.2.6&amp;lt;/purl&amp;gt;
&amp;lt;externalReferences&amp;gt;
&amp;lt;reference type=&amp;#34;website&amp;#34;&amp;gt;
&amp;lt;url&amp;gt;https://github.com/angular/angular#readme&amp;lt;/url&amp;gt;
&amp;lt;/reference&amp;gt;
&amp;lt;reference type=&amp;#34;issue-tracker&amp;#34;&amp;gt;
&amp;lt;url&amp;gt;https://github.com/angular/angular/issues&amp;lt;/url&amp;gt;
&amp;lt;/reference&amp;gt;
&amp;lt;reference type=&amp;#34;vcs&amp;#34;&amp;gt;
&amp;lt;url&amp;gt;git+https://github.com/angular/angular.git&amp;lt;/url&amp;gt;
&amp;lt;/reference&amp;gt;
&amp;lt;/externalReferences&amp;gt;
&amp;lt;/component&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This concludes our security scans for the whole build process. With the SBOM you have now a complete list of components and dependencies of your images.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;During the next steps, we will work with the Kubernetes manifests and update them, perform a linting on these manifests, and try to deploy the updated image to our DEV environment.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 8 - Updating Kubernetes Manifests</title><link>https://blog.stderr.at/securesupplychain/2023-06-23-securesupplychain-step8/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-23-securesupplychain-step8/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;With the finalization of the build process and the security checks during this phase, it is now time to update the Kubernetes manifests and provide the new tag for the created image.
I have forked (and cleaned up) another repository that will store all yaml specifications that are required for OpenShift. You can find this repository at: &lt;a href="https://github.com/tjungbauer/securing-software-supply-chain/tree/main/application/globex" target="_blank" rel="noopener"&gt;Kubernetes Manifests&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Update the manifest in Git with the new image tag&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_preparing_the_pipeline"&gt;Preparing the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Let’s create the Task object that will take care of the update.&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Task will use git commands to update the manifests accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: update-manifest
namespace: ci
spec:
description: &amp;gt;-
This task updates the manifest for the current application to point to the
image tag created with the short commit.
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- description: Used to tag the built image.
name: image
type: string
- default: main
description: Target branch to push to
name: target-branch
type: string
- default: Tekton Pipeline
description: Git user name for performing the push operation.
name: git_user_name
type: string
- default: tekton@tekton.com
description: Git user email for performing the push operation.
name: git_user_email
type: string
- description: File in which the image configuration is stored.
name: configuration_file
type: string
- description: Repo in which the image configuration is stored.
name: repository
type: string
- default: &amp;#39;registry.redhat.io/openshift-pipelines/pipelines-git-init-rhel8:v1.10.4-4&amp;#39;
name: gitInit
type: string
- name: verbose
description: Verbose output
type: string
default: &amp;#34;true&amp;#34;
steps: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- image: $(params.gitInit)
name: git
env:
- name: PARAM_VERBOSE
value: $(params.verbose)
resources: {}
script: &amp;gt;
#!/usr/bin/env sh
set -eu
if [ &amp;#34;${PARAM_VERBOSE}&amp;#34; = &amp;#34;true&amp;#34; ] ; then
set -x
fi
# Setting up the git config.
git config --global user.email &amp;#34;$(params.git_user_email)&amp;#34;
git config --global user.name &amp;#34;$(params.git_user_name)&amp;#34;
# Checkout target branch to avoid the detached HEAD state
TMPDIR=$(mktemp -d)
cd $TMPDIR
git clone $(params.repository)
cd securing-software-supply-chain
git checkout $(params.target-branch)
# Set to the short commit value passed as parameter.
# Notice the enclosing &amp;#34; to keep it as a string in the resulting YAML.
IMAGE=\&amp;#34;$(params.image)\&amp;#34;
sed -i &amp;#34;s#\(.*value:\s*\).*#\1 ${IMAGE}#&amp;#34; $(params.configuration_file)
git add $(params.configuration_file)
git commit -m &amp;#34;Automatically updated manifest to point to image tag
$IMAGE&amp;#34;
git push origin $(params.target-branch)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Required default parameters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Script to clone and push the update to Git.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the Pipeline we have to add the following section&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: MANIFEST_FILE
type: string
- name: MANIFEST_FILE_PROD
type: string
- name: MANIFEST_REPO
type: string
- name: MANIFEST_REPO_NAME
type: string
- name: MANIFEST_GIT_REF
type: string
...
tasks:
...
- name: update-dev-manifest
params:
- name: image
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
- name: configuration_file
value: $(params.MANIFEST_FILE)
- name: repository
value: $(params.MANIFEST_REPO)
- name: git_user_name
value: $(params.COMMIT_AUTHOR)
runAfter: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- acs-image-check
- acs-image-scan
- generate-sbom
taskRef:
kind: Task
name: update-manifest&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameters that define the settings for the manifest repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This time we are running after these three tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerBinding &lt;strong&gt;globex-ui&lt;/strong&gt;&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: manifestRepo
value: git@github.com:tjungbauer/securing-software-supply-chain.git &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: manifestRepoRef
value: main
- name: manifestFile &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
value: application/globex/overlays/dev/kustomization.yaml
- name: manifestFileProd &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
value: application/globex/overlays/prod/kustomization.yaml
- name: manifestRepoName
value: tjungbauer/securing-software-supply-chain&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The SSH url to GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The overlay that will be used for the DEV environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The overlay that will be used for the PROD environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerTemplate and add:&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
- description: The file to update to point to newly built image
name: manifestFile
- description: The file to update to point to newly built image in prod
name: manifestFileProd
- description: The repo to update to point to newly built image
name: manifestRepo
- description: The reference to the repo
name: manifestRepoRef
- description: The full name of the repo
name: manifestRepoName
...
resourcetemplates:
...
spec:
params:
- name: MANIFEST_FILE
value: $(tt.params.manifestFile)
- name: MANIFEST_FILE_PROD
value: $(tt.params.manifestFileProd)
- name: MANIFEST_REPO
value: $(tt.params.manifestRepo)
- name: MANIFEST_GIT_REF
value: $(tt.params.manifestRepoRef)
- name: MANIFEST_REPO_NAME
value: $(tt.params.manifestRepoName)
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_git_ssh_key"&gt;Git SSH Key&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To be able to do changes in Git, which is using SSH keys to authenticate, we need to specify a Secret that knows the SSH private key and Github’s host keys.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Be sure that to create an SSH key (for example using ssh-keygen) and that GitHub knows about this key. You can add your key at &lt;code&gt;&amp;#34;Account &amp;gt; Settings &amp;gt; SSH and GPG keys&amp;#34;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Run the following command to get the host keys of GitHub:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-bash hljs" data-lang="bash"&gt;ssh-keyscan -H github.com
# github.com:22 SSH-2.0-babeld-c89ab1f3
# github.com:22 SSH-2.0-babeld-c89ab1f3
|1|cr4dkqIl2SnZqktvRsFnx1lA5Ag=|eie8EHXqQHgCZJq7F/TtRRZcBbc= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
# github.com:22 SSH-2.0-babeld-c89ab1f3
|1|r6z4yeEV9Cog/2I4stZp1A34BAE=|EU8VcdD+KHMJJt9uPL4jh5zK0fI= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
# github.com:22 SSH-2.0-babeld-c89ab1f3
|1|hVnTyBzb/gSR8jpz+NUziUkHy1A=|ENMDVCVsLfz2CWLa1C+BnzZI8Yg= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
# github.com:22 SSH-2.0-babeld-c89ab1f3&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, we need to create a Secret with the following content:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: v1
kind: Secret
metadata:
annotations:
tekton.dev/git-0: github.com &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
name: git-ssh-key
namespace: ci
type: kubernetes.io/ssh-auth
data:
ssh-privatekey: &amp;lt;BASE64 PRIVAT SSH KEY &amp;gt; &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
known_hosts: &amp;lt; BASE64 of Githubs hostkey&amp;gt; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This annotation defines for which host Tekton will inject the Secret.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;A base64 decoded string of your PRIVATE ssh key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;A base64 decoded string of the host keys&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Update the &lt;strong&gt;pipeline&lt;/strong&gt; ServiceAccount and add the new Secret:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;secrets:
...
- name: git-ssh-key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Time to trigger the Pipeline, which now looks like:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step8-pipeline.png" alt="Pipeline Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Pipeline Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The new Task will automatically push the new image tag to the Kubernetes manifests Git repository. From there it can later be used to roll out the new version.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In Git you can track the changes:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step8-git_commit.png" alt="Git Commit"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Git Commit&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We have now updated the Kubernetes manifests with the new image tag. In the next steps, we will verify these manifests (linting) and then try to deploy the new image on the DEV environment.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 9 - Linting Kubernetes Manifests</title><link>https://blog.stderr.at/securesupplychain/2023-06-24-securesupplychain-step9/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-24-securesupplychain-step9/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;At this point we have checked our source code, verified that it has been signed and has no vulnerabilities, generated a SBOM and updated the Kubernetes manifests, that are responsible to deploy our application on OpenShift. As everything with OpenShift these Kubernetes objects are simple yaml files. In this step we will perform a linting on these files, to verify if they follow certain rules and best practices.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Clone the Kubernetes manifest into a workspace&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create Tasks that perform the linting&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WARN when the manifests do not follow best practices (we will not let the Task fail here, because I would need to modify all manifests)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_what_is_linting"&gt;What is linting&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;A &lt;strong&gt;linter&lt;/strong&gt; is an analysis tool that verifies if your code has any errors, bugs or stylistic errors. SonarQube could be seen as such tool. We used it to scan our source code. For Kubernetes manifests we can use tools that check the yaml files against best practices or security. For example, it could notify you if you forgot to define resources or probes in your manifests.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_tools"&gt;Tools&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In this step, I will use three linting tools. In no way, this means you need to use all three. I only use them for demonstration purposes. However, to lint your yaml files or Helm charts is a common practice you should consider.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;For this demonstration I am leveraging:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.kubelinter.io/#/" target="_blank" rel="noopener"&gt;KubeLinter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://yamllint.readthedocs.io/en/stable/" target="_blank" rel="noopener"&gt;Yamllint&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kube-score.com/" target="_blank" rel="noopener"&gt;Kube-score&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
In the end, you should choose the tool that fits best for you.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_manifests"&gt;Manifests&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The manifest we are going to use can be found at my GitHub repository: &lt;a href="https://github.com/tjungbauer/securing-software-supply-chain" target="_blank" rel="noopener"&gt;Securing Software Supply Chain&lt;/a&gt;. It is a fork and the original can be found &lt;a href="https://github.com/redhat-gpte-devopsautomation/securing-software-supply-chain" target="_blank" rel="noopener"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_prepare_pipeline"&gt;Prepare Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Modify the TriggerTemplate and add a parameter &lt;strong&gt;LINTING_INFORM_ONLY&lt;/strong&gt; that either sets the Tasks to inform or enforce (failing when linting does find issues). In addition, we define a new workspace, where we will download and store the Kubernetes manifests.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
...
- description: Only inform on linting errors (Log) but do not actually fail
name: lintingInformOnly &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
resourcetemplates:
...
spec:
params:
...
- name: LINTING_INFORM_ONLY
value: $(tt.params.lintingInformOnly)
...
workspaces:
...
- name: shared-data-manifests &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
volumeClaimTemplate:
metadata:
creationTimestamp: null
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
status: {}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;New parameter to either inform only when linting is unsuccessful or to enforce that the Task will end with an error.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspace to download and store the yaml files.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the TriggerBinding to set the values for the PipelineRun.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
...
- name: lintingInformOnly &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
value: &amp;#39;true&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Set the parameter to &amp;#39;true&amp;#39;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the Pipeline object. Here we will need to add the parameter and four Tasks (clone the Git Repo, KubeLinter, Yamllint and kube-score) as well as the workspace.&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;spec:
params:
...
- name: LINTING_INFORM_ONLY &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: string
...
- name: pull-manifests &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
params:
- name: url
value: $(params.MANIFEST_REPO)
- name: revision
value: $(params.MANIFEST_GIT_REF)
- name: deleteExisting
value: &amp;#39;true&amp;#39;
runAfter:
- update-dev-manifest &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
taskRef:
kind: ClusterTask &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
name: git-clone
workspaces: &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
- name: output
workspace: shared-data-manifests
- name: kube-linter &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;
params:
- name: informLintingOnly &lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;(7)&lt;/b&gt;
value: $(params.LINTING_INFORM_ONLY)
runAfter:
- pull-manifests &lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;(8)&lt;/b&gt;
taskRef:
kind: Task
name: kube-linter
workspaces:
- name: repository
workspace: shared-data-manifests
- name: kube-score &lt;i class="conum" data-value="9"&gt;&lt;/i&gt;&lt;b&gt;(9)&lt;/b&gt;
params:
- name: informLintingOnly
value: $(params.LINTING_INFORM_ONLY)
runAfter:
- pull-manifests &lt;i class="conum" data-value="10"&gt;&lt;/i&gt;&lt;b&gt;(10)&lt;/b&gt;
taskRef:
kind: Task
name: kube-score
workspaces:
- name: repository &lt;i class="conum" data-value="11"&gt;&lt;/i&gt;&lt;b&gt;(11)&lt;/b&gt;
workspace: shared-data-manifests
- name: yaml-lint &lt;i class="conum" data-value="12"&gt;&lt;/i&gt;&lt;b&gt;(12)&lt;/b&gt;
params:
- name: informLintingOnly
value: $(params.LINTING_INFORM_ONLY)
runAfter:
- pull-manifests &lt;i class="conum" data-value="13"&gt;&lt;/i&gt;&lt;b&gt;(13)&lt;/b&gt;
taskRef:
kind: Task
name: yaml-lint
workspaces:
- name: repository &lt;i class="conum" data-value="14"&gt;&lt;/i&gt;&lt;b&gt;(14)&lt;/b&gt;
workspace: shared-data-manifests
workspaces:
...
- name: shared-data-manifests&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;New parameter assigned to the Pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task to clone the repository to the workspace.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Will run after the Pipeline has updated the manifests with the new image.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Is a child of the ClusterTask git-clone.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The workspace to clone the repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task to execute KubeLinter.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="7"&gt;&lt;/i&gt;&lt;b&gt;7&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parameter to either enforce or inform only.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="8"&gt;&lt;/i&gt;&lt;b&gt;8&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Will run after the repository has been cloned.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="9"&gt;&lt;/i&gt;&lt;b&gt;9&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task to execute kube-score.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="10"&gt;&lt;/i&gt;&lt;b&gt;10&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Will run after the repository has been cloned.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="11"&gt;&lt;/i&gt;&lt;b&gt;11&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspace where the cloned repository can be found.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="12"&gt;&lt;/i&gt;&lt;b&gt;12&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task to execute Yamllint.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="13"&gt;&lt;/i&gt;&lt;b&gt;13&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Will run after the repository has been cloned.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="14"&gt;&lt;/i&gt;&lt;b&gt;14&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Workspace where the cloned repository can be found.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="admonitionblock warning"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-warning" title="Warning"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Remember: It is not required to execute three different linter tools. It is only done as a showcase. I personally like KubeLinter. Choose whatever tool is suitable for you.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create the different Task objects for the linter tools. Each Task will execute a linter program and provides its very own Log.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="admonitionblock caution"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-caution" title="Caution"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
I have created the image &lt;strong&gt;linter-image&lt;/strong&gt; that contains the three required binaries. It is available at Quay.io and its original Dockerfile can be found &lt;a href="https://github.com/tjungbauer/linter-image" target="_blank" rel="noopener"&gt;here&lt;/a&gt;. Use it at your own risk :).
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="olist loweralpha"&gt;
&lt;ol class="loweralpha" type="a"&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;KubeLinter&lt;/strong&gt;&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kube-linter
namespace: ci
spec:
description: &amp;gt;-
Task to run KubeLinter and perform a linting of Kubernetes manifets.
params:
- default: &amp;#39;false&amp;#39;
name: informLintingOnly
type: string
- default: &amp;#39;quay.io/tjungbau/linter-image:v1.0.2&amp;#39;
name: linterImage
type: string
steps:
- image: $(params.linterImage)
name: kube-linter
resources: {}
script: &amp;gt;
#!/usr/bin/env bash
RC=0
kube-linter lint /workspace/repository/. --config &amp;#34;/workspace/repository/.kube-linter.yaml&amp;#34; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
if [ $? -gt 0 ]; then
RC=1
fi
# We actually do not fail but inform only
if [ &amp;#34;$(params.informLintingOnly)&amp;#34; = &amp;#34;true&amp;#34; ]; then
echo &amp;#34;Informing only, task will not fail. Actual return code was $RC&amp;#34;
exit 0;
fi
(exit $RC)
workingDir: /workspace/repository
workspaces:
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Execute kube-linter using the configuration stored in the repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;kube-score&lt;/strong&gt;&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kube-score
namespace: ci
spec:
description: &amp;gt;-
Task to run kube-score and perform a linting of Kubernetes manifets.
params:
- default: &amp;#39;false&amp;#39;
name: informLintingOnly
type: string
- default: &amp;#39;quay.io/tjungbau/linter-image:v1.0.2&amp;#39;
name: linterImage
type: string
steps:
- image: $(params.linterImage)
name: kube-linter
resources: {}
script: &amp;gt;
#!/usr/bin/env bash
RC=0
KUBESCORE_IGNORE_TESTS=&amp;#34;${KUBESCORE_IGNORE_TESTS:-container-image-pull-policy,pod-networkpolicy}&amp;#34; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
for i in `find . -name &amp;#39;*.yaml&amp;#39; -type f`; do kube-score score
--ignore-test ${KUBESCORE_IGNORE_TESTS} $i; let RC=RC+$?; done
if [ $? -gt 0 ]; then
RC=1
fi
# We actually do not fail but inform only
if [ &amp;#34;$(params.informLintingOnly)&amp;#34; = &amp;#34;true&amp;#34; ]; then
echo &amp;#34;Informing only, task will not fail. Actual return code was $RC&amp;#34;
exit 0;
fi
(exit $RC)
workingDir: /workspace/repository
workspaces:
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Disable checks for Network Policies or image Pull policy for kube-score.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Yammllint&lt;/strong&gt;&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: yaml-lint
namespace: ci
spec:
description: &amp;gt;-
Task to run yamllint and perform a linting of Kubernetes manifets.
params:
- default: &amp;#39;false&amp;#39;
name: informLintingOnly
type: string
- default: &amp;#39;quay.io/tjungbau/linter-image:v1.0.2&amp;#39;
name: linterImage
type: string
steps:
- image: $(params.linterImage)
name: yaml-lint
resources: {}
script: |
#!/usr/bin/env bash
for files in `find . -type f -name &amp;#39;*.yaml&amp;#39;`; do &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
yamllint -c /workspace/repository/.yamllint.yaml ${files}; let var=var+$?
done
# We actually do not fail but inform only
if [ &amp;#34;$(params.informLintingOnly)&amp;#34; = &amp;#34;true&amp;#34; ]; then
echo &amp;#34;Informing only, task will not fail. Actual return code was $var&amp;#34;
exit 0;
fi
(exit $var)
workingDir: /workspace/repository
workspaces:
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Execute kube-linter using the configuration stored in the repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Pipeline now looks like this:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step9-pipeline.png" alt="Pipeline Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Pipeline Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Remember, you typically need only one linter tool, not three different ones. Since we inform only you will see some errors in the logs. For example, for kube-linter:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step9-kubelinter.png" alt="Kube-Linter Results"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. Kube-Linter Results&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now, all our yaml manifests have been linted, with three different tools. And because we do not fail at this stage, we can continue. The next steps will be some deployment checks.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Since everything is done using Argo CD and the manifests have been updated during the step &amp;#34;update-manifest&amp;#34;, the changes will be most likely already deployed. Even if the linting-step comes later and might even fail. This is fine because we first deploy on a DEV environment. So, if linting fails, it will prohibit the rollout to production, while some application testing can still be done on DEV.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 10 - The Example Application</title><link>https://blog.stderr.at/securesupplychain/2023-06-25-securesupplychain-step10/</link><pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-25-securesupplychain-step10/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;If you read all articles up to here (congratulations) you know that we always update the README file of &amp;#34;The Application&amp;#34;. But what is this application exactly and how can we update it like in a real-life example? The &lt;strong&gt;Globex UI&lt;/strong&gt; application is built with Angular and was prepared for this journey. It is a quite complex application and requires Kafka to be installed as well. However, since I am not a developer and this tool was already available, I forked and re-used it. The original can be found at &lt;a href="https://github.com/redhat-gpte-devopsautomation/globex-ui" target="_blank" rel="noopener"&gt;Globex UI&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Deploy AMQ Streams Operator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy Globex DEV&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy Globex Prod&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify Deployments&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_introduction"&gt;Introduction&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Globex UI, although quite complex, will provide a simple web interface. Since I do not have multiple clusters, I will use the Namespaces &lt;strong&gt;globex-dev&lt;/strong&gt; and &lt;strong&gt;globex-prod&lt;/strong&gt; to distinguish between the two environments. This means, our DEV environment will run inside globex-dev and PROD in globex-prod. Kafka related stuff will be deployed in the Namespace &lt;strong&gt;kafka&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step10-globex.png?width=600px" alt="Globex UI"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Globex UI&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The deployment will be done automatically by GitOps. As soon as our Pipeline will be executed and subsequently updates our image in the Kubernetes manifests (see step: &lt;a href="https://blog.stderr.at/openshift/securesupplychain/step9/"&gt;Linting Kubernetes Manifests&lt;/a&gt;), a new version of Globex UI will be deployed. The production update will follow in later steps in the pipeline.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;GitOps will monitor changes in our Kubernetes manifest repository. Here, we use Kustomize overlays to separate between DEV and PROD.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
As a general recommendation: Do everything with GitOps. &lt;strong&gt;If it is not in Git, it does not exist&lt;/strong&gt;.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_installation"&gt;Installation&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To install all required components, we create several GitOps Applications.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Although we create these Argo CD Applications by hand, it is recommended to put these definitions into Git also.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="admonitionblock caution"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-caution" title="Caution"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Since this is just a demo, I am using the cluster instance of OpenShift GitOps. DO NOT deploy customer workload using the default instance, since it has privileged permissions on the cluster. Instead, create a second instance (or multiple) for the developers.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The following Applications will be created:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step10-argocd_apps.png" alt="GitOps Applications"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. GitOps Applications&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_install_amq_streams_operator"&gt;Install AMQ Streams Operator&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As the very first step, we need to deploy the AMQ Streams Operator. Simply search for the operator in the Operator Hub and deploy it using the default values.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
Also, this deployment could and should be done using Argo CD. For the sake of speed, I have done this manually here.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step10-amq_streams.png?width=300px" alt="AMQ Streams Operator"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. AMQ Streams Operator&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_install_kafka_for_globex"&gt;Install Kafka for Globex&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Now we can deploy all Kafka-related stuff using GitOps. Let’s create the following Application object:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: globex-kafka
namespace: openshift-gitops
spec:
destination:
namespace: kafka &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
server: &amp;#39;https://kubernetes.default.svc&amp;#39; &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
project: default
source:
path: application/kafka/overlays/dev &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
repoURL: &amp;#39;https://github.com/tjungbauer/securing-software-supply-chain&amp;#39;
targetRevision: HEAD
syncPolicy:
automated: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
selfHeal: true
syncOptions:
- RespectIgnoreDifferences=true
- CreateNamespace=true&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The deployment must be done in the Namespace &lt;strong&gt;kafka&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We install using the local GitOps server instance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Path/URL to the GitHub Kustomize repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Auto-Update in case of changes are detected.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_install_globex_dev"&gt;Install Globex DEV&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The DEV installation will deploy the test version of our application into the Namespace &lt;strong&gt;globex-dev&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: globex-dev &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: openshift-gitops
spec:
destination:
namespace: globex-dev &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
server: &amp;#39;https://kubernetes.default.svc&amp;#39; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
ignoreDifferences:
- group: &amp;#39;*&amp;#39;
jqPathExpressions:
- &amp;#39;.imagePullSecrets[] | select(.name|test(&amp;#34;.-dockercfg-.&amp;#34;))&amp;#39;
kind: ServiceAccount
project: default
source:
path: application/globex/overlays/dev &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
repoURL: &amp;#39;https://github.com/tjungbauer/securing-software-supply-chain&amp;#39;
targetRevision: HEAD
syncPolicy:
automated: &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
selfHeal: true
syncOptions:
- RespectIgnoreDifferences=true
- CreateNamespace=true &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Globex DEV instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Install into the Namespace &lt;strong&gt;globex-dev&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We install using the local GitOps server instance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Path/URL to the GitHub Kustomize repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Auto-Update in case of changes are detected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Create the Namespace if it does not exist.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_install_globex_prod"&gt;Install Globex PROD&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;And finally, the same for the PROD instance:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: globex-prod &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: openshift-gitops
spec:
destination:
namespace: globex-prod &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
server: &amp;#39;https://kubernetes.default.svc&amp;#39; &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
ignoreDifferences:
- group: &amp;#39;*&amp;#39;
jqPathExpressions:
- &amp;#39;.imagePullSecrets[] | select(.name|test(&amp;#34;.-dockercfg-.&amp;#34;))&amp;#39;
kind: ServiceAccount
project: default
source:
path: application/globex/overlays/prod &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
repoURL: &amp;#39;https://github.com/tjungbauer/securing-software-supply-chain&amp;#39;
targetRevision: HEAD
syncPolicy:
automated: &lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;(5)&lt;/b&gt;
selfHeal: true
syncOptions:
- RespectIgnoreDifferences=true
- CreateNamespace=true &lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;(6)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Globex DEV instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Install into the Namespace &lt;strong&gt;globex-prod&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;We install using the local GitOps server instance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Path/URL to the GitHub Kustomize repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="5"&gt;&lt;/i&gt;&lt;b&gt;5&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Auto-Update in case of changes are detected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="6"&gt;&lt;/i&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Create the Namespace if it does not exist.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;During this step we have added nothing new to our pipeline, but deployed our example application &lt;strong&gt;Globex UI&lt;/strong&gt; instead, including Kafka. The next steps will now do another verification against ACS and if the transparency logs are available, and then finally prepares everything for production deployment.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 11 - ACS Deployment Check</title><link>https://blog.stderr.at/securesupplychain/2023-06-26-securesupplychain-step11/</link><pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-26-securesupplychain-step11/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;After the Pipeline prepared the new image for DEV it should be checked against ACS for policy compliance. This ensures that the deployment manifest adheres to policy requirements. The command line tool &lt;strong&gt;roxctl&lt;/strong&gt; will be leveraged to perform this task.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a Task that performs a deployment check using roxctl&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_create_the_task"&gt;Create the Task&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Create the following Task object. This Task is a bit more complex, since we want to verify the Deployment, we first need to build the Kustomize files which would create a big yaml file and will store it into the output folder (all.yaml). Then we use &lt;a href="https://github.com/looztra/kubesplit" target="_blank" rel="noopener"&gt;kubesplit&lt;/a&gt; to split this huge file into different smaller ones. Finally, the roxctl step will search for a file called &lt;strong&gt;deployment—​globex-ui.yml&lt;/strong&gt; and performs the security checks (verify if any security policy is violated in ACS) against this file.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: acs-deploy-check
namespace: ci
spec:
description: &amp;gt;-
Policy check a deployment with StackRox/RHACS This tasks allows you to check
a deployment against build-time policies and apply enforcement to fail
builds. It&amp;#39;s a companion to the stackrox-image-scan task, which returns full
vulnerability scan results for an image.
params:
- description: |
Secret containing the address:port tuple for StackRox Central)
(example - rox.stackrox.io:443)
name: rox_central_endpoint
type: string
- description: Secret containing the StackRox API token with CI permissions
name: rox_api_token
type: string
- default: &amp;#39;https://default-git-url.git&amp;#39;
name: gitRepositoryUrl
type: string
- default: main
name: gitRepositoryRevision
type: string
- default: &amp;#39;true&amp;#39;
name: verbose
type: string
- default: &amp;#39;false&amp;#39;
description: |
When set to `&amp;#34;true&amp;#34;`, skip verifying the TLS certs of the Central
endpoint. Defaults to `&amp;#34;false&amp;#34;`.
name: insecure-skip-tls-verify
type: string
- default: &amp;#39;quay.io/wpernath/kustomize-ubi&amp;#39;
name: kustomieBuildImage
type: string
- default: &amp;#39;looztra/kubesplit&amp;#39;
name: kubesplitImage
type: string
- default: &amp;#39;registry.access.redhat.com/ubi8:8.7-1026&amp;#39;
name: ubi8Image
type: string
results:
- description: Output of `roxctl deployment check`
name: check_output
type: string
steps:
- image: $(params.kustomieBuildImage)
name: kustomize-build
resources: {}
script: | &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
#!/usr/bin/env sh
set -eu -o pipefail
cd /workspace/repository/application/globex/overlays/dev
mkdir -p /workspace/repository/input
mkdir -p /workspace/repository/output
kustomize build . --output /workspace/repository/input/all.yaml
workingDir: /workspace/repository
- image: $(params.kubesplitImage) &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
name: kustomize-split
resources: {}
script: &amp;gt;
#!/usr/bin/env sh
set -eu -o pipefail
kubesplit -i /workspace/repository/input/all.yaml -o
/workspace/repository/output
workingDir: /workspace/repository
- env:
- name: ROX_API_TOKEN
valueFrom:
secretKeyRef:
key: rox_api_token
name: $(params.rox_api_token)
- name: ROX_CENTRAL_ENDPOINT
valueFrom:
secretKeyRef:
key: rox_central_endpoint
name: $(params.rox_central_endpoint)
image: $(params.ubi8Image)
name: rox-deploy-scan &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
resources: {}
script: |
#!/usr/bin/env bash
set +x
cd /workspace/repository/output
curl -s -k -L -H &amp;#34;Authorization: Bearer $ROX_API_TOKEN&amp;#34; \
&amp;#34;https://$ROX_CENTRAL_ENDPOINT/api/cli/download/roxctl-linux&amp;#34; \
--output ./roxctl \
&amp;gt; /dev/null
chmod +x ./roxctl &amp;gt; /dev/null
DEPLOYMENT_FILE=$(ls -1a | grep *deployment--globex-ui.yml)
if [ &amp;#34;$(params.insecure-skip-tls-verify)&amp;#34; = &amp;#34;true&amp;#34; ]; then
export ROX_INSECURE_CLIENT_SKIP_TLS_VERIFY=true
fi
./roxctl deployment check -e &amp;#34;$ROX_CENTRAL_ENDPOINT&amp;#34; --file &amp;#34;$DEPLOYMENT_FILE&amp;#34;
workingDir: /workspace/repository
workspaces:
- name: repository&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Build Kustomize and store the output into /workspace/repository/input/all.yaml.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Split the huge yaml file into separate ones and store them into /workspace/repository/output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Search for the file deployment—​globex-ui.yml and perform a &lt;strong&gt;roxctl deployment check&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_update_the_pipeline"&gt;Update the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Pipeline object must be extended with another Task:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: acs-deploy-check
params:
- name: rox_central_endpoint &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
value: stackrox-endpoint
- name: rox_api_token
value: stackrox-secret
- name: insecure-skip-tls-verify
value: &amp;#39;true&amp;#39;
runAfter: &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
- yaml-lint
- kube-score
- kube-linter
taskRef:
kind: Task
name: acs-deploy-check
workspaces:
- name: repository
workspace: shared-data-manifests &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The parameters required for ACS.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This task runs after the linting tasks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The workspace, where the manifests have been pulled.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Again, we trigger our pipeline by simply updating the README.md of our source code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The ACS check will verify if any security policies, that are valid for Deployment-states, are violated.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;My test returned the following result.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step11-results.png" alt="Pipeline Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Pipeline Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;However, since the policies are configured to &amp;#34;inform only&amp;#34;, the PipelineRun will finish successfully.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;All ACS checks have been done now. The deployment does not violate any security policy that is configured in ACS …​ or to be more exact: It does not violate enforced policy, thus the Task will end successfully.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 12 - Verify TLog Signature</title><link>https://blog.stderr.at/securesupplychain/2023-06-27-securesupplychain-step12/</link><pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-27-securesupplychain-step12/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;Since the image has been deployed on DEV now, we need to prepare everything for production. Before we start, we need to confirm that the whole signing process has been passed and that our signature has been applied correctly. With CoSign we signed our image and used Rekor to create a transparency log. We will use this log to confirm that the image was signed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Verify if the image has been signed using Rekor transparency log.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_cosign_public_key"&gt;CoSign public key&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Before we start, we need a secret in the Namespace &lt;strong&gt;ci&lt;/strong&gt; that provides the PUBLIC key of CoSign. This key was generated in a previous &lt;a href="https://blog.stderr.at/securesupplychain/2023-06-20-securesupplychain-step5/#_cosign_signing_the_image"&gt;Step 5 - CoSign - Signing the Image&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: cosign-secret &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
namespace: ci
data:
cosign.pub: &amp;gt;-
&amp;lt;base64 CoSign PUBLIC key&amp;gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;base64 decoded PUBLIC key of CoSign&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_create_the_task"&gt;Create the Task&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The following task will use several steps to fetch and parse the transparency log, that has been created. The different tasks are using images from &lt;strong&gt;Redhat-gpte&lt;/strong&gt;. The command line tool &lt;strong&gt;rekor-cli&lt;/strong&gt; will be used to verify the entries in the log.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: rekor-verify
namespace: ci
spec:
params:
- default: image-registry-secret &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
name: registrySecret
type: string
- default: cosign-secret &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
name: cosignSecret
type: string
- default: &amp;gt;-
quay.io/placeholder/test:latest
name: image
type: string
- default: &amp;#39;quay.io/tjungbau/pipeline-tools:v1.1.0&amp;#39;
name: pipelinetoolsImage
type: string
steps:
- env:
- name: REGISTRY_SECRET
valueFrom:
secretKeyRef:
key: .dockerconfigjson
name: $(params.registrySecret)
- name: COSIGN_PUBLIC_KEY
valueFrom:
secretKeyRef:
key: cosign.pub
name: $(params.cosignSecret)
image: $(params.pipelinetoolsImage)
name: cosign-verify-image
resources: {}
script: &amp;gt;
mkdir -p /home/cosign/.docker/
echo &amp;#34;${REGISTRY_SECRET}&amp;#34; &amp;gt; /home/cosign/.docker/config.json
echo &amp;#34;${COSIGN_PUBLIC_KEY}&amp;#34; &amp;gt; /workspace/cosign.pub
cosign verify --key /workspace/cosign.pub $(params.image) --output-file
/workspace/cosign.verify
cat /workspace/cosign.verify | jq --raw-output &amp;#39;.[0] | .critical |
.image | .[&amp;#34;docker-manifest-digest&amp;#34;]&amp;#39; &amp;gt; /workspace/cosign.sha
rekor-cli search --sha $(cat /workspace/cosign.sha) --format json &amp;gt;
/workspace/rekor.search
cat /workspace/rekor.search | jq &amp;#39;.UUIDs[0]&amp;#39; | sed &amp;#39;s/\&amp;#34;//g&amp;#39; &amp;gt;
/workspace/rekor.uuid
rekor-cli get --uuid $(cat /workspace/rekor.uuid) --format json &amp;gt;
/workspace/rekor.get
cat /workspace/rekor.get | jq -r .Attestation&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Registry pull secret.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CoSign public key.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_update_the_pipeline"&gt;Update the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Pipeline object must be extended with another Task:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: verify-tlog-signature
params: &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
- name: registrySecret &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
value: tjungbau-secure-supply-chain-demo-pull-secret
- name: cosignSecret &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
value: cosign-secret
- name: image
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
runAfter: &lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;(4)&lt;/b&gt;
- yaml-lint
- kube-score
- kube-linter
taskRef:
kind: Task
name: rekor-verify&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The parameters required for this task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Pull secret for quay.io, which was created during step 5.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CoSign public key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="4"&gt;&lt;/i&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This task runs after the linting tasks.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_the_pipeline"&gt;Execute the Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Triggering the pipeline will now check the signature of an image.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The logs should show something like this:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step12-signature_verification.png" alt="Rekor Image Signature Verification"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. Rekor Image Signature Verification&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As you can see: The image has been signed correctly and the transparency logs can be parsed.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_summary"&gt;Summary&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Finally, everything has been verified and we can bring everything into production. The final two steps will create a new branch in the Git manifest repository and a pull request that can be manually merged.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Step 13 - Bring it to Production</title><link>https://blog.stderr.at/securesupplychain/2023-06-28-securesupplychain-step13/</link><pubDate>Tue, 27 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.stderr.at/securesupplychain/2023-06-28-securesupplychain-step13/</guid><description>&lt;div class="paragraph"&gt;
&lt;p&gt;If you reached this article, congratulations! You read through tons of pages to build up a Pipeline. The last two steps in our Pipeline are: Creating a new branch and creating a pull request, with the changes of the image tag that must be approved and will be merged then (We will not update the main branch directly!). Finally, we will do a &amp;#34;real&amp;#34; update to the application to see the actual changes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_goals"&gt;Goals&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The goals of this step are:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a task that creates a new branch in Git repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a task that creates a pull request in Git repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Perform a full End2End run of the Pipeline and approve the pull request.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_create_a_token_at_github"&gt;Create a token at GitHub&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To create a pull request we need to authenticate against the Git api. For this, we will need a token. In GitHub open your personal settings (by clicking on your avatar) and then go to &amp;#34;Developer settings&amp;#34;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Select &amp;#34;Personal access tokens&amp;#34; and &amp;#34;fine-graining tokens&amp;#34; to create a new token.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-github_pta.png?width=400px" alt="GitHub Personal Access Token"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 1. GitHub Personal Access Token&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Enter the required fields:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Name: Secure Supply Chain&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Expiration: Whatever your like (max is 1 year)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repository access: Only select repositories&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select your repository, for example: tjungbauer/securing-software-supply-chain&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Permissions: &amp;#34;Pull requests&amp;#34; &amp;gt; Read and write&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-github_pta_created.png?width=600px" alt="GitHub Personal Access Token Created"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 2. GitHub Personal Access Token Created&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Save the created token and create the following secret:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;kind: Secret
apiVersion: v1
metadata:
name: github-token
namespace: ci
stringData:
token: &amp;lt;Token Value&amp;gt; &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
type: Opaque&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Clear-text token. If already base64 encoded, change &lt;em&gt;stringData&lt;/em&gt; to &lt;em&gt;data&lt;/em&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_task_create_a_new_branch"&gt;Task: Create a new branch&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Since we do not update the main branch directly, we will create a new (feature) branch, that will be used for a pull request and can be deleted after the pull request has been merged.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create the following Task object&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This Task will use git commands to create a new feature-branch and pushes the changes into that branch.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: new-branch-manifest-repo
namespace: ci
spec:
description: &amp;gt;-
This task creates a branch for a PR to point to the image tag created with
the short commit.
params:
- description: Used to tag the built image.
name: image
type: string
- default: main
description: Target branch to push to
name: target-branch
type: string
- default: Tekton Pipeline
description: Git user name for performing the push operation.
name: git_user_name
type: string
- default: tekton@tekton.com
description: Git user email for performing the push operation.
name: git_user_email
type: string
- description: File in which the image configuration is stored.
name: configuration_file
type: string
- description: Repo in which the image configuration is stored.
name: repository
type: string
- default: &amp;#39;registry.redhat.io/openshift-pipelines/pipelines-git-init-rhel8:v1.10.4-4&amp;#39;
name: gitInit
type: string
steps:
- image: $(params.gitInit)
name: git
resources: {}
script: &amp;gt;-
# Setting up the git config.
git config --global user.email &amp;#34;$(params.git_user_email)&amp;#34;
git config --global user.name &amp;#34;$(params.git_user_name)&amp;#34;
# Checkout target branch to avoid the detached HEAD state
TMPDIR=$(mktemp -d)
cd $TMPDIR
git clone $(params.repository) &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
cd securing-software-supply-chain
git checkout -b $(params.target-branch) &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
# Set to the short commit value passed as parameter.
# Notice the enclosing &amp;#34; to keep it as a string in the resulting YAML.
IMAGE=\&amp;#34;$(params.image)\&amp;#34;
sed -i &amp;#34;s#\(.*value:\s*\).*#\1 ${IMAGE}#&amp;#34; $(params.configuration_file)
git add $(params.configuration_file) &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
git commit -m &amp;#34;Automatically updated manifest to point to image tag
$IMAGE&amp;#34;
git push origin $(params.target-branch)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Clone the main repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Create a new feature branch.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Add, commit, and push everything to the new branch.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify the Pipeline object&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Task must be added to the Pipeline, it provides several required parameters.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: create-prod-manifest-branch
params:
- name: image
value: &amp;#39;$(params.IMAGE_REPO):$(params.IMAGE_TAG)&amp;#39;
- name: configuration_file
value: $(params.MANIFEST_FILE_PROD)
- name: repository
value: $(params.MANIFEST_REPO)
- name: git_user_name
value: $(params.COMMIT_AUTHOR)
- name: target-branch
value: feature-for-$(params.COMMIT_SHA)
runAfter:
- acs-deploy-check
- verify-tlog-signature
taskRef:
kind: Task
name: new-branch-manifest-repo&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_task_create_a_pull_request"&gt;Task: Create a Pull request&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="olist arabic"&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;
&lt;p&gt;Create the following Task object&lt;/p&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The following task will take the token and create a new pull request at GitHub:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt;apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: git-open-pull-request
namespace: ci
spec:
description: &amp;gt;-
This task will open a PR on Github based on several parameters. This could
be useful in GitOps repositories for example.
params:
- default: api.github.com
description: |
The GitHub host, adjust this if you run a GitHub enteprise or Gitea
name: GITHUB_HOST_URL
type: string
- default: &amp;#39;&amp;#39;
description: |
The API path prefix, GitHub Enterprise has a prefix e.g. /api/v3
name: API_PATH_PREFIX
type: string
- description: |
The GitHub repository full name, e.g.: tektoncd/catalog
name: REPO_FULL_NAME
type: string
- default: github
description: &amp;gt;
The name of the kubernetes secret that contains the GitHub token,
default: github
name: GITHUB_TOKEN_SECRET_NAME
type: string
- default: token
description: &amp;gt;
The key within the kubernetes secret that contains the GitHub token,
default: token
name: GITHUB_TOKEN_SECRET_KEY
type: string
- default: Bearer
description: &amp;gt;
The type of authentication to use. You could use the less secure &amp;#34;Basic&amp;#34;
for example
name: AUTH_TYPE
type: string
- description: |
The name of the branch where your changes are implemented.
name: HEAD
type: string
- description: |
The name of the branch you want the changes pulled into.
name: BASE
type: string
- description: |
The body description of the pull request.
name: BODY
type: string
- description: |
The title of the pull request.
name: TITLE
type: string
- default: &amp;#39;registry.access.redhat.com/ubi8/python-38:1&amp;#39;
name: ubi8PythonImage
type: string
results:
- description: Number of the created pull request.
name: NUMBER
type: string
- description: URL of the created pull request.
name: URL
type: string
steps:
- env:
- name: PULLREQUEST_NUMBER_PATH
value: $(results.NUMBER.path)
- name: PULLREQUEST_URL_PATH
value: $(results.URL.path)
image: $(params.ubi8PythonImage)
name: open-pr
resources: {}
script: &amp;gt;-
#!/usr/libexec/platform-python &lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;(1)&lt;/b&gt;
&amp;#34;&amp;#34;&amp;#34;This script will open a PR on Github&amp;#34;&amp;#34;&amp;#34;
import json
import os
import sys
import http.client
github_token = &lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;(2)&lt;/b&gt;
open(&amp;#34;/etc/github-open-pr/$(params.GITHUB_TOKEN_SECRET_KEY)&amp;#34;,
&amp;#34;r&amp;#34;).read()
open_pr_url = &amp;#34;/repos/$(params.REPO_FULL_NAME)/pulls&amp;#34;
data = { &lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;(3)&lt;/b&gt;
&amp;#34;head&amp;#34;: &amp;#34;$(params.HEAD)&amp;#34;,
&amp;#34;base&amp;#34;: &amp;#34;$(params.BASE)&amp;#34;,
&amp;#34;title&amp;#34;: &amp;#34;&amp;#34;&amp;#34;$(params.TITLE)&amp;#34;&amp;#34;&amp;#34;,
&amp;#34;body&amp;#34;: &amp;#34;&amp;#34;&amp;#34;$(params.BODY)&amp;#34;&amp;#34;&amp;#34;
}
print(&amp;#34;Sending this data to GitHub: &amp;#34;)
print(data)
authHeader = &amp;#34;Bearer &amp;#34; + github_token
giturl = &amp;#34;api.&amp;#34;+&amp;#34;$(params.GITHUB_HOST_URL)&amp;#34;
conn = http.client.HTTPSConnection(giturl)
conn.request(
&amp;#34;POST&amp;#34;,
open_pr_url,
body=json.dumps(data),
headers={
&amp;#34;User-Agent&amp;#34;: &amp;#34;OpenShift Pipelines&amp;#34;,
&amp;#34;Authorization&amp;#34;: authHeader.strip(),
&amp;#34;Accept&amp;#34;: &amp;#34;application/vnd.github+json&amp;#34;,
&amp;#34;Content-Type&amp;#34;: &amp;#34;application/json&amp;#34;,
&amp;#34;X-GitHub-Api-Version&amp;#34;: &amp;#34;2022-11-28&amp;#34;
})
resp = conn.getresponse()
if not str(resp.status).startswith(&amp;#34;2&amp;#34;):
print(&amp;#34;Error: %d&amp;#34; % (resp.status))
print(resp.read())
sys.exit(1)
else:
# https://docs.github.com/en/rest/reference/pulls#create-a-pull-request
body = json.loads(resp.read().decode())
open(os.environ.get(&amp;#39;PULLREQUEST_NUMBER_PATH&amp;#39;), &amp;#39;w&amp;#39;).write(f&amp;#39;{body[&amp;#34;number&amp;#34;]}&amp;#39;)
open(os.environ.get(&amp;#39;PULLREQUEST_URL_PATH&amp;#39;), &amp;#39;w&amp;#39;).write(body[&amp;#34;html_url&amp;#34;])
print(&amp;#34;GitHub pull request created for $(params.REPO_FULL_NAME): &amp;#34;
f&amp;#39;number={body[&amp;#34;number&amp;#34;]} url={body[&amp;#34;html_url&amp;#34;]}&amp;#39;)
volumeMounts:
- mountPath: /etc/github-open-pr
name: githubtoken
readOnly: true
volumes:
- name: githubtoken
secret:
secretName: $(params.GITHUB_TOKEN_SECRET_NAME)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="colist arabic"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="1"&gt;&lt;/i&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Python script to create the pull request.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="2"&gt;&lt;/i&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The token from the secret object.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;i class="conum" data-value="3"&gt;&lt;/i&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;The data we will send to GitHub.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify the Pipeline object&lt;/p&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-yaml hljs" data-lang="yaml"&gt; - name: issue-prod-pull-request
params:
- name: GITHUB_HOST_URL
value: $(params.REPO_HOST)
- name: GITHUB_TOKEN_SECRET_NAME
value: github-token
- name: REPO_FULL_NAME
value: $(params.MANIFEST_REPO_NAME)
- name: HEAD
value: feature-for-$(params.COMMIT_SHA)
- name: BASE
value: main
- name: BODY
value: Update prod image for $(params.COMMIT_MESSAGE)
- name: TITLE
value: &amp;#39;Production update: $(params.COMMIT_MESSAGE)&amp;#39;
runAfter:
- create-prod-manifest-branch
taskRef:
kind: Task
name: git-open-pull-request&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_review_the_whole_pipeline"&gt;Review the whole Pipeline&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We did it, we created a Secure Supply Chain using Tekton Tasks. The full Pipeline now looks like this:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-pipeline.png" alt="Pipeline Details"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 3. Pipeline Details&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The last step will create a pull request on Git. When this request is approved and merged, the update will finally happen in the production environment. This is a manual process to have control what comes in production and what does not.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_execute_full_pipeline_e2e"&gt;Execute full Pipeline E2E&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It is time to execute the whole pipeline now end to end. We will do a real update to the application now, so we can see the differences.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;As described in step 10, the DEV and PROD environments are running on the same cluster. In the field, this will probably not happen, but for now, it is good enough. GitOps/Argo CD monitors any changes and automatically updates whenever the Git repository (Kubernetes Manifests) is changed. During the PipelineRun we will update the image tag for DEV, which automatically rolls out and create a Pull request which is waiting for approval and will roll out the changes onto production.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Both environments have a route to access the application. At the moment both will look the same:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-dev_origin.png?width=600px" alt="Globex DEV origin"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 4. Globex DEV origin&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect2"&gt;
&lt;h3 id="_update_application"&gt;Update application&lt;/h3&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The repository of Globex UI is forked at: &lt;a href="https://github.com/tjungbauer/globex-ui" class="bare"&gt;https://github.com/tjungbauer/globex-ui&lt;/a&gt;. We used it throughout this journey to update the README.md file.
The readme file does not really change anything. So, let’s update the UI itself.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;look for the file &lt;strong&gt;src/index.html&lt;/strong&gt; and add the following line before &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="listingblock"&gt;
&lt;div class="content"&gt;
&lt;pre class="highlightjs highlight"&gt;&lt;code class="language-html hljs" data-lang="html"&gt;&amp;lt;center&amp;gt;&amp;lt;strong&amp;gt;My very important update&amp;lt;/strong&amp;gt;&amp;lt;/center&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Save this change and push it to GitHub.
This will trigger the Pipeline which is running quite long. However, once it is finished, the DEV environment should now show the new line in the UI.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonitionblock note"&gt;
&lt;table&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class="icon"&gt;
&lt;i class="fa icon-note" title="Note"&gt;&lt;/i&gt;
&lt;/td&gt;
&lt;td class="content"&gt;
After the pipeline updated the image tag in Git, the GitOps process must fetch this change. This may take a while. You can speed this up by refreshing the &amp;#34;Application&amp;#34; inside the Argo CD interface. It should then automatically synchronize.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The update can now be seen in the browser. The &amp;#34;important update&amp;#34; is visible at the bottom of the page.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-dev_updated.png?width=600px" alt="Globex DEV updated"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 5. Globex DEV updated&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The production environment was not yet updated. Instead, a pull request has been created:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-open_pr.png?width=600px" alt="Open pull request"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 6. Open pull request&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This request can be reviewed and merged. As you can see there was only one change in the files:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-pr_fileschanged.png" alt="Open pull request - changed files"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 7. Open pull request - changed files&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Merge the pull request and wait until Argo CD fetched the changes and updates the production environment. This is it, the changes are done and promoted to production:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img src="https://blog.stderr.at/securesupplychain/images/step13-prod_updated.png?width=600px" alt="Globex PROD updated"/&gt;
&lt;/div&gt;
&lt;div class="title"&gt;Figure 8. Globex PROD updated&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="sect1"&gt;
&lt;h2 id="_conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;div class="sectionbody"&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;This concludes this journey to a Secure Supply Chain using Tekton (OpenShift Pipelines). Is this the best must-have you need to do? No, it is an example, a demonstration. Feel free to use and modify it. You can also use other tools for the tasks or the pipeline as such. It does not matter if you use Tekton, Jenkins, Gitlab Runner etc. What is important is that you secure your whole supply chain as much as possible.
Any image you create should be signed to ensure that the source can be trusted. Every source code should be verified against best practices and all images should be scanned for vulnerabilities and policy violations during the build AND the deployment process.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item></channel></rss>