# image-tools Judges, signs and verifies the container images of [netOrk](https://git.netork.io/NetOrk/netork). Every image repository of NetOrk uses these tools at a pinned commit, so the same rules apply to every link of the chain: ``` netork/base-os ──► netork/base-python ──► netork/engine, netork/satellite └─► netork/ui └─► postgres, valkey, signal-api, apt-cacher-ng, unbound ``` Each link checks the signature of the image it builds on, is judged against CIS, and is signed with its CIS result and SBOM attached (NetOrk/netork#1390). ## What it does | | | |---|---| | **CIS gate** (`image_tools/cis.py`) | CIS Docker Benchmark 1.6.0 §4 and the CIS Debian Linux 12 controls that make sense in a container. Trivy measures what it automates; `image_tools/cis_os.sh` measures from inside the image. The image's repository decides the rest in its `cis.toml` and names the packages it may hold in `packages.allow`. | | **SBOM** (`image_tools/sbom.py`) | The image's CycloneDX SBOM, as Trivy reads it. | | **Signature** (`bin/sign.sh`) | A cosign signature on the reference and on every platform image behind it, with the CIS result and the SBOM as in-toto attestations. Nothing goes to a public transparency log. | | **Verification** (`bin/verify-base.sh`, `bin/verify-signature.sh`) | Every `FROM` image of a Dockerfile is pinned by digest and signed with the key in `cosign.pub`. | | **Verdict** (`image_tools/verify.py`) | Whether a rebuild improves on the pinned image, by netOrk's matcher (kb-server), for a base's nightly. | | **Bump** (`image_tools/bump.py`) | One pull request in every repository of a `dependents.toml` that moves only the pin, and that repository's workflow started. | ## Using it in a workflow ```yaml - name: Verify the base images uses: https://git.netork.io/NetOrk/image-tools/actions/verify-base@ with: dockerfiles: docker/engine/Dockerfile # build the image, then: - name: Judge it uses: https://git.netork.io/NetOrk/image-tools/actions/judge@ with: image: registry.netork.io/netork/engine:main-abc1234 arch: amd64 # a base's nightly, to decide whether its pin moves: - name: Judge it against the pin id: verdict uses: https://git.netork.io/NetOrk/image-tools/actions/verdict@ env: NETORK_LICENSE_KEY: ${{ secrets.NETORK_LICENSE_KEY }} with: current: registry.netork.io/netork/base-os:pinned candidate: registry.netork.io/netork/base-os:13-20261022.3-amd64 # push it, then: - name: Sign it uses: https://git.netork.io/NetOrk/image-tools/actions/sign@ env: COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} with: ref: registry.netork.io/netork/engine@sha256:… ``` The judge action reads `cis.toml` and `packages.allow` from the working directory by default. The runner needs Docker, Python 3.11 or newer, and `curl`; cosign is installed by the actions, pinned and checked against its release checksum (`bin/install-cosign.sh`). ## Verifying an image yourself ```bash cosign verify --key cosign.pub --insecure-ignore-tlog registry.netork.io/netork/base-os@sha256:… cosign verify-attestation --key cosign.pub --insecure-ignore-tlog \ --type https://netork.io/attestations/cis/v1 registry.netork.io/netork/base-os@sha256:… ``` `cosign.pub` is the public half of the `COSIGN_PRIVATE_KEY_B64` organization secret. The private key comes base64 in one line: the runner masks a secret only where it appears whole. ## Development ```bash pip install pytest ruff pyyaml shellcheck-py pytest -q ruff format --check . && ruff check . shellcheck bin/*.sh image_tools/cis_os.sh ``` ## License MIT, see [LICENSE](LICENSE).