Skip to content

Commit

Permalink
release -> publish
Browse files Browse the repository at this point in the history
Signed-off-by: laurentsimon <[email protected]>
  • Loading branch information
laurentsimon committed Apr 1, 2024
1 parent 7a66e3f commit d9c915b
Show file tree
Hide file tree
Showing 41 changed files with 297 additions and 297 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CLI release
name: CLI publish

on:
# For manual tests.
Expand All @@ -18,4 +18,4 @@ jobs:
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
go-version-file: "./cmd/evaluator/go.mod"
config-file: .github/workflows/release/slsa-evaluator.yml
config-file: .github/workflows/publish/slsa-evaluator.yml
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
- [What is provenance?](#what-is-provenance)
- [What is slsa-policy?](#what-is-slsa-repo)
- [Setup](#setup)
- [Release policy](#release-policy)
- [Publish policy](#publish-policy)
- [Org setup](#org-setup)
- [Policy setup](#policy-setup)
- [Pre-submit validation](#pre-submit-validation)
- [Release service](#release-service)
- [Publish service](#publish-service)
- [Team setup](#team-setup)
- [Policy definition](#policy-definition)
- [Call the release service](#call-the-release-service)
- [Call the publish service](#call-the-publish-service)
- [Deployment policy](#deployment-policy)
- [Org setup](#org-setup-1)
- [Policy setup](#policy-setup-1)
Expand Down Expand Up @@ -69,7 +69,7 @@ slsa-policy is a Go library, a CLI and a set of GitHub Actions to implement sour

## Setup

### Release policy
### Publish policy

#### Org setup

Expand All @@ -85,55 +85,55 @@ slsa-policy is a Go library, a CLI and a set of GitHub Actions to implement sour
1. Limit any bypass actors to those that are strictly necessary (i.e. break glass).
1. Require review from [CODEOWNERS](https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#additional-settings).
1. Add team maintainers as contributors to the repository. Give them `write` access. Do *NOT* give them admin access. Note that you can do that later when teams create their first policy, see [Policy definition](#policy-definition).
1. Create a folder to store the release policies. See an example [here](https://github.com/laurentsimon/slsa-org/tree/main/policies/release/).
1. Create a file with your trusted roots. See example [org.json](https://github.com/laurentsimon/slsa-org/tree/main/policies/release/org.json).
1. Create a folder to store the publish policies. See an example [here](https://github.com/laurentsimon/slsa-org/tree/main/policies/publish/).
1. Create a file with your trusted roots. See example [org.json](https://github.com/laurentsimon/slsa-org/tree/main/policies/publish/org.json).

##### Pre-submit validation

To validate the policy files, run the binary as:

```bash
cd policies/release
$ go run . release validate org.json .
cd policies/publish
$ go run . publish validate org.json .
```

TODO: we need pre-submits when new files are created, to ensure the appropriate owners are added to CODEOWNERS.

##### Release service
##### Publish service

You need to define a workflow that your teams will call when they want to release their container images. This workflow is responsible for evaluating the release policy. See an example [image-releaser.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-releaser.yml)
You need to define a workflow that your teams will call when they want to publish their container images. This workflow is responsible for evaluating the publish policy. See an example [image-publishr.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-publishr.yml)

In the workflow above, the CLI is called as follows:

```bash
cd policies/release
cd policies/publish
# This is passed by the caller, e.g. dev or prod.
env="${{ inputs.environment }}"
go run . release evaluate org.json . "${image}" "${env}"
go run . publish evaluate org.json . "${image}" "${env}"
```

#### Team setup

##### Policy definition

Teams create their policy files under the folder defined by their organization in [Policy setup](#policy-setup). See an example of a policy in [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/release/echo-server.json).
Teams create their policy files under the folder defined by their organization in [Policy setup](#policy-setup). See an example of a policy in [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/publish/echo-server.json).

When a team creates a new file or folder:

1. If not already done in [Org setup](#org-setup), org administrators should add team members as contributors and give them `write` access. Do *NOT* gives them admin access.
1. Update the CODEOWNERS file to give permissions to the team members who own the package. This allows teams to edit their policies without requiring reviews by the organization admnistrators.

##### Call the release service
##### Call the publish service

When publishing containers, teams must call the release policy service service [image-releaser.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-releaser.yml) defined in the org's [Release service](#release-service) section. See an example [deploy-image.yml](https://github.com/laurentsimon/slsa-project/blob/main/.github/workflows/deploy-image.yml). This workflows would be called with environment set as "staging" first. One staging tests have passed, it may be called with "prod" environment. Note that the environment must match one the values defined in the policy definition [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/release/echo-server.json).
When publishing containers, teams must call the publish policy service service [image-publishr.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-publishr.yml) defined in the org's [Publish service](#publish-service) section. See an example [deploy-image.yml](https://github.com/laurentsimon/slsa-project/blob/main/.github/workflows/deploy-image.yml). This workflows would be called with environment set as "staging" first. One staging tests have passed, it may be called with "prod" environment. Note that the environment must match one the values defined in the policy definition [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/publish/echo-server.json).

After the workflow has successfully run, you may manually verify the release attestation via:
After the workflow has successfully run, you may manually verify the publish attestation via:

```bash
# NOTE: change image to your image.
$ image=docker.io/laurentsimon/slsa-project-echo-server@sha256:4378b3d11e11ede0f64946e588c590e460e44f90c8a7921ad2cb7b04aaf298d4
$ creator_id=https://github.com/laurentsimon/slsa-org/.github/workflows/image-releaser.yml@refs/heads/main
$ type=https://slsa.dev/release/v0.1
$ creator_id=https://github.com/laurentsimon/slsa-org/.github/workflows/image-publishr.yml@refs/heads/main
$ type=https://slsa.dev/publish/v0.1
$ cosign verify-attestation "${image}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity "${creator_id}"
Expand Down Expand Up @@ -198,9 +198,9 @@ When a team creates a new file or folder:

##### Call the deployment service

Before submitting a request to deploy containers, teams must call the deployment policy service [image-deployer.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-deployer.yml) defined in the org's [Deployment service](#deployment-service) section. See an example [deploy-image.yml](https://github.com/laurentsimon/slsa-project/blob/main/.github/workflows/deploy-image.yml). This may be called with "staging" environment first to allow the container to run on the staging service account defined in [servers-staging.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/deployment/servers-staging.json). Once all staging tests have passed, it may be called with "prod" environment. Note that the environment must match one the values defined in the release policy file [servers-prod.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/deployment/servers-prod.json) and the deployment policy file [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/release/echo-server.json).
Before submitting a request to deploy containers, teams must call the deployment policy service [image-deployer.yml](https://github.com/laurentsimon/slsa-org/blob/main/.github/workflows/image-deployer.yml) defined in the org's [Deployment service](#deployment-service) section. See an example [deploy-image.yml](https://github.com/laurentsimon/slsa-project/blob/main/.github/workflows/deploy-image.yml). This may be called with "staging" environment first to allow the container to run on the staging service account defined in [servers-staging.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/deployment/servers-staging.json). Once all staging tests have passed, it may be called with "prod" environment. Note that the environment must match one the values defined in the publish policy file [servers-prod.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/deployment/servers-prod.json) and the deployment policy file [echo-server.json](https://github.com/laurentsimon/slsa-org/blob/main/policies/publish/echo-server.json).

After the workflow has successfully run, you may manually verify the release attestation via:
After the workflow has successfully run, you may manually verify the publish attestation via:

```bash
# NOTE: change image to your image.
Expand Down
4 changes: 2 additions & 2 deletions actions/installer/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ runs:
echo "GH_ACTION_REPOSITORY: ${GH_ACTION_REPOSITORY}"
echo "PWD: $PWD"
- name: Download the release binary
- name: Download the publish binary
id: download
shell: bash
working-directory: ${{ steps.working_dir.outputs.name }}
Expand All @@ -44,7 +44,7 @@ runs:
exit 1
fi
ref="${GH_ACTION_REF}"
gh release -R "${GH_ACTION_REPOSITORY}" download "${ref}" -p "binary-linux-amd64*"
gh publish -R "${GH_ACTION_REPOSITORY}" download "${ref}" -p "binary-linux-amd64*"
chmod u+x binary-linux-amd64
echo "ref=${ref}" >> "$GITHUB_OUTPUT"
Expand Down
4 changes: 2 additions & 2 deletions cmd/evaluator/internal/deployment/evaluate/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func Run(cli string, args []string) error {

// Evaluate the policy.
opts := deployment.AttestationVerificationOption{
Verifier: newReleaseVerifier(),
Verifier: newPublishVerifier(),
}
digests := intoto.DigestSet{
digestsArr[0]: digestsArr[1],
Expand All @@ -71,7 +71,7 @@ func Run(cli string, args []string) error {
return result.Error()
}

// Create a release attestation and sign it.
// Create a publish attestation and sign it.
// TODO(#3): do not attach the attestation, so that caller can do it however they want.
// TODO(#2): add policy.
att, err := result.AttestationNew()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,42 @@ import (
"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/utils"
"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/utils/crypto"
"github.com/laurentsimon/slsa-policy/pkg/deployment"
"github.com/laurentsimon/slsa-policy/pkg/release"
"github.com/laurentsimon/slsa-policy/pkg/publish"
"github.com/laurentsimon/slsa-policy/pkg/utils/intoto"
)

type releaseVerifier struct {
deployment.AttestationVerifierReleaseOptions
type publishVerifier struct {
deployment.AttestationVerifierPublishOptions
}

func newReleaseVerifier() *releaseVerifier {
return &releaseVerifier{}
func newPublishVerifier() *publishVerifier {
return &publishVerifier{}
}

func (v *releaseVerifier) validate() error {
func (v *publishVerifier) validate() error {
// Validate the identities.
if err := crypto.ValidateIdentity(v.AttestationVerifierReleaseOptions.ReleaserID,
v.AttestationVerifierReleaseOptions.ReleaserIDRegex); err != nil {
if err := crypto.ValidateIdentity(v.AttestationVerifierPublishOptions.PublishrID,
v.AttestationVerifierPublishOptions.PublishrIDRegex); err != nil {
return err
}
// Validate the build level.
if v.AttestationVerifierReleaseOptions.BuildLevel <= 0 || v.AttestationVerifierReleaseOptions.BuildLevel > 4 {
return fmt.Errorf("build level (%d) must be between 1 and 4", v.AttestationVerifierReleaseOptions.BuildLevel)
if v.AttestationVerifierPublishOptions.BuildLevel <= 0 || v.AttestationVerifierPublishOptions.BuildLevel > 4 {
return fmt.Errorf("build level (%d) must be between 1 and 4", v.AttestationVerifierPublishOptions.BuildLevel)
}
return nil
}

func (v *releaseVerifier) setOptions(opts deployment.AttestationVerifierReleaseOptions) error {
func (v *publishVerifier) setOptions(opts deployment.AttestationVerifierPublishOptions) error {
// Set the options.
v.AttestationVerifierReleaseOptions = opts
v.AttestationVerifierPublishOptions = opts
// Validate the options.
if err := v.validate(); err != nil {
return err
}
return nil
}

func (v *releaseVerifier) verifySignature(imageName string, digests intoto.DigestSet) (string, []byte, error) {
func (v *publishVerifier) verifySignature(imageName string, digests intoto.DigestSet) (string, []byte, error) {
// Validate the image.
if strings.Contains(imageName, "@") || strings.Contains(imageName, ":") {
return "", nil, fmt.Errorf("invalid image name (%q)", imageName)
Expand All @@ -58,32 +58,32 @@ func (v *releaseVerifier) verifySignature(imageName string, digests intoto.Diges
fmt.Println("imageURI:", imageURI)

// Verify the signature.
fullReleaserID, attBytes, err := crypto.VerifySignature(imageURI, v.AttestationVerifierReleaseOptions.ReleaserID,
v.AttestationVerifierReleaseOptions.ReleaserIDRegex)
fullPublishrID, attBytes, err := crypto.VerifySignature(imageURI, v.AttestationVerifierPublishOptions.PublishrID,
v.AttestationVerifierPublishOptions.PublishrIDRegex)
if err != nil {
return "", nil, fmt.Errorf("failed to verify image (%q) with releaser ID (%q) releaser ID regex (%q): %v",
imageURI, v.AttestationVerifierReleaseOptions.ReleaserID, v.AttestationVerifierReleaseOptions.ReleaserIDRegex, err)
return "", nil, fmt.Errorf("failed to verify image (%q) with publishr ID (%q) publishr ID regex (%q): %v",
imageURI, v.AttestationVerifierPublishOptions.PublishrID, v.AttestationVerifierPublishOptions.PublishrIDRegex, err)
}
return fullReleaserID, attBytes, nil
return fullPublishrID, attBytes, nil
}

func (v *releaseVerifier) verifyAttestationContent(attBytes []byte, imageName string, digests intoto.DigestSet, environment []string) (*string, error) {
func (v *publishVerifier) verifyAttestationContent(attBytes []byte, imageName string, digests intoto.DigestSet, environment []string) (*string, error) {
attReader := io.NopCloser(bytes.NewReader(attBytes))
verification, err := release.VerificationNew(attReader, &utils.PackageHelper{})
verification, err := publish.VerificationNew(attReader, &utils.PackageHelper{})
if err != nil {
return nil, fmt.Errorf("failed to create verifier for image (%q) and env (%q): %w", imageName, environment, err)
}

// Build level verification.
levelOpts := []release.VerificationOption{
release.IsSlsaBuildLevelOrAbove(v.AttestationVerifierReleaseOptions.BuildLevel),
levelOpts := []publish.VerificationOption{
publish.IsSlsaBuildLevelOrAbove(v.AttestationVerifierPublishOptions.BuildLevel),
}
// If environment is present, we must verify it.
var errList []error
if len(environment) > 0 {
for i := range environment {
penv := &environment[i]
opts := append(levelOpts, release.IsPackageEnvironment(*penv))
opts := append(levelOpts, publish.IsPackageEnvironment(*penv))
// WARNING: We must ensure that the imageName follows the format defined in the policy.
// This is the case, since our policy expect an image as registry/image.
if err := verification.Verify(digests, imageName, opts...); err != nil {
Expand All @@ -92,8 +92,8 @@ func (v *releaseVerifier) verifyAttestationContent(attBytes []byte, imageName st
continue
}
// Success.
utils.Log("Image (%q) verified with releaser ID (%q) and releaser ID regex (%q) and env (%q)\n",
imageName, v.AttestationVerifierReleaseOptions.ReleaserID, v.AttestationVerifierReleaseOptions.ReleaserIDRegex, *penv)
utils.Log("Image (%q) verified with publishr ID (%q) and publishr ID regex (%q) and env (%q)\n",
imageName, v.AttestationVerifierPublishOptions.PublishrID, v.AttestationVerifierPublishOptions.PublishrIDRegex, *penv)
return penv, nil
}
// We could not verify the attestation.
Expand All @@ -104,12 +104,12 @@ func (v *releaseVerifier) verifyAttestationContent(attBytes []byte, imageName st
if err := verification.Verify(digests, imageName, levelOpts...); err != nil {
return nil, fmt.Errorf("failed to verify image (%q) and env (%q): %w", imageName, environment, err)
}
utils.Log("Image (%q) verified with releaser ID (%q) and releaser ID regex (%q) and nil env\n",
imageName, v.AttestationVerifierReleaseOptions.ReleaserID, v.AttestationVerifierReleaseOptions.ReleaserIDRegex)
utils.Log("Image (%q) verified with publishr ID (%q) and publishr ID regex (%q) and nil env\n",
imageName, v.AttestationVerifierPublishOptions.PublishrID, v.AttestationVerifierPublishOptions.PublishrIDRegex)
return nil, nil
}

func (v *releaseVerifier) VerifyReleaseAttestation(digests intoto.DigestSet, imageName string, environment []string, opts deployment.AttestationVerifierReleaseOptions) (*string, error) {
func (v *publishVerifier) VerifyPublishAttestation(digests intoto.DigestSet, imageName string, environment []string, opts deployment.AttestationVerifierPublishOptions) (*string, error) {
if err := v.setOptions(opts); err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import (
"os"
"strings"

"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/release/validate"
"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/publish/validate"
"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/utils"
"github.com/laurentsimon/slsa-policy/cli/evaluator/internal/utils/crypto"
"github.com/laurentsimon/slsa-policy/pkg/release"
"github.com/laurentsimon/slsa-policy/pkg/publish"
"github.com/laurentsimon/slsa-policy/pkg/utils/intoto"
"github.com/laurentsimon/slsa-policy/pkg/utils/iterator/files_reader"
)

func usage(cli string) {
msg := "" +
"Usage: %s release evaluate orgPath projectsPath packageName [optional:environment]\n" +
"Usage: %s publish evaluate orgPath projectsPath packageName [optional:environment]\n" +
"\n" +
"Example:\n" +
"%s release evaluate ./path/to/policy/org ./path/to/policy/projects laurentsimon/echo-server@sha256:xxxx prod\n" +
"%s publish evaluate ./path/to/policy/org ./path/to/policy/projects laurentsimon/echo-server@sha256:xxxx prod\n" +
"\n"
fmt.Fprintf(os.Stderr, msg, cli, cli)
os.Exit(1)
Expand Down Expand Up @@ -52,16 +52,16 @@ func Run(cli string, args []string) error {
// Create a policy.
projectsReader := files_reader.FromPaths(projectsPath)
organizationReader, err := os.Open(orgPath)
pol, err := release.PolicyNew(organizationReader, projectsReader, &utils.PackageHelper{}, release.SetValidator(&validate.PolicyValidator{}))
pol, err := publish.PolicyNew(organizationReader, projectsReader, &utils.PackageHelper{}, publish.SetValidator(&validate.PolicyValidator{}))
if err != nil {
return fmt.Errorf("failed to create policy: %w", err)
}

// Evaluate the policy.
opts := release.AttestationVerificationOption{
opts := publish.AttestationVerificationOption{
Verifier: newBuildVerifier(),
}
reqOpts := release.RequestOption{
reqOpts := publish.RequestOption{
Environment: env,
}
digests := intoto.DigestSet{
Expand All @@ -73,7 +73,7 @@ func Run(cli string, args []string) error {
return result.Error()
}

// Create a release attestation and sign it.
// Create a publish attestation and sign it.
// TODO(#3): do not attach the attestation, so that caller can do it however they want.
// TODO(#2): add policy.
att, err := result.AttestationNew()
Expand Down
Loading

0 comments on commit d9c915b

Please sign in to comment.