# 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).
```