{"id":23182232,"url":"https://github.com/cbodonnell/airgap-oci","last_synced_at":"2025-06-12T15:03:14.206Z","repository":{"id":211139464,"uuid":"727955616","full_name":"cbodonnell/airgap-oci","owner":"cbodonnell","description":null,"archived":false,"fork":false,"pushed_at":"2024-01-03T15:22:24.000Z","size":104,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-10T11:24:32.299Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cbodonnell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-12-05T23:23:41.000Z","updated_at":"2023-12-05T23:23:54.000Z","dependencies_parsed_at":"2023-12-12T18:41:28.871Z","dependency_job_id":"7aa3b0c4-db01-46b8-ab7b-e9a97e9350e9","html_url":"https://github.com/cbodonnell/airgap-oci","commit_stats":null,"previous_names":["cbodonnell/airgap-oci"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbodonnell%2Fairgap-oci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbodonnell%2Fairgap-oci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbodonnell%2Fairgap-oci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbodonnell%2Fairgap-oci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cbodonnell","download_url":"https://codeload.github.com/cbodonnell/airgap-oci/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247280255,"owners_count":20912967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-18T08:19:39.461Z","updated_at":"2025-04-05T03:17:20.310Z","avatar_url":"https://github.com/cbodonnell.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Airgap Bundles as OCI Artifacts\n\nThis repo contains resources from initial investigations into how airgap bundles can be stored as OCI artifacts.\n\n## Dependencies\n\n[docker](https://docs.docker.com/get-docker/) - used to run a local registries in this example.\n\n[oras](https://github.com/oras-project/oras) - an OCI client for managing content like artifacts, images, packages. It can be used to create an OCI artifact from a directory of files.\n\n[crane](https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md) - a tool for interacting with remote images and registries. It can be used to copy images from one registry to another.\n\n[go](https://golang.org/doc/install) (optional) - used to generate the `scripts/artifact.sh` script.\n\n## Creating an Airgap Bundle OCI artifact\n\nToday, an airgap bundle consists of three main components:\n* `airgap.yaml` - airgap metadata\n* `app.tar.gz` - application bundle (kubernetes manifests, helm charts, etc.)\n* `images` - directory containing a registry of images\n\nIn this example, we will assume we already have the `airgap.yaml` and `app.tar.gz` files and we will use `crane` to copy the images from a remote registry to a temporary local registry to use for the bundle.\n\n### Create a local registry\n\nStart a local registry on port 5000 and store the registry data in the `bundle/images` directory.\n\n```bash\ndocker run -d -p 5000:5000 -v $(PWD)/bundle/images:/var/lib/registry --name bundle-registry registry:2\n```\n\n### Copy images to the local registry\n\nWe're copying two images in this example to demonstrate deduplication of layers.\n\n```bash\ncrane cp docker.io/library/postgres:15.5-alpine localhost:5000/postgres:15.5-alpine\ncrane cp docker.io/library/postgres:16.1-alpine localhost:5000/postgres:16.1-alpine\n```\n\nOnce the above commands complete successfully, we can stop the local registry:\n\n```bash\ndocker rm -f bundle-registry\n```\n\nAt this point the `bundle/images` directory contains the image data for the airgap bundle.\n\n### Create an OCI artifact\n\nIn this step we'll start up another registry on port 5001. In practice, this could be any registry where airgap bundles will be stored.\n\n```bash\ndocker run -d -p 5001:5000 -v $(PWD)/bundle/images:/var/lib/registry --name storage-registry registry:2\n```\n\nIt's not necessary to login to the registry in this example, but if it was required, it could be done with the following command:\n\n```bash\noras login localhost:5001 -u $USERNAME -p $PASSWORD\n```\n\nThe `scripts/artifact.sh` script uses `oras push` to create an OCI artifact that has a custom artifact type of `application/vnd.airgap.bundle` (take a look at the script and the [oras docs](https://oras.land/docs/commands/oras_push) for more details). This artifact will have layers for the metadata, application bundle, and images. Each image blob will be stored as separate layer in the artifact to leverage the deduplication of the OCI format. This script can be regenerated by running `go run ./cmd/script/main.go`, if necessary.\n\n```bash\n./scripts/artifact.sh\n```\n\nTo fetch the manifest for the uploaded artifact, run:\n\n```bash\noras manifest fetch localhost:5001/my-company/airgap-bundle:v1\n```\n\n## Downloading an Airgap Bundle OCI artifact\n\nThis section provides an example of how an end-user could download an airgap bundle OCI artifact.\n\n### Pulling an artifact\n\nFor this example, let's go to a clean working directory:\n  \n```bash\ncd $(mktemp -d)\n```\n\nAs before, it's not necessary to login to the registry in this example, but if it was required, it could be done with the following command:\n\n```bash\noras login localhost:5001 -u $USERNAME -p $PASSWORD\n```\n\nWe can set the location of the oras cache if desired. This is useful if we need to download the same or similar artifacts multiple times.\n\n```bash\nexport ORAS_CACHE=~/.oras/cache\n```\n\nTo download an artifact, run:\n\n```bash\noras pull localhost:5001/my-company/airgap-bundle:v1\n```\n\nThis will download the artifact to the current directory. The `bundle` directory will contain all of the files that make up the airgap bundle, including the `airgap.yaml` and `app.tar.gz` files, as well as the `images` directory containing the image registry data.\n\nFor easier transport, the `bundle` directory can be tarred and gzipped:\n\n```bash\ntar -czvf airgap-bundle.tar.gz bundle\n```\n\n### Checksums\n\nAn OCI image has a manifest that contains a list of layers. Each layer has a digest that is a checksum of the layer's contents. The manifest also has a digest that is a checksum of the manifest's contents. The manifest digest is used to reference the image. OCI clients use these digests to verify the integrity of the image.\n\n*The following manual steps are not necessary in practice, but are included here for demonstration purposes.*\n\nTo compute digest of the manifest, run:\n\n```bash\noras manifest fetch localhost:5001/my-company/airgap-bundle:v1 | shasum -a 256\n```\n\nTo pull an artifact using the manifest digest, run:\n\n```bash\noras pull localhost:5001/my-company/airgap-bundle@sha256:${SHA256SUM}\n```\n\nIf desired, checksums of the files returned can be validated against the checksums of the layers in the manifest:\n\n```bash\nshasum -a 256 bundle/airgap.yaml\nshasum -a 256 bundle/app.tar.gz\nshasum -a 256 bundle/images/docker/registry/v2/blobs/sha256/*/*/data\n```\n\n## Additional Resources\n\n* https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage\n* https://oras.land/docs/\n* https://www.youtube.com/watch?v=XbUAPlZi0x0\n* https://dlorenc.medium.com/oci-artifacts-explained-8f4a77945c13\n* https://explore.ggcr.dev/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbodonnell%2Fairgap-oci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcbodonnell%2Fairgap-oci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbodonnell%2Fairgap-oci/lists"}