Test GHA for image syncing, scanning and building
- sync to vendor images
- build container images
- run security scans again each destination image in configuration
- separate implementation from the data
- implementation can be swapped out underneath, if need be and maintain the function
The repo is mostly concerned with base images or images used in build processes.
- declarative image sync management, given source and destination
- declarative apko build management
- with Melange (Alpine APK building) integration
- generated SBOMs
- container image signing
- automatic security scanning for each image synced and built
- multiple build modes
- apko
- apko + melange
- docker (fallback)
- automatic trigger of builds, sync and scan every week
the structure of the repo is as follows:
config.yaml
: define configuration about runtimeimages/NAME/{images.yaml|Dockerfile,*}
: images to build configurations.github/workflows/{sync,scan,build}.yml
: lifecycle actions
- specify images for syncing and building in the respective sync and build fields
- set the image push GitHub secrets of
QUAY_USERNAME
andQUAY_ROBOT_TOKEN
Images are synced by specifying source and destinations like this
sync:
- source: docker.io/alpine:latest
destination: ghcr.io/somecoolorg/images/alpine:latest
Images will only be synced if the digest of the source and destination don't match or if the sync[].always
key is set to true
Images for building are specified with the source being an apko formatted YAML file and an image destination
build:
- source: ./images/acoolthing/image.yaml
destination: ghcr.io/somecoolorg/images/acoolthing:latest
with ./images/acoolthing/image.yaml
being something like this
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.17/main
- https://dl-cdn.alpinelinux.org/alpine/v3.17/community
packages:
- alpine-base
- busybox
- ca-certificates-bundle
entrypoint:
command: /bin/sh -c
archs:
- x86_64
- aarch64
Specify a source, destination and as many melangeConfigs
build:
- source: ./images/coolthing/image.yaml
destination: ghcr.io/somecoolorg/images/coolthing:latest
melangeConfigs:
- ./images/coolthing/pkg-hello.yaml
with each melange config being a path to the package to build.
An entry in the apko YAML must be set contents.repositories[last index]
to @local /github/workspace/packages
, then packages can be installed with NAME@local
; like this apko configuration
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.17/main
- https://dl-cdn.alpinelinux.org/alpine/v3.17/community
- '@local /github/workspace/packages'
packages:
- alpine-base
- busybox
- ca-certificates-bundle
- hello@local
entrypoint:
command: /bin/sh -c
archs:
- x86_64
Only use this if you have to. It is better to build with apko.
Images can be built with Docker like this
build:
- source: ./images/oldschool/Dockerfile
destination: ghcr.io/somecoolorg/images/oldschool:latest
dockerOptions:
context: ./images/oldschool
IMAGE_REF refers to an image reference in the format of either
- ghcr.io/somecoolorg/images/hello:latest (just a tag)
- ghcr.io/somecoolorg/images/hello@sha256:a61743b19423a01827ba68a1ec81a09d04f84bc69848303905ecbc73824fb88b (just a digest)
- ghcr.io/somecoolorg/images/hello:latest@sha256:a61743b19423a01827ba68a1ec81a09d04f84bc69848303905ecbc73824fb88b (a tag and a digest)
the digest can be obtained through
crane digest IMAGE_REF
to use it, it can be included when an image ref is formatted like the examples above in What is an image ref?.
determine the digest of the image, for example given the image is ghcr.io/somecoolorg/images/hello:latest
crane digest ghcr.io/somecoolorg/images/hello:latest
the digest might be sha256:a61743b19423a01827ba68a1ec81a09d04f84bc69848303905ecbc73824fb88b
.
add a new sync image to the sync key like
- source: ghcr.io/somecoolorg/images/hello@sha256:a61743b19423a01827ba68a1ec81a09d04f84bc69848303905ecbc73824fb88b
destination: ghcr.io/somecoolorg/images/hello:some-cool-tag
Images are signed using cosign
with keyless, this means that
- signatures depend on the build or key infrastructure
- there's no worry of key rotation
Images are able to be verified through
cosign verify IMAGE_REF \
--certificate-identity https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/sync.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
please note: the certificate identity field will vary between
- https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/sync.yml@refs/heads/main
- https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/build.yml@refs/heads/main
produces a nice and readible tree of signatures, attestations and SBOMs related to the IMAGE_REF
cosign tree IMAGE_REF
verify the attestation
cosign verify-attestation IMAGE_REF --certificate-identity https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/sync.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
since SBOMs are the predicate of a signed attestation instead of just uploaded, it requires an extra layer to retrieve their content
cosign verify-attestation IMAGE_REF --certificate-identity https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/sync.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com | jq -r .payload | base64 -d | jq -r .predicate.Data
using sigs.k8s.io/bom, the SBOM attestation can be visualised
π cosign verify-attestation IMAGE_REF --certificate-identity https://github.com/BobyMCbobs/gha-sync-scan-and-build/.github/workflows/sync.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com | jq -r .payload | base64 -d | jq -r .predicate.Data | bom document outline -
Verification for ghcr.io/bobymcbobs/gha-sync-scan-and-build/hello:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified offline
- The signatures were verified against the specified public key
&{Version:SPDX-2.3 DataLicense:CC0-1.0 ID:SPDXRef-DOCUMENT Name:sbom-sha256:e1097a462313187e2a61ba229179efafa8e62bd6166dc94698c9dcffdff05c9c Namespace:https://spdx.org/spdxdocs/apko/ Creator:{Person: Organization:Chainguard, Inc Tool:[apko (v0.7.3-45-g39b16b5)]} Created:1970-01-01 00:00:00 +0000 UTC LicenseListVersion:3.16 Packages:map[SPDXRef-Package-sha256-63bd196f274790f29c3956c9df4a8525aacec14c1c2ecb684cf7462c53f0471f:0xc000314000] Files:map[] ExternalDocRefs:[]}
PACKAGE: <nil>
_
___ _ __ __| |_ __
/ __| '_ \ / _` \ \/ /
\__ \ |_) | (_| |> <
|___/ .__/ \__,_/_/\_\
|_|
π SPDX Document sbom-sha256:e1097a462313187e2a61ba229179efafa8e62bd6166dc94698c9dcffdff05c9c
β
β π¦ DESCRIBES 1 Packages
β
β sha256:63bd196f274790f29c3956c9df4a8525aacec14c1c2ecb684cf7462c53f0471f
β β π 2 Relationships
β β CONTAINS PACKAGE sha256:e1097a462313187e2a61ba229179efafa8e62bd6166dc94698c9dcffdff05c9c@unknown
β β β π 4 Relationships
β β β CONTAINS PACKAGE alpine-baselayout-data@3.4.0-r0
β β β CONTAINS PACKAGE ca-certificates-bundle@20220614-r4
β β β CONTAINS PACKAGE musl@1.2.3-r4
β β β CONTAINS PACKAGE hello@2.12-r0
β β β β π 3 Relationships
β β β β CONTAINS FILE /usr/bin/hello (/usr/bin/hello)
β β β β CONTAINS FILE /usr/share/info/hello.info (/usr/share/info/hello.info)
β β β β CONTAINS FILE /usr/share/man/man1/hello.1 (/usr/share/man/man1/hello.1)
β β β
β β
β β GENERATED_FROM PACKAGE github.com/BobyMCbobs/gha-sync-scan-and-build@6d8476f50ccdd7864eb0d46b2036f44516a55336
β
β π DESCRIBES 0 Files
Name | Description | Links | Related/Alternatives |
---|---|---|---|
crane | an officially supported cli container registry tool from Google | https://github.com/google/go-containerregistry/tree/main/cmd/crane | skopeo |
yq | a cli YAML parser | https://github.com/mikefarah/yq | jq |
cosign | a cli container image artifact signing utility by Sigstore (Linux Foundation) | https://github.com/sigstore/cosign | ... |
melange | a cli Alpine APK package declarative builder supported by Chainguard | https://github.com/chainguard-dev/melange | ... |
apko | a cli tool for declaratively building Alpine based container images | https://github.com/chainguard-dev/apko | ko (https://ko.build - Linux Foundation) |
docker | a container ecosystem, primarily for development | https://docker.io | podman |
trivy | a container image scanner | https://github.com/aquasecurity/trivy | clair |
syft | a cli tool to generate sboms based on container images and filesystems | https://github.com/anchore/syft |
semi-related topics
- require signed container images for use in production
- use distroless container images as base images
- don't ship package managers into production
- https://github.com/chainguard-images
- run containers as non-root users
- limit workload privilege
- https://github.com/chainguard-dev/apko/blob/main/docs/apko_file.md#accounts-top-level-element
- set
pod.spec.containers.securityContext.runAsUser
; https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod docker run --user 10000 ...
https://docs.docker.com/engine/reference/commandline/run- https://docs.aws.amazon.com/config/latest/developerguide/ecs-task-definition-nonroot-user.html
- use a static tag with digest instead of latest
- ensure that you are using the version of the image that you expect
- don't use e.g:
alpine:latest
, instead use an immutable image reference likealpine:3.17.3@sha256:b6ca290b6b4cdcca5b3db3ffa338ee0285c11744b4a6abaa9627746ee3291d8d
to ensure an expected version is always used - version full digests can be manually resolved for consumption using
crane digest alpine:3.17.3
(for example) - when using systems like Knative, these digests are automatically resolved
- deploy containers with a read-only root filesystem
- https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-readonly-access.html
docker run --read-only ...
; https://docs.docker.com/engine/reference/commandline/run- set
pod.spec.containers.securityContext.readOnlyRootFilesystem
totrue
; https://kubernetes.io/docs/tasks/configure-pod-container/security-context
- build Go apps with ko