Kyverno and SLSA 3

How the Kyverno project believes it is meeting SLSA Level 3 requirements.

With the release of Kyverno 1.9, Kyverno has begun generating and attesting to the provenance of its release artifacts in the SLSA standard and provisionally meet Level 3. This blog post attempts to explain a bit about SLSA and Level 3 and how we meet the requirements. Once the Open Source Security Foundation (OpenSSF) establishes its conformance program, we hope to see official acknowledgement of this process.

About SLSA

Supply Chain Levels for Software Artifacts, or SLSA (pronounced “salsa”), is a security framework which aims to prevent tampering and secure artifacts in a project. SLSA helps in mitigating supply chain threats. SLSA compliance is based on four levels. Level 1 starts with basic requirements and achieving level 4 requires strict hardening of the supply chain platform.

The following diagram depicts the several known points of attacks. For extensive documentation, refer to https://slsa.dev.

slsa

(Graphic source: slsa.dev)

SLSA Requirements and Kyverno Compliance State

SLSA divides the requirements into three main areas: source, build, and integrity. Each area has sub-requirements to meet certain levels. Since Kyverno claims to achieve SLSA level 3, this document will explain how we are achieving that. For a complete summary of SLSA requirements, refer to the official SLSA project pages at https://slsa.dev/spec/v0.1/requirements.

Source Requirements

RequirementRequired at SLSA Level 3Met by Kyverno
Version controlledYesYes
Verified historyYesYes
Retained indefinitelyYes (for 18 months or above)Yes

Version Controlled

Description: The source code should be tracked in a version-controlled system. The version control system should maintain the history of changes. The identification of uploaders and reviewers (if any), timestamps of the reviews, its content, and the parent reviews. Each revision should be tracked back using an immutable reference. These requirements are met by most of the revision systems, e.g git.

Kyverno Processes: Kyverno uses GitHub for source code management. Each commit gives info about the author, an explanation about the commit, and the time at which the commit was generated.

Verified History

Description: The history of a revision must be trackable. It should contain a timestamp and a strongly authenticated user (author, uploader, reviewer, etc.). The identities must use two-step verification or similar.

Kyverno Processes: Each commit in Kyverno contains a timestamp and info about the author. It also gives an explanation about the commit.

Retained Indefinitely

Description: If there are no legal or policy requirements, the revision and its change history must be preserved indefinitely and cannot be deleted. For SLSA Level 3, the retention can be limited to 18 months.

Kyverno Processes: The git tree remains unaltered. The source code of the build is archived.

Build Requirements

The conversion of source code into the build is the responsibility of the build system. The build system must be secured against any sort of outside intervention. The build system should produce the builds in a reproducible manner for verification.

RequirementRequired at SLSA Level 3Met by Kyverno
Scripted buildYesYes
Build serviceYesYes
Build as codeYesYes
Ephemeral environmentYesYes
IsolatedYesYes

Scripted Build

Description: All build steps should be defined in the build script. e.g Makefile or GitHub action workflow file. The invocation command for build script is the only manual command allowed.

Kyverno Processes: Since the Kyverno code base is stored in GitHub, GitHub Actions are used to invoke build scripts. Kyverno uses a mix of Makefile and GitHub Workflows. Everything is declared inside build scripts.

Build Service

Description: The build step should be executed on some build service environment e.g GitHub Action, AWS CodePipeline. The developer workstation doesn’t qualify as a build service.

Kyverno Processes: As mentioned above, the Kyverno source code is stored in GitHub. Kyverno relies on GitHub Actions as a build service environment.

Build As Code

Description: The build definition and configuration used by the build service to generate the build must be derived from text files. The build-as-code files need to be stored in the version control system.

Kyverno Processes: The build is generated by executing GitHub workflows. The build definition and configuration are all defined in workflow files under the directory .github/workflows/ in the kyverno/kyverno repository.

Ephemeral Environment

Description: The build step should be executed in an ephemeral environment. The environment should be solely created for this build and must not be reused. e.g container or VM.

Kyverno Processes: Kyverno relies on GitHub Actions as a build service environment. GitHub uses the concept of runners and the Kyverno project uses GitHub-hosted runners. For each job, a new virtual machine is created.

Isolated

Description: The build step should be executed independently of each other. The build instance, either prior or concurrent, should not influence others.

Kyverno Processes: The GitHub-hosted runners run each job in a separate virtual machine. When the job has finished, the VM is automatically decommissioned.

Provenance Requirements

