building container images with bill of materials
APACHE-2.0 License
masculine noun - shipyard
estaleiro
allows you to ship container images with confidence - a declarative
approach to dealing with the last mile in building container images, so you can
have more control (through transparency) over what you ship.
HIGHLY EXPERIMENTAL - DO NOT USE THIS
Table of Contents
Keeping track of what has been added to a container image that one is about to ship if hard.
With the versatility of Dockerfiles, it's quite easy to shoot itself in the foot when it comes to either installing dependencies that one couldn't even consume, or that wouldn't be wise.
While it's great to talk about best practices, it's hard to enforce them.
estaleiro
sits at the very final portion of your image building process,
gating what gets into the final container image that is supposed to be shipped
to your customers.
.-----------.
somehow you build stuff ---> | estaleiro | --> final container image
*-----------* +
bill of materials
with the Dockerfiles and |
build process that you |
already have .
anything that gets into the
final container image *MUST*
declare where it comes from.
It leverages buildkit
as a way of
implementing a convention of how the last stage in building a container image
(i.e., gathering binaries built in previous steps), putting guard-rails where
needed, and enforcing a set of rules where needed.
Here's an example of how that looks like in practice:
FROM golang AS base
ENV CGO_ENABLED=0
RUN apt update && apt install -y git
ADD . /src
WORKDIR /src
RUN go mod download
FROM base AS build
RUN go build \
-tags netgo -v -a \
-o /usr/local/bin/estaleiro \
-ldflags "-X main.version=$(cat ./VERSION) -extldflags \"-static\""
estaleiro
file that describes how to package that binary produced# syntax = cirocosta/estaleiro-frontend
# the final image to produce
#
image "cirocosta/estaleiro" {
base_image = "ubuntu:bionic"
apt {
package "ca-certificates" {}
}
file "/usr/local/bin/estaleiro" {
from_step "build" {
path = "/bin/estaleiro"
}
}
}
# performs the build of `estaleiro`.
#
step "build" {
dockerfile = "./Dockerfile"
target = "build"
source_file "/bin/estaleiro" {
vcs "git" {
ref = "${estaleiro-commit}"
repository = "https://github.com/cirocosta/estaleiro"
}
}
}
Having those pieces in, estaleiro
creates the intermediary representation to
be used by buildkitd
to build the final container image that starts from
ubuntu:bionic
, has the ca-certificates
package installed, and the file that
the Dockerfile built - all while keeping track of their versions and sources
along the way in the form of a bill of materials:
base_image:
name: docker.io/library/ubuntu
digest: sha256:c303f19cfe9ee92badbbbd7567bc1ca47789f79303ddcef56f77687d4744cd7a
packages:
- name: fdisk
version: 2.31.1-0.4ubuntu3.3
source_package: util-linux
architecture: amd64
- name: libpam-runtime
version: 1.1.8-3.6ubuntu2.18.04.1
source_package: pam
architecture: all
# ...
changeset:
files:
- name: "/usr/local/bin/seataleiro"
digest: "sha256:89f687d4744cd779303ddc7ef56f77c303f19cfe9ee92badbbbd7567bc1ca47a"
source:
- url: https://github.com/cirocosta/estaleiro
type: git
ref: 6a4d0b73673a1863a62b7ac6cbde4ae7597c56d7
from_step:
name: "build"
dockerfile_digest: "sha256:9303ddc7ef56f77c303f19cfe9ee92badbbbd7567bc189f687d4744cd77ca47a"
packages:
- name: ca-certificates
version: "20180409"
source_package: ""
architecture: all
location:
uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409_all.deb
name: ca-certificates_20180409_all.deb
size: "150932"
md5sum: eae40792673dcb994af86284d0a01f36
source:
- uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409.dsc
name: ca-certificates_20180409.dsc
size: "1420"
md5sum: cd1f6540d0dab28f897e0e0cb2191130cdbf897f8ce3f52c8e483b2ed1555d30
- uri: http://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20180409.tar.xz
name: ca-certificates_20180409.tar.xz
size: "246908"
md5sum: 7af6f5bfc619fd29cbf0258c1d95107c38ce840ad6274e343e1e0d971fc72b51
# and all of its dependencies too ...
THIS IS STILL HIGHLY EXPERIMENTAL
All that you need is:
Having an estaleiro
file (like the estaleiro.hcl
that you find in this
repo), direct docker build
to it via a regular --file
(-f
), having
DOCKER_BUILDKIT=1
set as the environent variable:
# create an `estaleiro.hcl` file.
#
# note.: the first line (with syntax ...) is important - it's
# what tells the docker engine to fetch our implementation
# of `estaleiro`, responsible for creating the build
# definition.
#
$ echo "# syntax=cirocosta/estaleiro
image "cirocosta/sample" {
base_image = "ubuntu:bionic"
}
" > ./estaleiro.hcl
# instruct `docker` to build our image
#
$ docker build -t test -f ./estaleiro.hcl
[+] Building 9.4s (4/4) FINISHED
# retrieve the bill of materials from the filesystem
#
$ docker create --name tmp
$ docker cp tmp:/bom/merged.yml ./bom.yml
See ./LICENSE.