paketo-buildpacks / stacks

stacks
Apache License 2.0
35 stars 23 forks source link

Stack BOM #90

Closed sophiewigmore closed 2 years ago

sophiewigmore commented 2 years ago

Summary

First pass at https://github.com/paketo-buildpacks/rfcs/pull/125

Notes: This PR does not exactly conform to the RFC. It has some additions that are being considered in the upstream CNB RFC, which have not yet been decided on. This implementation could be subject to change. Notable features of this implementation include:

How To Use

  1. go build the create-stack binary by running cd create-stack && go build .
  2. run ./create-stack --build-destination <build image name> --run-destination <run image name> --version <some version> --stack <tiny, base, or full> --stacks-dir $PWD/.. This step will take a few minutes to run.
  3. Resulting images will appear on the docker daemon:
    • <build image name>:<version>
    • <build image name>:<version>-cnb
    • <run image name>:<version>
    • <run image name>:<version>-cnb
  4. The <run image name>:<version>-cnb contains the BOM files. Check it out by:
    • docker inspect <run image name>:<version>-cnb | jq -r '.[0].Config.Labels."io.buildpacks.base.sbom"' to see the newly added label. The value is the SHA256 of the newly added layer that contains the BOM entries.
    • docker inspect <run image name>:<version>-cnb | jq -r '.[0].RootFS.Layers' to see that this layer exists
    • To actually see the content of the BOM files, you will have to copy the files out of the container, since we do not have root privileges to view the files in the container. This can be achieved by running docker run -it <run image name>:<version>-cnb, and then in a separate tab, run docker cp <container ID>:/cnb/sbom <output dir>. Inside will be two files, a CycloneDX BOM and and a Syft BOM.

Checklist

ryanmoran commented 2 years ago

@sophiewigmore I think you could test Attach with something like the following as a skeleton:

    context("Attach", func() {
        var (
            bom          stack.BOM
            dockerClient *client.Client
        )

        it.Before(func() {
            var err error
            dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
            Expect(err).NotTo(HaveOccurred())

            ctx := gocontext.Background()

            logs, err := dockerClient.ImagePull(ctx, "alpine:latest", types.ImagePullOptions{})
            Expect(err).NotTo(HaveOccurred())
            defer logs.Close()

            // NOTE: required to force a wait for the image to pull
            _, err = io.Copy(io.Discard, logs)
            Expect(err).NotTo(HaveOccurred())
        })

        it.After(func() {
            _, err := dockerClient.ImageRemove(gocontext.Background(), "alpine:latest", types.ImageRemoveOptions{Force: true})
            Expect(err).NotTo(HaveOccurred())
        })

        it("attaches the BOM layer to the image", func() {
            err := bom.Attach("alpine:latest", []string{syftFile, cdxFile})
            Expect(err).NotTo(HaveOccurred())

            // You can then use the `daemon` package to fetch the image and take a look to confirm that the labels and layers are there.
        })
    })
ryanmoran commented 2 years ago

The feature is now hidden behind a feature flag. You can enable it by running the create-stack command while setting the EXPERIMENTAL_ATTACH_RUN_IMAGE_SBOM environment variable to true.

sophiewigmore commented 2 years ago

@ryanmoran your changes look good to me!

sophiewigmore commented 2 years ago

When the flag isn't set, is it confirmed the produced run image doesn't have the BOM/looks as expected?

ryanmoran commented 2 years ago

I've updated one of the acceptance tests, for base, to build without the flag and asserted that the SBOM does not appear.