A Brief Introduction to OCI Containers on FreeBSD
Wednesday, 4 Dec 2024
OCI Containers on FreeBSD
These Work-in-progress notes will be updated over time, and merged into the FreeBSD HandBook once they are complete. For the moment, have fun, and send feedback to dch@FreeBSD.org
14.2-RELEASE now includes OCI-compatible images, and the Podman toolkit on FreeBSD is ready to use them, on both amd64 and arm64 systems.
In the aarch64 and amd64 download directories, you’ll see 3 official OCI-format images. The naming may be a little confusing at first, but should make sense once you start using them.
Each image comprises a subset of a standard FreeBSD base.txz
release
tarball, for various use cases.
CHECKSUM.SHA256
CHECKSUM.SHA512
FreeBSD-14.2-RELEASE-amd64-container-image-dynamic.txz
FreeBSD-14.2-RELEASE-amd64-container-image-minimal.txz
FreeBSD-14.2-RELEASE-amd64-container-image-static.txz
static
The static
image is the bare minimum functional system, if you have
a statically compiled binary, this should be able to run using only
the static image.
- only the bare bones
- TLS certs
- minimal password file
- minimal termcap
- tzdata file
- just enough to run a static binary
Note that it has, by design, a lean footprint, to make a security compromise of the container less useful to the attacker. There’s no UNIX shell, no command-line tools, nor a package manager.
dynamic
The dynamic
image uses the static
image as a parent layer, and
adds enough bits to be able to use shared libraries, and libc.
It should be able to run most binaries and tools, but still doesn’t have a shell, nor a package manager.
minimal
Again, minimal
builds on the preceding dynamic
layer, and finally
adds the minimum that a user would expect - a UNIX shell, and package.
scratch and builder
There are two other common images that are worth mentioning - scratch
as the /dev/null
of images, is completely empty. You typically use
this to construct very specific containers for your own systems.
A builder container is typically used as an intermediate step that enables you to create, as an output, the final container you wish to run in production. The builder might have compilers, and other build tools necessary for your organisation, even if the final image, does not.
A Brief Introduction to Podman
The podman suits comprises 3 main tools and a bunch of supporting ones:
- podman is a daemonless container engine using docker-compatible commands
- buildah generates OCI formatted container images
- skopeo works with remote OCI image registries
Mad props to SamuelKarp@ and dfr@ who made all of this work, from building https://github.com/samuelkarp/runj the first container-friendly port for FreeBSD, to porting the entire podman suite and getting necessary kernel changes to support it. Mad Mad Props!
Installing Podman
- remember to run all this in screen/tmux/mosh to avoid network loss while restarting your firewall
-
integrate changes from
/usr/local/etc/containers/pf.conf.sample
into your/etc/pf.conf
setting egress macros appropriately -
amend
sysctl.conf
, and/etc/fstab
as appropriate
# zfs create -o mountpoint=/var/db/containers zroot/containers
# zfs snapshot zroot/containers@empty
# sysctl net.pf.filter_local=1
# mount -t fdescfs fdesc /dev/fd
# pkg install -r FreeBSD -y podman-suite emulators/qemu-user-static
# service pf restart
If you don’t have zfs, switch to ufs:
# sed -I .bak -e 's/driver = "zfs"/driver = "vfs"/' \
/usr/local/etc/containers/storage.conf
- review and amend the template config files. None of these are initially required though:
# pkg list buildah podman conmon ocijail containers-common \
containernetworking-plugins \
|grep /etc/
/usr/local/etc/containers/containers.conf.sample
/usr/local/etc/containers/policy.json.sample
/usr/local/etc/containers/registries.conf.sample
/usr/local/etc/containers/storage.conf.sample
/usr/local/etc/containers/pf.conf.sample
A Quick Terminal Tour
Contrary to podman/docker on Linux, everything needs to run as root. For the moment.
Now that we have the necessary tools and firewall rules in place, let’s take it out for a spin, fetching the officially published images from 14.2-RELEASE, either aarch64 or amd64 as appropriate.
- https://download.freebsd.org/releases/OCI-IMAGES/14.2-RELEASE/amd64
- https://download.freebsd.org/releases/OCI-IMAGES/14.2-RELEASE/aarch64
Fetch the Official Images
Note how the first image already contains all the blobs needed for the smaller images - the archives share common blobs within the store. You can fetch the exported images and compare the checksums against the 14.2-RELEASE announcement to be certain, or you can verify the image checksums against the ones show below, the end result is the same.
The blob truncated checksums below will be the same on all images in any registry. While these aren’t yet published as part of the release process, they should be for future releases, making this step even easier to verify.
# export OCIBASE=https://download.freebsd.org/releases/OCI-IMAGES/14.2-RELEASE/amd64/Latest
# podman load -i=$OCIBASE/FreeBSD-14.2-RELEASE-amd64-container-image-minimal.txz
Getting image source signatures
Copying blob 3d72902df126 done |
Copying blob 9154eccec9cd done |
Copying blob 6c3cf2b1cc8a done |
Copying config c5f3e77557 done |
Writing manifest to image destination
Loaded image: localhost/freebsd14-minimal:14.2-RELEASE-amd64
# podman load -i=$OCIBASE/FreeBSD-14.2-RELEASE-amd64-container-image-dynamic.txz
Getting image source signatures
Copying blob 3d72902df126 skipped: already exists
Copying blob 9154eccec9cd skipped: already exists
Copying config ebf7538b22 done |
Writing manifest to image destination
Loaded image: localhost/freebsd14-dynamic:14.2-RELEASE-amd64
# podman load -i=$OCIBASE/FreeBSD-14.2-RELEASE-amd64-container-image-static.txz
Getting image source signatures
Copying blob 3d72902df126 skipped: already exists
Copying config 7876fe59db done |
Writing manifest to image destination
Loaded image: localhost/freebsd14-static:14.2-RELEASE-amd64
List the Images
- the image IDs are reproducible
- and available in buildah and podman
# podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/freebsd14-minimal 14.2-RELEASE-amd64 c5f3e77557a9 4 days ago 35.1 MB
localhost/freebsd14-dynamic 14.2-RELEASE-amd64 ebf7538b22f4 4 days ago 15.9 MB
localhost/freebsd14-static 14.2-RELEASE-amd64 7876fe59dbb3 4 days ago 5.45 MB
# buildah images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/freebsd14-minimal 14.2-RELEASE-amd64 c5f3e77557a9 4 days ago 35.1 MB
localhost/freebsd14-dynamic 14.2-RELEASE-amd64 ebf7538b22f4 4 days ago 15.9 MB
localhost/freebsd14-static 14.2-RELEASE-amd64 7876fe59dbb3 4 days ago 5.45 MB
List the Layers
Note how showing the layers for the upper image, called minimal
, shows how
it is built on top of the lower layers - each layer shares the same components.
# podman image tree localhost/freebsd14-minimal:14.2-RELEASE-amd64
Image ID: c5f3e77557a9
Tags: [localhost/freebsd14-minimal:14.2-RELEASE-amd64]
Size: 35.07MB
Image Layers
├── ID: cd53fb07fb66 Size: 5.449MB Top Layer of: [localhost/freebsd14-static:14.2-RELEASE-amd64]
├── ID: a01d37f7777b Size: 10.4MB Top Layer of: [localhost/freebsd14-dynamic:14.2-RELEASE-amd64]
└── ID: 36b0c80ca1f7 Size: 19.21MB Top Layer of: [localhost/freebsd14-minimal:14.2-RELEASE-amd64]
Inspect the Checksums
These sha256 digests can be compared across hosts, to ensure you are getting the expected images.
# podman inspect localhost/freebsd14-minimal:14.2-RELEASE-amd64
[
{
"Id": "c5f3e77557a916c124bfb74bdd982efe0a5749aaba9de0d282703d4634b28839",
"Digest": "sha256:b98b8f5438a07e000a156ecb216529d8dfa191448ac3dc1f6add9848b4461d05",
"RepoTags": [
"localhost/freebsd14-minimal:14.2-RELEASE-amd64"
],
"RepoDigests": [
"localhost/freebsd14-minimal@sha256:b98b8f5438a07e000a156ecb216529d8dfa191448ac3dc1f6add9848b4461d05"
],
"Parent": "ebf7538b22f43206d308fecc12bfebc72beb6d1f0e89ce9d95ed1979583010ee",
"Comment": "",
"Created": "2024-11-29T10:34:57.587490327Z",
"Config": {
"Labels": {
"io.buildah.version": "1.36.0"
}
},
"Version": "",
"Author": "",
"Architecture": "amd64",
"Os": "freebsd",
"Size": 35067392,
"VirtualSize": 35067392,
"GraphDriver": {
"Name": "zfs",
"Data": {
"Dataset": "zroot/containers/36b0c80ca1f71afdd5813c5fbd5b55fe686ec6361df1ff36286f2176be67a3fe",
"Mountpoint": "/var/db/containers/storage/zfs/graph/36b0c80ca1f71afdd5813c5fbd5b55fe686ec6361df1ff36286f2176be67a3fe"
}
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:cd53fb07fb66f2c92709cbc407af686fa53276397762dfb27c040a2f64508000",
"sha256:f709c8f6cb8695415033da5819b83262c51cd9512bddb64e0681c48a61b8723f",
"sha256:357c210f8575c70360e88cfcf3d0c241f7e3fa21669ec55bb935dce361a90585"
]
},
"Labels": {
"io.buildah.version": "1.36.0"
},
"Annotations": {
"org.opencontainers.image.base.digest": "sha256:7c1c45d99ffe8344021227ad651f675ac22eeb39b96c8c6abfe2d0612f4c2ccc",
"org.opencontainers.image.base.name": "localhost/freebsd14-dynamic:latest"
},
"ManifestType": "application/vnd.oci.image.manifest.v1+json",
"User": "",
"History": [
{
"created": "2024-11-29T09:55:25.405122405Z",
"created_by": "/bin/sh"
},
{
"created": "2024-11-29T10:34:08.010623771Z",
"created_by": "/bin/sh",
"comment": "FROM localhost/freebsd14-static:latest"
},
{
"created": "2024-11-29T10:34:58.10725353Z",
"created_by": "/bin/sh",
"comment": "FROM localhost/freebsd14-dynamic:latest"
}
],
"NamesHistory": [
"localhost/freebsd14-minimal:14.2-RELEASE-amd64"
]
}
]
Run A Container
# podman run -it localhost/freebsd14-minimal:14.2-RELEASE-amd64 /bin/sh
# uname -a
FreeBSD 3fad4ca90729 15.0-CURRENT FreeBSD 15.0-CURRENT main-n273934-3455b824a1e8 GENERIC-NODEBUG amd64
# freebsd-version -kru
freebsd-version: unable to locate kernel
# freebsd-version -ru
15.0-CURRENT
14.2-RELEASE
# exit