Sealing OS packages in a Dockerfile
Insert `seal fix --os` into a Dockerfile so the resulting image ships with sealed OS packages.
The canonical place to run seal fix --os is inside a Dockerfile RUN step, after the base image's package manager has installed the OS packages your application needs and before the build does anything that drops privileges or finalizes the image.
The CLI auto-detects the distribution and dispatches to the matching OS-package family. One pattern covers Alpine, Debian / Ubuntu, and the Red Hat family alike; the only thing that changes is which base image you started from.
Pattern
# Whatever base image your application uses.
FROM alpine:3.20
# Install the OS packages your application needs, normally.
RUN apk add --no-cache curl ca-certificates openssl
# Drop the Seal CLI into the image and seal the OS packages.
ADD --chmod=755 https://github.com/seal-community/cli/releases/download/latest/seal-linux-amd64-latest /usr/local/bin/seal
ARG SEAL_TOKEN
ARG SEAL_PROJECT=my-project-id
RUN seal fix --os --mode all --remove-cli
# Continue the build with the sealed OS packages in place.
COPY . /app
WORKDIR /app
CMD ["/app/server"]What the relevant lines do:
ADD --chmod=755 ... /usr/local/bin/sealdownloads the latest CLI binary into the image and marks it executable.ARG SEAL_TOKENandARG SEAL_PROJECTaccept the credentials from thedocker buildinvocation. The variables are available only during the build; they do not persist into the final image.RUN seal fix --os --mode alldetects the distribution, identifies which installed OS packages have sealed versions available, and replaces them in place.--remove-clideletes the CLI binary from the image after the run so it does not ship in the final image. Drop the flag if you want to keep the CLI available at runtime (for example, to run scans against the running container).
Passing credentials from the build
Pass SEAL_TOKEN from your CI's secret store as a build argument:
In CI, the host-side SEAL_TOKEN is your CI's secret-store-backed environment variable. --build-arg propagates it into the Dockerfile's RUN seal fix --os step without writing it to a layer.
For BuildKit-enabled builds, the more secure form uses build secrets instead of --build-arg. The build argument form above is the simpler default; the BuildKit form is documented in the CI integration page.
Per-distribution notes
The CLI handles the distribution-specific package manager work for you. There are still a few base-image conventions worth respecting in the surrounding Dockerfile:
apk add --no-cacheis the idiomatic install line; the CLI works against the resulting package database.Alpine images are small; the CLI's network round-trips to
apk.sealsecurity.ioare the dominant cost of the seal step.
The CLI updates the apt source list itself when it needs to pull from
deb.sealsecurity.io. You do not need to add a Seal entry to/etc/apt/sources.listby hand.Keep the install + clean-up on one layer (
apt-get update && apt-get install && rm -rf /var/lib/apt/lists/*) the way you normally would; the seal step runs after that layer is committed.
Works with
dnf(modern RHEL family) andyum(older RHEL family) alike. The CLI detects which one is present.On Amazon Linux 2 use
RUN yum install ...instead ofdnf. The CLI behavior is the same.
Multi-stage builds
In a multi-stage build, run seal fix --os in the final stage, the one whose layers ship in the published image. Sealing the OS packages of a builder stage that is later discarded is wasted work and does not protect the runtime image.
Layer ordering and caching
docker build caches each RUN step's result by hashing the step's inputs. The seal fix --os step's output depends on the sealed-package set Seal currently publishes for your distribution, which Docker has no way to know about: from Docker's perspective, the step's inputs (the previous layer, the command string, the ARG values) might look identical between two builds even when Seal has published a new sealed iteration.
Two ways to manage that:
Pin to specific sealed iterations. Use
--mode remoteor--mode localwith explicit Sealing Rules. Bumping a rule changes the rule contents the CLI reads at build time, which propagates into the layer cache invalidation chain.Bust the cache periodically. Add a no-op
ARGlikeARG SEAL_REBUILD_TOKEN=1and pass--build-arg SEAL_REBUILD_TOKEN=$(date +%Y%m%d)from CI so every build at least once a day re-runs the seal step against the latest sealed iterations.
The right approach depends on whether your team's mental model for "what's in this image" is pinned (rules) or rolling (latest safest).
Verify
After the build finishes, run the produced image and ask the OS package manager for the version of any sealed package:
The reported version differs from the upstream origin version in a way that identifies the sealed iteration. The exact form is Alpine-specific and documented in Seal's naming and versioning conventions.
The version field carries the sealed-iteration suffix (+spN).
The package release field carries the sealed-iteration suffix (+spN).
The exact version-string format per OS-package family is documented in Seal's naming and versioning conventions reference.
Common pitfalls
The CLI runs before all
apk add/apt-get install/dnf installsteps. The packages installed after the seal step ship in their upstream vulnerable form. Placeseal fix --osafter every step that installs or upgrades OS packages so the full installed set is sealed.The image drops to a non-root user before the CLI runs. The seal step needs to write to the package-manager database. Run
seal fix --oswhile the build is stillUSER root, then drop privileges with a laterUSERdirective.The base image is one Seal does not support. The CLI exits non-zero with a message naming the detected distribution. The supported distributions are the
apk,deb, andrpmfamilies enumerated in theseal fix --osreference.
Related
seal fix --osreference: the full flag list.Running the CLI inside a Dockerfile: the parallel pattern for
seal fix(application dependencies). Both can run in the sameDockerfile.Integrating with your container build pipeline: wiring the
docker buildinto your CI system.
Last updated