# rules_oci SOW ###### tags: `sow` Based on [rules_container requirements for distroless](https://docs.google.com/document/d/1GMx8RuEeNl50MdRUiAWMIpX4vOXcvSSQdPH7wJJfA_U). Deliverable in 2-4 months (calendar time). # Components ## toolchains - [estesp/manifest-tool](https://github.com/estesp/manifest-tool/releases): to generate manifest lists for multi platform images - [cosign](https://github.com/sigstore/cosign/releases) - [go-containerregistry/crane](https://github.com/google/go-containerregistry/releases): append layers, pull and push images and mutate image config (cmd, entrypoint etc), create empty base image - [go-containerregistry/registry](https://github.com/google/go-containerregistry/tree/main/cmd/registry): serve images locally. To able to use crane and cosign. 1. Ship as `crane serve` inside crane if accepted 2. Ship as a standalone tool ## oci_image / oci_index - crane needs to talk to a registry. implement a local oci registry and publish binaries github.com/google/go-containerregistry/cmd/registry@latest (send a PR implementing `crane serve`) - Fix releases for https://github.com/estesp/manifest-tool to be per-binary/platform with integrity hashes, then wire as a toolchain - remove umoci from the toolchain - implement 1+n transitions to build for multiple platforms - Get empty OCI base image with `crane append` (keeping a scratch base image checked into the ruleset could work alternatively) ## flatten_tars - implement a rule that takes multiple tar files and spits out single flattened layer ## deb_to_tar - implement the rule that uses this tool to convert deb to tar - export `BomInfo` from [debian control file](https://www.debian.org/doc/debian-policy/ch-controlfields.html). It provides most of the required information that is necessary to build the sbom. - transition into target platform (if requested) and fetch the matching package instead - make [distroless/debian_package_manager](https://github.com/GoogleContainerTools/distroless/tree/main/debian_package_manager) use this rule ## sbom - Collect transitive `BOMInfo` which is propagted through the depgraph and merge them into one. (spdx) - Do we need to support `cyclonedx` as well? (https://github.com/anchore/syft can convert between types) - Provide the generated `spdx sbom` to `oci_sign` ## oci_sign Note:cosign needs to talk to a registry in order to sign images. we need to run an OCI registry backed by OCI layout on disk. go-containerregistry people started one but stranded https://github.com/google/go-containerregistry/pull/1211 - introduce [cosign](https://github.com/sigstore/cosign/releases) as a toolchain - implement the rule that signs images recursively with `-r` option (for multi-platform images) by spinning up a local registry - make oci_sign to spit out reordered index.json so that crane pushes the signature image before the actual image - Support attaching files to signed image via `attachments` attribute ## oci_push - support for pushing multiple tags. (aka crane push with different tags) - make it a runnable target `bazel run //image:push -- --registry index.docker.io --tag latest --tag 1.x.x` ## structure_test - introduce https://github.com/GoogleContainerTools/container-structure-test as a toolchain, with https://github.com/opencontainers/runc (will work only on Linux) - implement the rule that takes oci_image and runs tests against it - distroless team heavily relies on `commandTests`. Thus we need to bring in the docker toolchain for this - docker runtime can not load the intermediate OCI format. We need to make a tarball out of OCI layout by using crane ## migration of distroless to new rulesets - replace usage of `for arch in ARCH` with transitions # Alternatives considered [Buildah](https://github.com/containers/buildah): It needs a container runtime to function. [Skopeo](https://github.com/containers/skopeo): Can convert `docker` to `oci` but it does not have prebuilt binaries published. Needs to be brought in with `rules_go`. Also, this tool is only needed when there is a base image. # Example ## WORKSPACE ```starlark http_archive(name = "rules_oci") load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") rules_oci_dependencies() load(":debian_packages.bzl", "debian_packages", "debian_tar_packages") debian_packages(name = "debs") debian_tar_packages(name = "tars") ``` ## BUILD ### Debian 11 ```starlark # debian base # minimal debian 10 filesystem # only two `.tar` files are exported through DefaultInfo alongside BOMInfo deb_to_tar( name = "debian10_fs", debs = [ "@debs//amd64_libbz2_1", "@debs//amd64_debian10_base_files", # add more... ] ) flatten_tars( name = "libs", tars = [ "@tars//amd64_debian10_libreadline7", "@tars//amd64_debian10_libssl1.1" ] ) passwd_entry(name = "debian10_passwd") # this image will have 4 layers at total oci_image( name = "debian10", layers = [ ":debian10_fs", ":debian10_passwd", ":libs" ] ) platforms = { # 1:2+ transitions "@platforms//linux": [ "@platforms//cpu:x86_64", "@platforms//cpu:arm64", "@platforms//cpu:armel", "@platforms//cpu:ppc", ] } # will yield multi platform image oci_index( name = "debian_multi", image = ":debian10", platforms = platforms ) # will yield sbom for each platform through DefaultInfo.files sbom( name = "debian10_sbom", deps = [ ":debian10_fs" ], platforms = platforms ) # crane push with multiple tags support and aware of pushing the signature first. # this target is intended to be `bazel run` # ``` # bazel run //:debian10_push -- # ``` # optionally pass a `cosign.key` as an argument to sign and then push # ``` # bazel run //:debian10_push -- --key /path/to/cosign.key # ``` oci_push( name = "debian10_push", image = ":debian10", attachments = [ ":debian10_sbom" ], ) ``` ### Nodejs ```starlark # https://github.com/GoogleContainerTools/distroless/blob/main/node_archives.bzl oci_image( name = "node_14", base = ":debian11", entrypoint = ["/nodejs/bin/node"], layers = [ "@nodejs14_amd64//:tar" ] ) ``` # Command snippets ```bash # go install github.com/google/go-containerregistry/cmd/registry@latest # https://github.com/estesp/manifest-tool ## mutate image crane pull consul ./consul --format oci --platform=linux/amd64 crane push ./consul localhost:1338/consul:latest crane append localhost:1338/consul:latest --layer ## flatten layers into one crane append --new_layer first.tar --new_layer second.tar --new_layer third.tar --new_tag localhost:1338/flattened:latest crane flatten localhost:1338/flattened:latest FLATTENED_BLOB_DIGEST=$(crane manifest localhost:1338/flattened:latest | jq --raw-output ".layers[0].digest") crane blob localhost:1338/flattened@$FLATTENED_BLOB_DIGEST > blob.tar tar -tf blob.tar ## sign the image cosign sign --key cosign.key localhost:1338/consul:latest REF=$(cosign triangulate localhost:1338/consul:latest) crane pull $REF ./consul-signature --format oci crane push ./consul-signature $REF ## attach sbom to image cosign attach sbom --sbom gen.spdx --input-format json --type spdx ## install a deb ## https://github.com/GoogleContainerTools/distroless/blob/main/debian_archives.bzl curl -sL https://apt.puppetlabs.com/puppetlabs-release-precise.deb -o puppetlabs-release-precise.deb fpm -s deb -t tar puppetlabs-release-precise.deb tar -tf puppetlabs-release.tar curl -sL https://snapshot.debian.org/archive/debian/20220517T205511Z/pool/main/b/bzip2/libbz2-1.0_1.0.6-9.2~deb10u1_amd64.deb -o libbz2-1.0_1.0.6-9.2~deb10u1_amd64.deb fpm -s deb -t tar libbz2-1.0_1.0.6-9.2~deb10u1_amd64.deb tar -tf libbz2-1.0.tar ## get the control file ### .deb is a tar file! ### bazel 5.2 can extract them fpm -s tar -t dir libbz2-1.0_1.0.6-9.2~deb10u1_amd64.deb fpm -s tar -t dir libbz2-1.dir/control.tar.xz # will spit out a nicely formatted yaml file. ### merge multi platform images (manifest list) crane copy consul localhost:1338/consul-arm64 --platform=linux/arm64 crane copy consul localhost:1338/consul-amd64 --platform=linux/amd64 manifest-tool push from-args \ --platforms linux/amd64,linux/arm64 \ --template localhost:1338/consul-ARCH:latest \ --target localhost:1338/consul:latest ``` # Debian Control File ``` Package: libbz2-1.0 Source: bzip2 Version: 1.0.6-9.2~deb10u1 Architecture: amd64 Maintainer: Anibal Monsalve Salazar <anibal@debian.org> Installed-Size: 104 Depends: libc6 (>= 2.4) Section: libs Priority: important Multi-Arch: same Homepage: https://web.archive.org/web/20180801004107/http://www.bzip.org/ Description: high-quality block-sorting file compressor library - runtime This package contains libbzip2 which is used by the bzip2 compressor. . bzip2 is a freely available, patent free, data compressor. . bzip2 compresses files using the Burrows-Wheeler block-sorting text compression algorithm, and Huffman coding. Compression is generally considerably better than that achieved by more conventional LZ77/LZ78-based compressors, and approaches the performance of the PPM family of statistical compressors. . The archive file format of bzip2 (.bz2) is incompatible with that of its predecessor, bzip (.bz). ```