In order to prove that build was produced and artifacts were produced according to SLSA Level 3, SLSA mentions some requirements for provenance which can be grouped into the following:

  • Requirements on the process by which provenance is generated and consumed
  • Requirements on the content of the provenance

Kyverno relies on the official SLSA GitHub Generator project for provenance generation. For more information on the generator project, see https://github.com/slsa-framework/slsa-github-generator.

Requirements on the process by which provenance is generated and consumed

RequirementRequired at SLSA Level 3Met by Kyverno
AvailableYesYes
AuthenticatedYesYes
Service generatedYesYes
Non-falsifiableYesYes

Available

Description: The provenance should be provided to the customer in a format accepted by the customer.

Kyverno Processes: Kyverno relies on the official SLSA GitHub generator framework to provide provenance in the in-toto format. The provenance is attested using the Sigstore cosign project and is publicly available as an image artifact on GitHub Container Registry stored in the same repository as the Kyverno container images themselves. This repository and its contents can be publicly pulled without authentication.

Authenticated

Description: The consumer can verify the authenticity and integrity of the provenance.

Kyverno Processes: The provenance is signed by OIDC identity and the public key to verify the provenance is stored in the public Rekor transparency log. If someone tampers with the file then the signature will fail to verify. Anyone can verify the provenance for Kyverno artifacts with the process documented here.

Service Generated

Description: The provenance data should be obtained from the build service.

Kyverno Processes: The GitHub Action reusable workflow hosted by the SLSA GitHub Generator project creates the provenance file. The caller workflow from the Kyverno project is release.yaml and the called workflow is generator_container_slsa3.yml. The necessary data to the called workflow is passed in the form of variables and secrets. The called workflow is a third-party workflow which is used by Kyverno for provenance generation and any actions in the called workflow run as if they were part of the caller workflow.

Non-Falsifiable

Description: The build service’s users can not falsify the provenance.

Kyverno Processes: GitHub takes care of avoiding interference with the build system. GitHub uses ephemeral and isolated virtual machines, no one can persistently compromise this environment. GitHub automatically provisions a new VM for that job. When the job execution is finished, the VM is automatically decommissioned. Use of the SLSA GitHub generator separates the signing from building so the Kyverno build itself never has access to the signing secrets. Use of OIDC-based secrets through Sigstore’s keyless signing means the ephemeral signing secret is associated only with one specific build making it easy to detect secret theft and an attempt at signing something else.

Provenance Content Requirements

RequirementRequired at SLSA Level 3Met by Kyverno
Identifies artifactYesYes
Identifies builderYesYes
Identifies build instructionsYesYes
Identifies source codeYesYes
Identifies entry pointYesYes
Includes all build parametersYesYes

Identifies Artifact

Description: The output artifact must be identified by provenance by at least one cryptographic hash.

Kyverno Processes: The provenance file stores SHA-256 hashes of build artifacts. The provenance identifies the container image using its digest in SHA-256 format.

Identifies Builder

Description: The entity who executed the build process and generated the provenance should be identified by provenance.

Kyverno Processes: The id of the builder is added to provenance in the predicate.builder section. In case of Kyverno, GitHub Actions act as the builder. The build logic is defined inside GitHub workflows file. The provenance generation logic is in a separate reusable workflow which is recorded in the predicate.builder section. As an example, refer to the Provenance Example section.

Identifies Build Instructions

Description: The top-level instruction that was executed to initiate the build should be available in the provenance file.

Kyverno Processes: The top-level instruction is the GitHub Action workflow which is calling the provenance generation workflow. This is recorded in invocation.entrypoint in the provenance file. In Kyverno’s case, it is release.yaml. The SLSA requirements mentions that “The identified instructions should be at the highest level available to the build” so it doesn’t necessarily need to record all the the instructions as they are part of workflow file. As an example, refer to the Provenance Example section.

Identifies Source Code

Description: The repository origin of the source code used in the build must be identified in provenance.

Kyverno Processes: The repository information is stored in the provenance file. This is recorded in the predicate.invocation section. As an example, refer to the Provenance Example section.

Identifies Entry Point

Description: The processes which started the build processes should be identified by provenance.

Kyverno Processes: The required information is stored in the invocation.configSource.entryPoint in the provenance file. As an example, refer to the Provenance Example section.

Include All Build Parameters

Description: The provenance must include all the build parameters which are under user control.

