Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update container images on air-gapped environments #1068

Open
almet opened this issue Jan 30, 2025 · 1 comment
Open

Update container images on air-gapped environments #1068

almet opened this issue Jan 30, 2025 · 1 comment
Labels
icu Issues related with independent container updates
Milestone

Comments

@almet
Copy link
Member

almet commented Jan 30, 2025

On air-gapped environments, we will need to have another way to install new container images, rather than using the Github Container Registry.

For Linux, we should be able to use specific packages (see #1067), but we should still be able to install specific container images alongside their signatures by another mean, especially for Windows and macOS environments.

Having a CLI option for doing this would be useful.

@almet almet added the icu Issues related with independent container updates label Jan 30, 2025
@almet almet added this to the 0.9.0 milestone Jan 30, 2025
@almet
Copy link
Member Author

almet commented Feb 3, 2025

In order to handle air-gapped environments, we need a format which provides the following:

  1. Layer "blobs" (e.g. where the podman/docker layers are stored)
  2. The cosign signatures
  3. A way to ensure that the cosign signatures have been applied to this image format, once loaded.

podman save

When saving a container image from podman with podman save, the image digest is actually lost:

$ podman image list ghcr.io/almet/dangerzone/dangerzone --format "{{.Digest}}"
sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7

$ podman save ghcr.io/almet/dangerzone/dangerzone -o archive.tar

$ podman rmi ghcr.io/almet/dangerzone/dangerzone:latest
Untagged: ghcr.io/almet/dangerzone/dangerzone:latest
Deleted: 97808d20e505674268604beb67515c6444d0d46257a74d9d80c7450788e477a9

$ podman load -i archive.tar

$ podman image list ghcr.io/almet/dangerzone/dangerzone --format "{{.Digest}}"
sha256:118e38fabe3ccd0ec89b2e21bb7704b62fae92e106166ed02b4ee301151319e

Interestingly, after doing a podman pull from the repository, the repo digest is added, so we have:

[
  {
    "Id": "97808d20e505674268604beb67515c6444d0d46257a74d9d80c7450788e477a9",
    "ParentId": "",
    "RepoTags": null,
    "RepoDigests": [
      "ghcr.io/almet/dangerzone/dangerzone@sha256:118e38fabe3ccd0ec89b2e21bb7704b62fae92e106166ed02b4ee301151319e8",
      "ghcr.io/almet/dangerzone/dangerzone@sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7"
    ],
    "Size": 1321336831,
    "SharedSize": 0,
    "VirtualSize": 1321336831,
    "Labels": {
      "io.buildah.version": "1.33.7"
    },
    "Containers": 0,
    "Digest": "sha256:118e38fabe3ccd0ec89b2e21bb7704b62fae92e106166ed02b4ee301151319e8",
    "History": [
      "ghcr.io/almet/dangerzone/dangerzone:latest"
    ],
    "Names": [
      "ghcr.io/almet/dangerzone/dangerzone:latest"
    ],
    "Created": 1737389331,
    "CreatedAt": "2025-01-20T16:08:51Z"
  }
]

cosign save

cosign save provides an OCI-image archive that cannot be loaded as is, but can if tweaked a bit (removing the signatures from the top-level index.json)

# Save the container + signatures in a folder, then tar it.
cosign save ghcr.io/almet/dangerzone/dangerzone@sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7 --dir container
cd container && tar -cvf ../container.tar

If loaded directly without tweaks, it results in the following error:

Error: payload does not match any of the supported image formats:
 * oci: open here.tar/index.json: not a directory
 * oci-archive: loading index: more than one image in oci, choose an image
 * docker-archive: loading tar component "manifest.json": file does not exist
 * dir: open here.tar/manifest.json: not a directory

This is because the index.json contains a link to the signatures as well as the image itself, like this:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "size": 1566,
      "digest": "sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7",
      "annotations": {
        "kind": "dev.cosignproject.cosign/image"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 4369,
      "digest": "sha256:e5cb967032b8ae1c1d28f98d3b509513a287a1a982e58dde518ebd3fe1d8ab2b",
      "annotations": {
        "kind": "dev.cosignproject.cosign/sigs"
      }
    }
  ]
}

Removing the signatures before taring the folder comes up with a loadable container image.

cat container/index.json | jq '.manifests |= [.[0]]' > container/index.json
cd container && tar -cvf ../container.tar
podman load -i container.tar

Unfortunately, it doesn't retain the image name, but the image Digest is the right one!

podman image list --format "{{.Digest}}"
sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7

To retag it:

IMAGE_ID=$(podman image list -f digest="sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7" --format "{{.Id}}")
podman tag $IMAGE_ID ghcr.io/almet/dangerzone/dangerzone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
icu Issues related with independent container updates
Projects
Status: Todo
Development

No branches or pull requests

1 participant