Kyverno Processes: The main GitHub Action workflow file does not accept any parameters. The workflow_dispatch section in the release.yaml file does not accept any parameters. At SLSA level 3 the parameters, if any, must be listed. At SLSA level 4, the parameters must be empty.

Provenance Example

The following is an example of the generated provenance for Kyverno which will be returned from the process documented here.

  1{
  2  "_type": "https://in-toto.io/Statement/v0.1",
  3  "predicateType": "https://slsa.dev/provenance/v0.2",
  4  "subject": [
  5    {
  6      "name": "ghcr.io/kyverno/kyverno",
  7      "digest": {
  8        "sha256": "96a54a5747485b800adf05ff84d48fc9a8b66f1cdf9087cbed385dacc1cdb4d6"
  9      }
 10    }
 11  ],
 12  "predicate": {
 13    "builder": {
 14      "id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v1.4.0"
 15    },
 16    "buildType": "https://github.com/slsa-framework/slsa-github-generator/container@v1",
 17    "invocation": {
 18      "configSource": {
 19        "uri": "git+https://github.com/kyverno/kyverno@refs/tags/v1.9.0-beta.2",
 20        "digest": {
 21          "sha1": "ab368ebc080535448aa08fafdff82a4d4c69e127"
 22        },
 23        "entryPoint": ".github/workflows/release.yaml"
 24      },
 25      "parameters": {},
 26      "environment": {
 27        "github_actor": "realshuting",
 28        "github_actor_id": "25727662",
 29        "github_base_ref": "",
 30        "github_event_name": "push",
 31        "github_event_payload": {
 32          "after": "9d6e981a64b0b8dc357befb7fc811304d7b11f7b",
 33          "base_ref": null,
 34          "before": "0000000000000000000000000000000000000000",
 35          "commits": [],
 36          "compare": "https://github.com/kyverno/kyverno/compare/v1.9.0-beta.2",
 37          "created": true,
 38          "deleted": false,
 39          "enterprise": {
 40            "avatar_url": "https://avatars.githubusercontent.com/b/9995?v=4",
 41            "created_at": "2021-11-03T14:57:36Z",
 42            "description": "",
 43            "html_url": "https://github.com/enterprises/cncf",
 44            "id": 9995,
 45            "name": "Cloud Native Computing Foundation",
 46            "node_id": "E_kgDNJws",
 47            "slug": "cncf",
 48            "updated_at": "2022-12-27T16:04:12Z",
 49            "website_url": "https://cncf.io"
 50          },
 51          "forced": false,
 52          "head_commit": {
 53            "author": {
 54              "email": "shuting@nirmata.com",
 55              "name": "shuting",
 56              "username": "realshuting"
 57            },
 58            "committer": {
 59              "email": "noreply@github.com",
 60              "name": "GitHub",
 61              "username": "web-flow"
 62            },
 63            "distinct": true,
 64            "id": "ab368ebc080535448aa08fafdff82a4d4c69e127",
 65            "message": "tag v1.9.0-beta.2 (#5959)",
 66            "timestamp": "2023-01-10T11:45:52Z",
 67            "tree_id": "95c14e810108e4125c571ba33483204f4b078900",
 68            "url": "https://github.com/kyverno/kyverno/commit/ab368ebc080535448aa08fafdff82a4d4c69e127"
 69          },
 70          "organization": {
 71            "avatar_url": "https://avatars.githubusercontent.com/u/68448710?v=4",
 72            "description": "Kubernetes Native Policy Management",
 73            "events_url": "https://api.github.com/orgs/kyverno/events",
 74            "hooks_url": "https://api.github.com/orgs/kyverno/hooks",
 75            "id": 68448710,
 76            "issues_url": "https://api.github.com/orgs/kyverno/issues",
 77            "login": "kyverno",
 78            "members_url": "https://api.github.com/orgs/kyverno/members{/member}",
 79            "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4NDQ4NzEw",
 80            "public_members_url": "https://api.github.com/orgs/kyverno/public_members{/member}",
 81            "repos_url": "https://api.github.com/orgs/kyverno/repos",
 82            "url": "https://api.github.com/orgs/kyverno"
 83          },
 84          "pusher": {
 85            "email": "shutting06@gmail.com",
 86            "name": "realshuting"
 87          },
 88          "ref": "refs/tags/v1.9.0-beta.2",
 89          "repository": {
 90            "allow_forking": true,
 91            "archive_url": "https://api.github.com/repos/kyverno/kyverno/{archive_format}{/ref}",
 92            "archived": false,
 93            "assignees_url": "https://api.github.com/repos/kyverno/kyverno/assignees{/user}",
 94            "blobs_url": "https://api.github.com/repos/kyverno/kyverno/git/blobs{/sha}",
 95            "branches_url": "https://api.github.com/repos/kyverno/kyverno/branches{/branch}",
 96            "clone_url": "https://github.com/kyverno/kyverno.git",
 97            "collaborators_url": "https://api.github.com/repos/kyverno/kyverno/collaborators{/collaborator}",
 98            "comments_url": "https://api.github.com/repos/kyverno/kyverno/comments{/number}",
 99            "commits_url": "https://api.github.com/repos/kyverno/kyverno/commits{/sha}",
100            "compare_url": "https://api.github.com/repos/kyverno/kyverno/compare/{base}...{head}",
101            "contents_url": "https://api.github.com/repos/kyverno/kyverno/contents/{+path}",
102            "contributors_url": "https://api.github.com/repos/kyverno/kyverno/contributors",
103            "created_at": 1549297548,
104            "default_branch": "main",
105            "deployments_url": "https://api.github.com/repos/kyverno/kyverno/deployments",
106            "description": "Kubernetes Native Policy Management",
107            "disabled": false,
108            "downloads_url": "https://api.github.com/repos/kyverno/kyverno/downloads",
109            "events_url": "https://api.github.com/repos/kyverno/kyverno/events",
110            "fork": false,
111            "forks": 479,
112            "forks_count": 479,
113            "forks_url": "https://api.github.com/repos/kyverno/kyverno/forks",
114            "full_name": "kyverno/kyverno",
115            "git_commits_url": "https://api.github.com/repos/kyverno/kyverno/git/commits{/sha}",
116            "git_refs_url": "https://api.github.com/repos/kyverno/kyverno/git/refs{/sha}",
117            "git_tags_url": "https://api.github.com/repos/kyverno/kyverno/git/tags{/sha}",
118            "git_url": "git://github.com/kyverno/kyverno.git",
119            "has_discussions": true,
120            "has_downloads": true,
121            "has_issues": true,
122            "has_pages": true,
123            "has_projects": true,
124            "has_wiki": true,
125            "homepage": "https://kyverno.io",
126            "hooks_url": "https://api.github.com/repos/kyverno/kyverno/hooks",
127            "html_url": "https://github.com/kyverno/kyverno",
128            "id": 169108858,
129            "is_template": false,
130            "issue_comment_url": "https://api.github.com/repos/kyverno/kyverno/issues/comments{/number}",
131            "issue_events_url": "https://api.github.com/repos/kyverno/kyverno/issues/events{/number}",
132            "issues_url": "https://api.github.com/repos/kyverno/kyverno/issues{/number}",
133            "keys_url": "https://api.github.com/repos/kyverno/kyverno/keys{/key_id}",
134            "labels_url": "https://api.github.com/repos/kyverno/kyverno/labels{/name}",
135            "language": "Go",
136            "languages_url": "https://api.github.com/repos/kyverno/kyverno/languages",
137            "license": {
138              "key": "apache-2.0",
139              "name": "Apache License 2.0",
140              "node_id": "MDc6TGljZW5zZTI=",
141              "spdx_id": "Apache-2.0",
142              "url": "https://api.github.com/licenses/apache-2.0"
143            },
144            "master_branch": "main",
145            "merges_url": "https://api.github.com/repos/kyverno/kyverno/merges",
146            "milestones_url": "https://api.github.com/repos/kyverno/kyverno/milestones{/number}",
147            "mirror_url": null,
148            "name": "kyverno",
149            "node_id": "MDEwOlJlcG9zaXRvcnkxNjkxMDg4NTg=",
150            "notifications_url": "https://api.github.com/repos/kyverno/kyverno/notifications{?since,all,participating}",
151            "open_issues": 296,
152            "open_issues_count": 296,
153            "organization": "kyverno",
154            "owner": {
155              "avatar_url": "https://avatars.githubusercontent.com/u/68448710?v=4",
156              "email": "kyverno@googlegroups.com",
157              "events_url": "https://api.github.com/users/kyverno/events{/privacy}",
158              "followers_url": "https://api.github.com/users/kyverno/followers",
159              "following_url": "https://api.github.com/users/kyverno/following{/other_user}",
160              "gists_url": "https://api.github.com/users/kyverno/gists{/gist_id}",
161              "gravatar_id": "",
162              "html_url": "https://github.com/kyverno",
163              "id": 68448710,
164              "login": "kyverno",
165              "name": "kyverno",
166              "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4NDQ4NzEw",
167              "organizations_url": "https://api.github.com/users/kyverno/orgs",
168              "received_events_url": "https://api.github.com/users/kyverno/received_events",
169              "repos_url": "https://api.github.com/users/kyverno/repos",
170              "site_admin": false,
171              "starred_url": "https://api.github.com/users/kyverno/starred{/owner}{/repo}",
172              "subscriptions_url": "https://api.github.com/users/kyverno/subscriptions",
173              "type": "Organization",
174              "url": "https://api.github.com/users/kyverno"
175            },
176            "private": false,
177            "pulls_url": "https://api.github.com/repos/kyverno/kyverno/pulls{/number}",
178            "pushed_at": 1673351226,
179            "releases_url": "https://api.github.com/repos/kyverno/kyverno/releases{/id}",
180            "size": 75072,
181            "ssh_url": "git@github.com:kyverno/kyverno.git",
182            "stargazers": 3340,
183            "stargazers_count": 3340,
184            "stargazers_url": "https://api.github.com/repos/kyverno/kyverno/stargazers",
185            "statuses_url": "https://api.github.com/repos/kyverno/kyverno/statuses/{sha}",
186            "subscribers_url": "https://api.github.com/repos/kyverno/kyverno/subscribers",
187            "subscription_url": "https://api.github.com/repos/kyverno/kyverno/subscription",
188            "svn_url": "https://github.com/kyverno/kyverno",
189            "tags_url": "https://api.github.com/repos/kyverno/kyverno/tags",
190            "teams_url": "https://api.github.com/repos/kyverno/kyverno/teams",
191            "topics": [
192              "kubernetes",
193              "policy-management"
194            ],
195            "trees_url": "https://api.github.com/repos/kyverno/kyverno/git/trees{/sha}",
196            "updated_at": "2023-01-10T06:16:33Z",
197            "url": "https://github.com/kyverno/kyverno",
198            "visibility": "public",
199            "watchers": 3340,
200            "watchers_count": 3340,
201            "web_commit_signoff_required": true
202          },
203          "sender": {
204            "avatar_url": "https://avatars.githubusercontent.com/u/25727662?v=4",
205            "events_url": "https://api.github.com/users/realshuting/events{/privacy}",
206            "followers_url": "https://api.github.com/users/realshuting/followers",
207            "following_url": "https://api.github.com/users/realshuting/following{/other_user}",
208            "gists_url": "https://api.github.com/users/realshuting/gists{/gist_id}",
209            "gravatar_id": "",
210            "html_url": "https://github.com/realshuting",
211            "id": 25727662,
212            "login": "realshuting",
213            "node_id": "MDQ6VXNlcjI1NzI3NjYy",
214            "organizations_url": "https://api.github.com/users/realshuting/orgs",
215            "received_events_url": "https://api.github.com/users/realshuting/received_events",
216            "repos_url": "https://api.github.com/users/realshuting/repos",
217            "site_admin": false,
218            "starred_url": "https://api.github.com/users/realshuting/starred{/owner}{/repo}",
219            "subscriptions_url": "https://api.github.com/users/realshuting/subscriptions",
220            "type": "User",
221            "url": "https://api.github.com/users/realshuting"
222          }
223        },
224        "github_head_ref": "",
225        "github_ref": "refs/tags/v1.9.0-beta.2",
226        "github_ref_type": "tag",
227        "github_repository_id": "169108858",
228        "github_repository_owner": "kyverno",
229        "github_repository_owner_id": "68448710",
230        "github_run_attempt": "1",
231        "github_run_id": "3883016519",
232        "github_run_number": "232",
233        "github_sha1": "ab368ebc080535448aa08fafdff82a4d4c69e127"
234      }
235    },
236    "metadata": {
237      "buildInvocationID": "3883016519-1",
238      "completeness": {
239        "parameters": true,
240        "environment": false,
241        "materials": false
242      },
243      "reproducible": false
244    },
245    "materials": [
246      {
247        "uri": "git+https://github.com/kyverno/kyverno@refs/tags/v1.9.0-beta.2",
248        "digest": {
249          "sha1": "ab368ebc080535448aa08fafdff82a4d4c69e127"
250        }
251      }
252    ]
253  }
254}

Summary

The SLSA project is providing huge gains in assuring the integrity of the software supply chain.